#include "WebServer.h" #include "Reports.h" WebServer::WebServer(ReportCommand *_rc, unsigned int _port): InputOutput(_rc), m_port(_port), m_sc(INVALID_SOCKET) { pthread_invalidate(m_runThread); } int WebServer::shutdown() { m_run = false; //this will return an error if m_runThread == this thread. //however, this is not a problem if (pthread_isvalid(m_runThread)) pthread_join(m_runThread, 0); return 0; } int WebServer::listen(const int mode) { InputOutput::listen(mode); //note that m_runThread is set to 0 after staticlistenasync() returns if (mode == ASYNC) return pthread_create(&m_runThread, 0, WebServer::staticlistenasync, this); else return listenasync(); } void WebServer::staticrunCleanup(LPVOID lpParam) { WebServer *ws = (WebServer*)lpParam; pthread_invalidate(ws->m_runThread); } THREAD_CALLBACK_TYPE WebServer::staticlistenasync(LPVOID lpParam) { WebServer *ws = (WebServer*)lpParam; pthread_cleanup_push(WebServer::staticrunCleanup, ws); ws->listenasync(); pthread_cleanup_pop(1); //and invoke return 0; } int WebServer::listenasync() { //------------------------------------------- webserver char *buffer = (char*)mallocCheck(10*1024); char *command, *query; int bytes; const char *response; //socket listen INITSOCKET; //also done by the DomainConnection, but it does not hurt to init twice { DEBUGPRINT0("[WEBSERVER]: init", DEBUG_LINE); SOCKET s = INVALID_SOCKET; sockaddr_in addr; //The address structure for a TCP socket addr.sin_family = AF_INET; //Address family addr.sin_port = htons(m_port); //Assign port to this socket addr.sin_addr.s_addr = htonl (INADDR_ANY); //Accept a connection from any IP using INADDR_ANY s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //Create socket if (s == INVALID_SOCKET) { DEBUGERROR0("[WEBSERVER]: s=socket() :INVALID_SOCKET"); DEBUGERROR0("[WEBSERVER]: InvalidSocket()"); throw InvalidSocket(); } if (bind(s, (const LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR) { DEBUGERROR0("[WEBSERVER]: bind(s) :INVALID_SOCKET"); CLOSESOCKET(s); DEBUGERROR0("[WEBSERVER]: InvalidSocket()"); throw InvalidSocket(); } SETLINGER(s,1); //force the listen socket to TIME_WAIT only 1 second //event loop MSG0("[WEBSERVER]: listening..."); m_run = true; while (m_run) { //blocking socket, wait for incoming reqeuest DEBUGPRINT0("[WEBSERVER]: listen", DEBUG_LINE); if (::listen(s, SOMAXCONN) == SOCKET_ERROR) { DEBUGERROR0("[WEBSERVER]: listen(s) :SOCKET_ERROR"); CLOSESOCKET(s); DEBUGERROR0("[WEBSERVER]: InvalidSocket()"); throw InvalidSocket(); } else { //incoming request. Windows allows only one blocking socket function call per thread. DEBUGPRINT0("[WEBSERVER]: incoming request", DEBUG_LINE); m_sc = ::accept(s, NULL, NULL); //accept connection so that the listen doesn't return again immediately if (m_sc == INVALID_SOCKET) { DEBUGERROR0("[WEBSERVER]: accept(s) :INVALID_SOCKET"); CLOSESOCKET(s); DEBUGERROR0("[WEBSERVER]: InvalidSocket()"); throw InvalidSocket(); } SETLINGER(m_sc,1); //force the listen socket to TIME_WAIT only 1 second //get all of request to force the client to shutdown the socket (max 10kb) bytes = ::recv(m_sc, buffer, 10*1024, 0); if (_STRNCMP(buffer, "GET ", 4)) { DEBUGERROR0("[WEBSERVER]: InvalidRequest()"); throw InvalidRequest(); } else { //find command and query sections of the relative URL query = command = buffer+4; //HTTP protocol relative URL start while (*command && *++command != ' '); //find command end *command = 0; //zero terminate it while (*--command != ' ' && *command != '/'); //find command start (before end) command++; if (!*command) command = "Report_Index"; //default command while (*query && *++query != '?'); //find the query start (if exists) *query = 0; //zero terminate //call command handler object if (response = m_rc->newRequest(command, this, query)) send(response); } CLOSESOCKET(m_sc); m_sc = INVALID_SOCKET; } } CLOSESOCKET(s); } free(buffer); MSG0("[WEBSERVER]: finished (closed socket)"); return 0; } int WebServer::send(const char *responsepart) const { ::send(m_sc, responsepart, (int)strlen(responsepart), 0); return 0; } int WebServer::sendHandshake(const int hs, const char *xslt) const { //MIMEType could be text/xsl. Linux requires application/xml switch (hs) { case html: { send("HTTP/1.0 200 OK\r\nConnection: close;\r\nContent-type: text/html; charset=UTF-8;\r\n\r\n"); break; } case xsl: { send("HTTP/1.0 200 OK\r\nConnection: close;\r\nContent-type: application/xml; charset=UTF-8;\r\n\r\n"); break; } case xml: { send("HTTP/1.0 200 OK\r\nConnection: close;\r\nContent-type: application/xml; charset=UTF-8;\r\n\r\n"); send("\r\n"); break; } } if (xslt) sendXSLT(xslt); return 0; } //private void WebServer::sendXSLT(const char *sheet) const { send("\r\n"); }