226 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			226 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "MessageServer.h"
 | |
| 
 | |
| #include <QDebug>
 | |
| 
 | |
| 
 | |
| MessageServer::MessageServer(QObject *parent) :
 | |
|     QTcpServer(parent)
 | |
| {
 | |
| }
 | |
| 
 | |
| MessageServer::~MessageServer(){
 | |
|     stop();
 | |
| }
 | |
| 
 | |
| bool MessageServer::start()
 | |
| {
 | |
|     if(isListening()){
 | |
|         qDebug() << "MessageServer already listening:" << m_host << m_port;
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     auto address = QHostAddress();
 | |
|     if(m_host.isEmpty() || !address.setAddress(m_host)){
 | |
|         qDebug() << "MessageServer address invalid:" << m_host << m_port;
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if(m_port <= 0){
 | |
|         qDebug() << "MessageServer port invalid:" << m_host << m_port;
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     bool listening = listen(address, m_port);
 | |
|     qDebug() << "MessageServer listening:" << listening << m_host << m_port;
 | |
| 
 | |
|     return listening;
 | |
| }
 | |
| 
 | |
| void MessageServer::stop()
 | |
| {
 | |
|     // disconnect all clients
 | |
|     foreach(auto client, m_clients){
 | |
|         client->close();
 | |
|     }
 | |
| 
 | |
|     // then close the server
 | |
|     close();
 | |
| }
 | |
| 
 | |
| void MessageServer::setServer(QString host, quint16 port){
 | |
|     bool listening = isListening();
 | |
|     if(listening && (m_host != host || m_port != port)){
 | |
|         stop();
 | |
|     }
 | |
| 
 | |
|     m_host = host;
 | |
|     m_port = port;
 | |
| 
 | |
|     if(listening){
 | |
|         start();
 | |
|     }
 | |
| }
 | |
| 
 | |
| void MessageServer::setPause(bool paused)
 | |
| {
 | |
|     m_paused = paused;
 | |
| 
 | |
|     if(paused){
 | |
|         pauseAccepting();
 | |
|     } else {
 | |
|         resumeAccepting();
 | |
|     }
 | |
| }
 | |
| 
 | |
| void MessageServer::setMaxConnections(int n){
 | |
|     // set the maximum number of connections allowed
 | |
|     m_maxConnections = n;
 | |
| 
 | |
|     // then, prune old ones greater than the max (fifo)
 | |
|     pruneConnections();
 | |
| }
 | |
| 
 | |
| int MessageServer::activeConnections(){
 | |
|    int i = 0;
 | |
|    foreach(auto client, m_clients){
 | |
|        if(client->isConnected()) i++;
 | |
|    }
 | |
|    return i;
 | |
| }
 | |
| 
 | |
| void MessageServer::pruneConnections(){
 | |
|     // keep only the n most recent connections (fifo)
 | |
|     if(m_maxConnections && m_maxConnections < activeConnections()){
 | |
|         for(int i = m_maxConnections; i < activeConnections(); i++){
 | |
|             auto client = m_clients.first();
 | |
|             client->close();
 | |
|             m_clients.removeFirst();
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| void MessageServer::send(const Message &message){
 | |
|     foreach(auto client, m_clients){
 | |
|         if(!client->awaitingResponse(message.id())){
 | |
|             continue;
 | |
|         }
 | |
|         client->send(message);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void MessageServer::incomingConnection(qintptr handle)
 | |
| {
 | |
|     qDebug() << "MessageServer incomingConnection" << handle;
 | |
| 
 | |
|     auto client = new Client(this, this);
 | |
|     client->setSocket(handle);
 | |
| 
 | |
| #if JS8_MESSAGESERVER_IS_SINGLE_CLIENT
 | |
|     while(!m_clients.isEmpty()){
 | |
|         auto client = m_clients.first();
 | |
|         client->close();
 | |
|         m_clients.removeFirst();
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     if(m_maxConnections && m_maxConnections <= activeConnections()){
 | |
|         qDebug() << "MessageServer connections full, dropping incoming connection";
 | |
|         client->send(Message("API.ERROR", "Connections Full"));
 | |
|         client->close();
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     m_clients.append(client);
 | |
| }
 | |
| 
 | |
| Client::Client(MessageServer * server, QObject *parent):
 | |
|     QObject(parent),
 | |
|     m_server {server}
 | |
| {
 | |
|     setConnected(true);
 | |
| }
 | |
| 
 | |
| void Client::setSocket(qintptr handle){
 | |
|     m_socket = new QTcpSocket(this);
 | |
| 
 | |
|     connect(m_socket, &QTcpSocket::disconnected, this, &Client::onDisconnected);
 | |
|     connect(m_socket, &QTcpSocket::readyRead, this, &Client::readyRead);
 | |
| 
 | |
|     m_socket->setSocketDescriptor(handle);
 | |
| }
 | |
| 
 | |
| void Client::setConnected(bool connected){
 | |
|     m_connected = connected;
 | |
| }
 | |
| 
 | |
| void Client::close(){
 | |
|     if(!m_socket){
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     m_socket->close();
 | |
|     m_socket = nullptr;
 | |
| }
 | |
| 
 | |
| void Client::send(const Message &message){
 | |
|     if(!isConnected()){
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if(!m_socket){
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if(!m_socket->isOpen()){
 | |
|         qDebug() << "client socket isn't open";
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     qDebug() << "client writing" << message.toJson();
 | |
|     m_socket->write(message.toJson());
 | |
|     m_socket->write("\n");
 | |
|     m_socket->flush();
 | |
| 
 | |
|     // remove if needed
 | |
|     if(m_requests.contains(message.id())){
 | |
|         m_requests.remove(message.id());
 | |
|     }
 | |
| }
 | |
| 
 | |
| void Client::onDisconnected(){
 | |
|     qDebug() << "MessageServer client disconnected";
 | |
|     setConnected(false);
 | |
| }
 | |
| 
 | |
| void Client::readyRead(){
 | |
|     qDebug() << "MessageServer client readyRead";
 | |
| 
 | |
|     while(m_socket->canReadLine()){
 | |
|         auto msg = m_socket->readLine().trimmed();
 | |
|         qDebug() << "-> Client" << m_socket->socketDescriptor() << msg;
 | |
| 
 | |
|         if(msg.isEmpty()){
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         QJsonParseError e;
 | |
|         QJsonDocument d = QJsonDocument::fromJson(msg, &e);
 | |
|         if(e.error != QJsonParseError::NoError){
 | |
|             send({"API.ERROR", "Invalid JSON (unparsable)"});
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if(!d.isObject()){
 | |
|             send({"API.ERROR", "Invalid JSON (not an object)"});
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         Message m;
 | |
|         m.read(d.object());
 | |
|         auto id = m.ensureId();
 | |
|         m_requests[id] = m;
 | |
| 
 | |
|         emit m_server->message(m);
 | |
|     }
 | |
| }
 | 
