From 5cf8fc12ae000cd2d2b54a2bf158f82bdb8a0e67 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Sep 2013 21:28:41 +0200 Subject: Added cHTTPServer callbacks; fixed keep-alives. The HTTP server now calls callbacks specified in its start function (debugified atm.) and it processes multiple requests on a single connection. --- source/HTTPServer/HTTPConnection.cpp | 35 +++++++++++++++- source/HTTPServer/HTTPConnection.h | 5 ++- source/HTTPServer/HTTPServer.cpp | 80 +++++++++++++++++++++++++++++++----- source/HTTPServer/HTTPServer.h | 25 +++++++++++ 4 files changed, 132 insertions(+), 13 deletions(-) (limited to 'source/HTTPServer') diff --git a/source/HTTPServer/HTTPConnection.cpp b/source/HTTPServer/HTTPConnection.cpp index 2265d970f..c36b07d3d 100644 --- a/source/HTTPServer/HTTPConnection.cpp +++ b/source/HTTPServer/HTTPConnection.cpp @@ -69,6 +69,39 @@ void cHTTPConnection::FinishResponse(void) +void cHTTPConnection::AwaitNextRequest(void) +{ + switch (m_State) + { + case wcsRecvIdle: + { + // The client is waiting for a response, send an "Internal server error": + m_OutgoingData.append("HTTP/1.1 500 Internal Server Error\r\n\r\n"); + m_HTTPServer.NotifyConnectionWrite(*this); + m_State = wcsRecvHeaders; + break; + } + + case wcsSendingResp: + { + // The response headers have been sent, we need to terminate the response body: + m_OutgoingData.append("0\r\n\r\n"); + m_State = wcsRecvHeaders; + break; + } + + default: + { + ASSERT(!"Unhandled state recovery"); + break; + } + } +} + + + + + void cHTTPConnection::DataReceived(const char * a_Data, int a_Size) { switch (m_State) @@ -128,10 +161,10 @@ void cHTTPConnection::DataReceived(const char * a_Data, int a_Size) } if (m_CurrentRequestBodyRemaining == 0) { + m_State = wcsRecvIdle; m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); delete m_CurrentRequest; m_CurrentRequest = NULL; - m_State = wcsRecvIdle; } break; } diff --git a/source/HTTPServer/HTTPConnection.h b/source/HTTPServer/HTTPConnection.h index d8ecdf1d9..46c36a8a2 100644 --- a/source/HTTPServer/HTTPConnection.h +++ b/source/HTTPServer/HTTPConnection.h @@ -52,9 +52,12 @@ public: /// Sends the data as the response (may be called multiple times) void Send(const AString & a_Data) { Send(a_Data.data(), a_Data.size()); } - /// Finishes sending current response, gets ready for receiving another request (HTTP 1.1 keepalive) + /// Indicates that the current response is finished, gets ready for receiving another request (HTTP 1.1 keepalive) void FinishResponse(void); + /// Resets the connection for a new request. Depending on the state, this will send an "InternalServerError" status or a "ResponseEnd" + void AwaitNextRequest(void); + protected: typedef std::map cNameValueMap; diff --git a/source/HTTPServer/HTTPServer.cpp b/source/HTTPServer/HTTPServer.cpp index d518df10d..8494d6fce 100644 --- a/source/HTTPServer/HTTPServer.cpp +++ b/source/HTTPServer/HTTPServer.cpp @@ -22,10 +22,43 @@ +class cDebugCallbacks : + public cHTTPServer::cCallbacks +{ + virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override + { + // Nothing needed + } + + + virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override + { + // TODO + } + + + virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override + { + cHTTPResponse Resp; + Resp.SetContentType("text/plain"); + a_Connection.Send(Resp); + a_Connection.Send("Hello, world"); + } + + +} g_DebugCallbacks; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHTTPServer: + cHTTPServer::cHTTPServer(void) : m_ListenThreadIPv4(*this, cSocket::IPv4, "WebServer IPv4"), m_ListenThreadIPv6(*this, cSocket::IPv6, "WebServer IPv6"), - m_SocketThreads() + m_Callbacks(NULL) { } @@ -48,6 +81,20 @@ bool cHTTPServer::Initialize(cIniFile & a_IniFile) LOG("WebAdmin is disabled"); return false; } + + // DEBUG: + return Start(g_DebugCallbacks); + + return true; +} + + + + + +bool cHTTPServer::Start(cCallbacks & a_Callbacks) +{ + m_Callbacks = &a_Callbacks; if (!m_ListenThreadIPv4.Start()) { return false; @@ -64,6 +111,23 @@ bool cHTTPServer::Initialize(cIniFile & a_IniFile) +void cHTTPServer::Stop(void) +{ + m_ListenThreadIPv4.Stop(); + m_ListenThreadIPv6.Stop(); + + // Drop all current connections: + cCSLock Lock(m_CSConnections); + for (cHTTPConnections::iterator itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) + { + m_SocketThreads.RemoveClient(*itr); + } // for itr - m_Connections[] +} + + + + + void cHTTPServer::OnConnectionAccepted(cSocket & a_Socket) { cHTTPConnection * Connection = new cHTTPConnection(*this); @@ -105,7 +169,7 @@ void cHTTPServer::NotifyConnectionWrite(cHTTPConnection & a_Connection) void cHTTPServer::NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) { - // TODO + m_Callbacks->OnRequestBegun(a_Connection, a_Request); } @@ -114,7 +178,7 @@ void cHTTPServer::NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Re void cHTTPServer::RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) { - // TODO + m_Callbacks->OnRequestBody(a_Connection, a_Request, a_Data, a_Size); } @@ -123,14 +187,8 @@ void cHTTPServer::RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_R void cHTTPServer::RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) { - // TODO - - // DEBUG: Send a debug response: - cHTTPResponse Resp; - Resp.SetContentType("text/plain"); - a_Connection.Send(Resp); - a_Connection.Send("Hello"); - a_Connection.FinishResponse(); + m_Callbacks->OnRequestFinished(a_Connection, a_Request); + a_Connection.AwaitNextRequest(); } diff --git a/source/HTTPServer/HTTPServer.h b/source/HTTPServer/HTTPServer.h index 2d0acc386..efe60809d 100644 --- a/source/HTTPServer/HTTPServer.h +++ b/source/HTTPServer/HTTPServer.h @@ -34,10 +34,31 @@ class cHTTPServer : public cListenThread::cCallback { public: + class cCallbacks + { + public: + /** Called when a new request arrives over a connection and its headers have been parsed. + The request body needn't have arrived yet. + */ + virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0; + + /// Called when another part of request body has arrived. + virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) = 0; + + /// Called when the request body has been fully received in previous calls to OnRequestBody() + virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0; + } ; + cHTTPServer(void); bool Initialize(cIniFile & a_IniFile); + /// Starts the server and assigns the callbacks to use for incoming requests + bool Start(cCallbacks & a_Callbacks); + + /// Stops the server, drops all current connections + void Stop(void); + protected: friend class cHTTPConnection; @@ -48,6 +69,10 @@ protected: cCriticalSection m_CSConnections; cHTTPConnections m_Connections; ///< All the connections that are currently being serviced + + /// The callbacks to call for various events + cCallbacks * m_Callbacks; + // cListenThread::cCallback overrides: virtual void OnConnectionAccepted(cSocket & a_Socket) override; -- cgit v1.2.3