From 9fe2914da368c1f6ebb8ba2c48cd623150568fc4 Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Tue, 29 Dec 2015 12:43:29 -0500 Subject: [PATCH] Convert sysadm-server to run either in TCP or WebSocket mode. New CLI flags: "-ws": Use the websocket protocols instead of tcp "-p ": Use the designated port number for the server. --- src/server/WebServer.cpp | 106 +++++++++++++++++++++++---------------- src/server/WebServer.h | 25 +++++---- src/server/WebSocket.cpp | 47 +++++++++++++++-- src/server/WebSocket.h | 4 ++ src/server/main.cpp | 31 +++++++++--- 5 files changed, 150 insertions(+), 63 deletions(-) diff --git a/src/server/WebServer.cpp b/src/server/WebServer.cpp index 1b2e6fd..a313318 100644 --- a/src/server/WebServer.cpp +++ b/src/server/WebServer.cpp @@ -13,30 +13,22 @@ #define DEBUG 0 -#define PORTNUMBER 12142 - #define APPCAFEWORKING QString("/var/tmp/appcafe/dispatch-queue.working") //======================= // PUBLIC //======================= -WebServer::WebServer() : QWebSocketServer("sysadm-server", QWebSocketServer::NonSecureMode){ +WebServer::WebServer(){ //Setup all the various settings //Any SSL changes /*QSslConfiguration ssl = this->sslConfiguration(); ssl.setProtocol(QSsl::SecureProtocols); this->setSslConfiguration(ssl);*/ + WSServer = 0; + TCPServer = 0; AUTH = new AuthorizationManager(); watcher = new QFileSystemWatcher(this); - //Setup Connections - connect(this, SIGNAL(closed()), this, SLOT(ServerClosed()) ); - connect(this, SIGNAL(serverError(QWebSocketProtocol::CloseCode)), this, SLOT(ServerError(QWebSocketProtocol::CloseCode)) ); - connect(this, SIGNAL(newConnection()), this, SLOT(NewSocketConnection()) ); - connect(this, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(NewConnectError(QAbstractSocket::SocketError)) ); - connect(this, SIGNAL(originAuthenticationRequired(QWebSocketCorsAuthenticator*)), this, SLOT(OriginAuthRequired(QWebSocketCorsAuthenticator*)) ); - connect(this, SIGNAL(peerVerifyError(const QSslError&)), this, SLOT(PeerVerifyError(const QSslError&)) ); - connect(this, SIGNAL(sslErrors(const QList&)), this, SLOT(SslErrors(const QList&)) ); connect(watcher, SIGNAL(fileChanged(const QString&)), this, SLOT(WatcherUpdate(QString)) ); connect(watcher, SIGNAL(directoryChanged(const QString&)), this, SLOT(WatcherUpdate(QString)) ); } @@ -45,28 +37,60 @@ WebServer::~WebServer(){ delete AUTH; } -bool WebServer::startServer(){ - bool ok = this->listen(QHostAddress::Any, PORTNUMBER); +bool WebServer::startServer(quint16 port, bool websocket){ + bool ok = false; + if(websocket){ ok = setupWebSocket(port); } + else{ ok = setupTcp(port); } + if(ok){ QCoreApplication::processEvents(); qDebug() << "Server Started:" << QDateTime::currentDateTime().toString(Qt::ISODate); - qDebug() << " Name:" << this->serverName() << "Port:" << this->serverPort(); - qDebug() << " URL:" << this->serverUrl().toString() << "Remote Address:" << this->serverAddress().toString(); + qDebug() << " Port:" << port; + if(WSServer!=0){ qDebug() << " URL:" << WSServer->serverUrl().toString(); } + if(!QFile::exists(APPCAFEWORKING)){ QProcess::execute("touch "+APPCAFEWORKING); } qDebug() << " Dispatcher Events:" << APPCAFEWORKING; watcher->addPath(APPCAFEWORKING); WatcherUpdate(APPCAFEWORKING); //load it initially - }else{ qCritical() << "Could not start server - exiting..."; } + }else{ + qCritical() << "Could not start server - exiting..."; + } + return ok; } void WebServer::stopServer(){ - this->close(); + if(WSServer!=0){ WSServer->close(); } //note - this will throw the "closed()" signal when done + else if(TCPServer!=0){ TCPServer->close(); QCoreApplication::exit(0);} //no corresponding signal } //=================== // PRIVATE //=================== +bool WebServer::setupWebSocket(quint16 port){ + WSServer = new QWebSocketServer("sysadm-server", QWebSocketServer::NonSecureMode, this); + //Setup Connections + connect(WSServer, SIGNAL(newConnection()), this, SLOT(NewSocketConnection()) ); + connect(WSServer, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(NewConnectError(QAbstractSocket::SocketError)) ); + // -- websocket specific signals + connect(WSServer, SIGNAL(closed()), this, SLOT(ServerClosed()) ); + connect(WSServer, SIGNAL(serverError(QWebSocketProtocol::CloseCode)), this, SLOT(ServerError(QWebSocketProtocol::CloseCode)) ); + connect(WSServer, SIGNAL(originAuthenticationRequired(QWebSocketCorsAuthenticator*)), this, SLOT(OriginAuthRequired(QWebSocketCorsAuthenticator*)) ); + connect(WSServer, SIGNAL(peerVerifyError(const QSslError&)), this, SLOT(PeerVerifyError(const QSslError&)) ); + connect(WSServer, SIGNAL(sslErrors(const QList&)), this, SLOT(SslErrors(const QList&)) ); + //Now start the server + return WSServer->listen(QHostAddress::Any, port); +} + +bool WebServer::setupTcp(quint16 port){ + TCPServer = new QTcpServer(this); + //Setup Connections + connect(TCPServer, SIGNAL(newConnection()), this, SLOT(NewSocketConnection()) ); + connect(TCPServer, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(NewConnectError(QAbstractSocket::SocketError)) ); + //Now start the server + return TCPServer->listen(QHostAddress::Any, port); +} + QString WebServer::generateID(){ int id = 0; for(int i=0; ierrorString(); -} - +//GENERIC SERVER SIGNALS // New Connection Signals void WebServer::NewSocketConnection(){ - if(!this->hasPendingConnections()){ return; } + WebSocket *sock = 0; + if(WSServer!=0){ + if(WSServer->hasPendingConnections()){ sock = new WebSocket( WSServer->nextPendingConnection(), generateID(), AUTH); } + }else if(TCPServer!=0){ + if(TCPServer->hasPendingConnections()){ sock = new WebSocket( TCPServer->nextPendingConnection(), generateID(), AUTH); } + } + if(sock==0){ return; } //no new connection qDebug() << "New Socket Connection"; - //if(idletimer->isActive()){ idletimer->stop(); } - QWebSocket *csock = this->nextPendingConnection(); - if(csock == 0){ qWarning() << " - new connection invalid, skipping..."; QTimer::singleShot(10, this, SLOT(NewSocketConnection())); return; } - qDebug() << " - Accepting connection:" << csock->origin(); - WebSocket *sock = new WebSocket(csock, generateID(), AUTH); connect(sock, SIGNAL(SocketClosed(QString)), this, SLOT(SocketClosed(QString)) ); connect(this, SIGNAL(DispatchStatusUpdate(QString)), sock, SLOT(AppCafeStatusUpdate(QString)) ); sock->setLastDispatch(lastDispatch); //make sure this socket is aware of the latest notification OpenSockets << sock; } -void WebServer::NewConnectError(QAbstractSocket::SocketError err){ - //if(csock!=0){ - //qWarning() << "New Connection Error["+QString::number(err)+"]:" << csock->errorString(); - //csock->close(); - //}else{ - qWarning() << "New Connection Error["+QString::number(err)+"]:" << this->errorString(); - //} - //csock = 0; //remove the current socket +void WebServer::NewConnectError(QAbstractSocket::SocketError err){ + qWarning() << "New Connection Error["+QString::number(err)+"]:" << ( (WSServer!=0) ? WSServer->errorString() : TCPServer->errorString()); QTimer::singleShot(0,this, SLOT(NewSocketConnection()) ); //check for a new connection - } -// SSL/Authentication Signals +//WEBSOCKET SERVER SIGNALS +// Overall Server signals +void WebServer::ServerClosed(){ + QCoreApplication::exit(0); +} + +void WebServer::ServerError(QWebSocketProtocol::CloseCode code){ + qWarning() << "Server Error["+QString::number(code)+"]:" << ( (WSServer!=0) ? WSServer->errorString() : TCPServer->errorString()); +} + +// - SSL/Authentication Signals (still websocket only) void WebServer::OriginAuthRequired(QWebSocketCorsAuthenticator *auth){ qDebug() << "Origin Auth Required:" << auth->origin(); //if(auth->origin() == this->serverAddress().toString()){ @@ -151,6 +170,7 @@ void WebServer::SslErrors(const QList &list){ } } +// - More Functions for all socket interactions void WebServer::SocketClosed(QString ID){ for(int i=0; iID()==ID){ delete OpenSockets.takeAt(i); break; } diff --git a/src/server/WebServer.h b/src/server/WebServer.h index 3787c8f..cd5b2f1 100644 --- a/src/server/WebServer.h +++ b/src/server/WebServer.h @@ -7,7 +7,9 @@ #define _PCBSD_REST_WEB_SERVER_H #include +#include #include +#include #include #include #include @@ -20,40 +22,45 @@ #include "WebSocket.h" #include "AuthorizationManager.h" -class WebServer : public QWebSocketServer{ +class WebServer : public QObject{ Q_OBJECT public: WebServer(); ~WebServer(); - bool startServer(); + bool startServer(quint16 port, bool websocket); public slots: void stopServer(); private: + QWebSocketServer *WSServer; + QTcpServer *TCPServer; QList OpenSockets; AuthorizationManager *AUTH; QFileSystemWatcher *watcher; QString lastDispatch; + + //Server Setup functions + bool setupWebSocket(quint16 port); + bool setupTcp(quint16 port); + //Generic functions for either type of server QString generateID(); //generate a new ID for a socket QString readFile(QString path); private slots: - // Overall Server signals - void ServerClosed(); //closed() signal - void ServerError(QWebSocketProtocol::CloseCode); //serverError() signal - - // New Connection Signals + // Generic Server Slots void NewSocketConnection(); //newConnection() signal void NewConnectError(QAbstractSocket::SocketError); //acceptError() signal - // SSL/Authentication Signals + // (WebSocket-only) Server signals/slots + void ServerClosed(); //closed() signal + void ServerError(QWebSocketProtocol::CloseCode); //serverError() signal + // - SSL/Authentication Signals void OriginAuthRequired(QWebSocketCorsAuthenticator*); //originAuthenticationRequired() signal void PeerVerifyError(const QSslError&); //peerVerifyError() signal void SslErrors(const QList&); //sslErrors() signal - void SocketClosed(QString ID); //File watcher signals diff --git a/src/server/WebSocket.cpp b/src/server/WebSocket.cpp index 8f526f2..125937b 100644 --- a/src/server/WebSocket.cpp +++ b/src/server/WebSocket.cpp @@ -12,6 +12,7 @@ WebSocket::WebSocket(QWebSocket *sock, QString ID, AuthorizationManager *auth){ SockID = ID; SockAuthToken.clear(); //nothing set initially SOCKET = sock; + TSOCKET = 0; SendAppCafeEvents = false; AUTHSYSTEM = auth; idletimer = new QTimer(this); @@ -24,11 +25,31 @@ WebSocket::WebSocket(QWebSocket *sock, QString ID, AuthorizationManager *auth){ idletimer->start(); } +WebSocket::WebSocket(QTcpSocket *sock, QString ID, AuthorizationManager *auth){ + SockID = ID; + SockAuthToken.clear(); //nothing set initially + TSOCKET = sock; + SOCKET = 0; + SendAppCafeEvents = false; + AUTHSYSTEM = auth; + idletimer = new QTimer(this); + idletimer->setInterval(IDLETIMEOUTMINS*60000); //connection timout for idle sockets + idletimer->setSingleShot(true); + connect(idletimer, SIGNAL(timeout()), this, SLOT(checkIdle()) ); + connect(TSOCKET, SIGNAL(readyRead()), this, SLOT(EvaluateTCPMessage()) ); + connect(TSOCKET, SIGNAL(aboutToClose()), this, SLOT(SocketClosing()) ); + idletimer->start(); +} + WebSocket::~WebSocket(){ if(SOCKET!=0){ SOCKET->close(); + delete SOCKET; + } + if(TSOCKET!=0){ + TSOCKET->close(); + delete TSOCKET; } - delete SOCKET; } @@ -199,7 +220,8 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){ out.Header << "Content-Type: text/json; charset=utf-8"; } //Return any information - SOCKET->sendTextMessage(out.assembleMessage()); + if(SOCKET!=0){ SOCKET->sendTextMessage(out.assembleMessage()); } + else if(TSOCKET!=0){ TSOCKET->write(out.assembleMessage().toUtf8().data()); } } // === SYSCACHE REQUEST INTERACTION === @@ -303,6 +325,10 @@ void WebSocket::checkIdle(){ qDebug() << " - Client Timeout: Closing connection..."; SOCKET->close(); //timeout - close the connection to make way for others } + if(TSOCKET !=0){ + qDebug() << " - Client Timeout: Closing connection..."; + TSOCKET->close(); //timeout - close the connection to make way for others + } } void WebSocket::SocketClosing(){ @@ -314,7 +340,9 @@ void WebSocket::SocketClosing(){ //Stop any current requests //Reset the pointer - SOCKET = 0; + if(SOCKET!=0){ SOCKET = 0; } + if(TSOCKET!=0){ TSOCKET = 0; } + emit SocketClosed(SockID); } @@ -334,6 +362,15 @@ void WebSocket::EvaluateMessage(const QString &msg){ qDebug() << "Done with Message"; } +void WebSocket::EvaluateTcpMessage(){ + //Need to read the data from the Tcp socket and turn it into a string + qDebug() << "New TCP Message:"; + if(idletimer->isActive()){ idletimer->stop(); } + EvaluateREST( QString(TSOCKET->readAll()) ); + idletimer->start(); + qDebug() << "Done with Message"; +} + // ====================== // PUBLIC SLOTS // ====================== @@ -359,5 +396,7 @@ void WebSocket::AppCafeStatusUpdate(QString msg){ retdoc.setObject(ret); out.Body = retdoc.toJson(); out.Header << "Content-Type: text/json; charset=utf-8"; - SOCKET->sendTextMessage(out.assembleMessage()); + //Now send the message back through the socket + if(SOCKET!=0){ SOCKET->sendTextMessage(out.assembleMessage()); } + else if(TSOCKET!=0){ TSOCKET->write(out.assembleMessage().toUtf8().data()); } } diff --git a/src/server/WebSocket.h b/src/server/WebSocket.h index 5f37f83..cde25df 100644 --- a/src/server/WebSocket.h +++ b/src/server/WebSocket.h @@ -7,6 +7,7 @@ #define _PCBSD_REST_WEB_SOCKET_H #include +#include #include #include #include @@ -23,6 +24,7 @@ class WebSocket : public QObject{ Q_OBJECT public: WebSocket(QWebSocket*, QString ID, AuthorizationManager *auth); + WebSocket(QTcpSocket*, QString ID, AuthorizationManager *auth); ~WebSocket(); QString ID(); @@ -31,6 +33,7 @@ public: private: QTimer *idletimer; QWebSocket *SOCKET; + QTcpSocket *TSOCKET; QString SockID, SockAuthToken, lastDispatchEvent; AuthorizationManager *AUTHSYSTEM; bool SendAppCafeEvents; @@ -54,6 +57,7 @@ private slots: //Currently connected socket signal/slot connections void EvaluateMessage(const QByteArray&); void EvaluateMessage(const QString&); + void EvaluateTcpMessage(); public slots: void AppCafeStatusUpdate(QString msg = ""); diff --git a/src/server/main.cpp b/src/server/main.cpp index 77f09eb..e4f327d 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -18,8 +18,10 @@ #endif #define DEBUG 1 +#define WSPORTNUMBER 12142 // WebSocket server default port +#define PORTNUMBER 12142 // TCP server default port -QFile logfile("/var/log/sysadm-server.log"); +QFile logfile; void MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg){ QString txt; switch(type){ @@ -53,9 +55,21 @@ int main( int argc, char ** argv ) qDebug() << "sysadm-server must be started as root!"; return 1; } - //Setup the log file - if(DEBUG){ - qDebug() << "Log File:" << logfile.fileName(); + //Evaluate input arguments + bool websocket = false; + quint16 port = 0; + for(int i=1; istartServer() ){ + if( w->startServer(port, websocket) ){ //Now start the event loop int ret = a.exec(); + qDebug() << "Server Stopped:" << QDateTime::currentDateTime().toString(Qt::ISODate); logfile.close(); return ret; }else{ + qDebug() << "[FATAL] Server could not be started:" << QDateTime::currentDateTime().toString(Qt::ISODate); + qDebug() << " - Tried port:" << port; + logfile.close(); return 1; } }