From 2dbc54a14824329faf53851441c6585fd451f0a2 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 25 Dec 2015 19:42:50 +0100 Subject: Renamed the HTTP classes to indicate they're for server. --- src/HTTPServer/CMakeLists.txt | 10 +- src/HTTPServer/HTTPConnection.cpp | 278 ----------------------------- src/HTTPServer/HTTPConnection.h | 124 ------------- src/HTTPServer/HTTPServer.cpp | 20 +-- src/HTTPServer/HTTPServer.h | 24 ++- src/HTTPServer/HTTPServerConnection.cpp | 278 +++++++++++++++++++++++++++++ src/HTTPServer/HTTPServerConnection.h | 124 +++++++++++++ src/HTTPServer/SslHTTPConnection.cpp | 115 ------------ src/HTTPServer/SslHTTPConnection.h | 47 ----- src/HTTPServer/SslHTTPServerConnection.cpp | 115 ++++++++++++ src/HTTPServer/SslHTTPServerConnection.h | 47 +++++ src/WebAdmin.cpp | 14 +- src/WebAdmin.h | 12 +- 13 files changed, 603 insertions(+), 605 deletions(-) delete mode 100644 src/HTTPServer/HTTPConnection.cpp delete mode 100644 src/HTTPServer/HTTPConnection.h create mode 100644 src/HTTPServer/HTTPServerConnection.cpp create mode 100644 src/HTTPServer/HTTPServerConnection.h delete mode 100644 src/HTTPServer/SslHTTPConnection.cpp delete mode 100644 src/HTTPServer/SslHTTPConnection.h create mode 100644 src/HTTPServer/SslHTTPServerConnection.cpp create mode 100644 src/HTTPServer/SslHTTPServerConnection.h diff --git a/src/HTTPServer/CMakeLists.txt b/src/HTTPServer/CMakeLists.txt index a08a1fcf8..e8026f761 100644 --- a/src/HTTPServer/CMakeLists.txt +++ b/src/HTTPServer/CMakeLists.txt @@ -6,31 +6,31 @@ include_directories ("${PROJECT_SOURCE_DIR}/../") SET (SRCS EnvelopeParser.cpp - HTTPConnection.cpp HTTPFormParser.cpp HTTPMessage.cpp HTTPServer.cpp + HTTPServerConnection.cpp MultipartParser.cpp NameValueParser.cpp - SslHTTPConnection.cpp + SslHTTPServerConnection.cpp UrlParser.cpp ) SET (HDRS EnvelopeParser.h - HTTPConnection.h HTTPFormParser.h HTTPMessage.h HTTPServer.h + HTTPServerConnection.h MultipartParser.h NameValueParser.h - SslHTTPConnection.h + SslHTTPServerConnection.h UrlParser.h ) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set_source_files_properties(HTTPServer.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=global-constructors ") - set_source_files_properties(HTTPConnection.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=switch-enum") + set_source_files_properties(HTTPServerConnection.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=switch-enum") set_source_files_properties(HTTPMessage.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=tautological-compare") endif() diff --git a/src/HTTPServer/HTTPConnection.cpp b/src/HTTPServer/HTTPConnection.cpp deleted file mode 100644 index 0db154139..000000000 --- a/src/HTTPServer/HTTPConnection.cpp +++ /dev/null @@ -1,278 +0,0 @@ - -// HTTPConnection.cpp - -// Implements the cHTTPConnection class representing a single persistent connection in the HTTP server. - -#include "Globals.h" -#include "HTTPConnection.h" -#include "HTTPMessage.h" -#include "HTTPServer.h" - - - - - -cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) : - m_HTTPServer(a_HTTPServer), - m_State(wcsRecvHeaders), - m_CurrentRequest(nullptr), - m_CurrentRequestBodyRemaining(0) -{ - // LOGD("HTTP: New connection at %p", this); -} - - - - - -cHTTPConnection::~cHTTPConnection() -{ - // LOGD("HTTP: Connection deleting: %p", this); - delete m_CurrentRequest; - m_CurrentRequest = nullptr; -} - - - - - -void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response) -{ - SendData(Printf("HTTP/1.1 %d %s\r\n", a_StatusCode, a_Response.c_str())); - SendData(Printf("Content-Length: %u\r\n\r\n", static_cast(a_Response.size()))); - SendData(a_Response.data(), a_Response.size()); - m_State = wcsRecvHeaders; -} - - - - - -void cHTTPConnection::SendNeedAuth(const AString & a_Realm) -{ - SendData(Printf("HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Length: 0\r\n\r\n", a_Realm.c_str())); - m_State = wcsRecvHeaders; -} - - - - - -void cHTTPConnection::Send(const cHTTPResponse & a_Response) -{ - ASSERT(m_State == wcsRecvIdle); - AString toSend; - a_Response.AppendToData(toSend); - m_State = wcsSendingResp; - SendData(toSend); -} - - - - - -void cHTTPConnection::Send(const void * a_Data, size_t a_Size) -{ - ASSERT(m_State == wcsSendingResp); - // We're sending in Chunked transfer encoding - SendData(Printf(SIZE_T_FMT_HEX "\r\n", a_Size)); - SendData(a_Data, a_Size); - SendData("\r\n"); -} - - - - - -void cHTTPConnection::FinishResponse(void) -{ - ASSERT(m_State == wcsSendingResp); - SendData("0\r\n\r\n"); - m_State = wcsRecvHeaders; -} - - - - - -void cHTTPConnection::AwaitNextRequest(void) -{ - switch (m_State) - { - case wcsRecvHeaders: - { - // Nothing has been received yet, or a special response was given (SendStatusAndReason() or SendNeedAuth()) - break; - } - - case wcsRecvIdle: - { - // The client is waiting for a response, send an "Internal server error": - SendData("HTTP/1.1 500 Internal Server Error\r\n\r\n"); - m_State = wcsRecvHeaders; - break; - } - - case wcsSendingResp: - { - // The response headers have been sent, we need to terminate the response body: - SendData("0\r\n\r\n"); - m_State = wcsRecvHeaders; - break; - } - - default: - { - ASSERT(!"Unhandled state recovery"); - break; - } - } -} - - - - - -void cHTTPConnection::Terminate(void) -{ - if (m_CurrentRequest != nullptr) - { - m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); - } - m_Link.reset(); -} - - - - - -void cHTTPConnection::OnLinkCreated(cTCPLinkPtr a_Link) -{ - ASSERT(m_Link == nullptr); - m_Link = a_Link; -} - - - - - -void cHTTPConnection::OnReceivedData(const char * a_Data, size_t a_Size) -{ - ASSERT(m_Link != nullptr); - - switch (m_State) - { - case wcsRecvHeaders: - { - if (m_CurrentRequest == nullptr) - { - m_CurrentRequest = new cHTTPRequest; - } - - size_t BytesConsumed = m_CurrentRequest->ParseHeaders(a_Data, a_Size); - if (BytesConsumed == AString::npos) - { - delete m_CurrentRequest; - m_CurrentRequest = nullptr; - m_State = wcsInvalid; - m_Link->Close(); - m_Link.reset(); - return; - } - if (m_CurrentRequest->IsInHeaders()) - { - // The request headers are not yet complete - return; - } - - // The request has finished parsing its headers successfully, notify of it: - m_State = wcsRecvBody; - m_HTTPServer.NewRequest(*this, *m_CurrentRequest); - m_CurrentRequestBodyRemaining = m_CurrentRequest->GetContentLength(); - if (m_CurrentRequestBodyRemaining == AString::npos) - { - // The body length was not specified in the request, assume zero - m_CurrentRequestBodyRemaining = 0; - } - - // Process the rest of the incoming data into the request body: - if (a_Size > BytesConsumed) - { - cHTTPConnection::OnReceivedData(a_Data + BytesConsumed, a_Size - BytesConsumed); - return; - } - else - { - cHTTPConnection::OnReceivedData("", 0); // If the request has zero body length, let it be processed right-away - return; - } - } - - case wcsRecvBody: - { - ASSERT(m_CurrentRequest != nullptr); - if (m_CurrentRequestBodyRemaining > 0) - { - size_t BytesToConsume = std::min(m_CurrentRequestBodyRemaining, static_cast(a_Size)); - m_HTTPServer.RequestBody(*this, *m_CurrentRequest, a_Data, BytesToConsume); - m_CurrentRequestBodyRemaining -= BytesToConsume; - } - if (m_CurrentRequestBodyRemaining == 0) - { - m_State = wcsRecvIdle; - m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); - if (!m_CurrentRequest->DoesAllowKeepAlive()) - { - m_State = wcsInvalid; - m_Link->Close(); - m_Link.reset(); - return; - } - delete m_CurrentRequest; - m_CurrentRequest = nullptr; - } - break; - } - - default: - { - // TODO: Should we be receiving data in this state? - break; - } - } -} - - - - - -void cHTTPConnection::OnRemoteClosed(void) -{ - if (m_CurrentRequest != nullptr) - { - m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); - } - m_Link.reset(); -} - - - - - - -void cHTTPConnection::OnError(int a_ErrorCode, const AString & a_ErrorMsg) -{ - OnRemoteClosed(); -} - - - - -void cHTTPConnection::SendData(const void * a_Data, size_t a_Size) -{ - m_Link->Send(a_Data, a_Size); -} - - - - diff --git a/src/HTTPServer/HTTPConnection.h b/src/HTTPServer/HTTPConnection.h deleted file mode 100644 index 414075411..000000000 --- a/src/HTTPServer/HTTPConnection.h +++ /dev/null @@ -1,124 +0,0 @@ - -// HTTPConnection.h - -// Declares the cHTTPConnection class representing a single persistent connection in the HTTP server. - - - - - -#pragma once - -#include "../OSSupport/Network.h" - - - - - -// fwd: -class cHTTPServer; -class cHTTPResponse; -class cHTTPRequest; - - - - - -class cHTTPConnection : - public cTCPLink::cCallbacks -{ -public: - - enum eState - { - wcsRecvHeaders, ///< Receiving request headers (m_CurrentRequest is created if nullptr) - wcsRecvBody, ///< Receiving request body (m_CurrentRequest is valid) - wcsRecvIdle, ///< Has received the entire body, waiting to send the response (m_CurrentRequest == nullptr) - wcsSendingResp, ///< Sending response body (m_CurrentRequest == nullptr) - wcsInvalid, ///< The request was malformed, the connection is closing - } ; - - cHTTPConnection(cHTTPServer & a_HTTPServer); - virtual ~cHTTPConnection(); - - /** Sends HTTP status code together with a_Reason (used for HTTP errors). - Sends the a_Reason as the body as well, so that browsers display it. */ - void SendStatusAndReason(int a_StatusCode, const AString & a_Reason); - - /** Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm */ - void SendNeedAuth(const AString & a_Realm); - - /** Sends the headers contained in a_Response */ - void Send(const cHTTPResponse & a_Response); - - /** Sends the data as the response (may be called multiple times) */ - void Send(const void * a_Data, size_t a_Size); - - /** Sends the data as the response (may be called multiple times) */ - void Send(const AString & a_Data) { Send(a_Data.data(), a_Data.size()); } - - /** Indicates that the current response is finished, gets ready for receiving another request (HTTP 1.1 keepalive) */ - void FinishResponse(void); - - /** Resets the internal connection state for a new request. - Depending on the state, this will send an "InternalServerError" status or a "ResponseEnd" */ - void AwaitNextRequest(void); - - /** Terminates the connection; finishes any request being currently processed */ - void Terminate(void); - -protected: - typedef std::map cNameValueMap; - - /** The parent webserver that is to be notified of events on this connection */ - cHTTPServer & m_HTTPServer; - - /** All the incoming data until the entire request header is parsed */ - AString m_IncomingHeaderData; - - /** Status in which the request currently is */ - eState m_State; - - /** The request being currently received - Valid only between having parsed the headers and finishing receiving the body. */ - cHTTPRequest * m_CurrentRequest; - - /** Number of bytes that remain to read for the complete body of the message to be received. - Valid only in wcsRecvBody */ - size_t m_CurrentRequestBodyRemaining; - - /** The network link attached to this connection. */ - cTCPLinkPtr m_Link; - - - // cTCPLink::cCallbacks overrides: - /** The link instance has been created, remember it. */ - virtual void OnLinkCreated(cTCPLinkPtr a_Link) override; - - /** Data is received from the client. */ - virtual void OnReceivedData(const char * a_Data, size_t a_Size) override; - - /** The socket has been closed for any reason. */ - virtual void OnRemoteClosed(void) override; - - /** An error has occurred on the socket. */ - virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; - - // Overridable: - /** Called to send raw data over the link. Descendants may provide data transformations (SSL etc.) */ - virtual void SendData(const void * a_Data, size_t a_Size); - - /** Sends the raw data over the link. - Descendants may provide data transformations (SSL etc.) via the overridable SendData() function. */ - void SendData(const AString & a_Data) - { - SendData(a_Data.data(), a_Data.size()); - } -} ; - -typedef std::vector cHTTPConnections; - - - - - diff --git a/src/HTTPServer/HTTPServer.cpp b/src/HTTPServer/HTTPServer.cpp index 814a87fe4..1cdf29bca 100644 --- a/src/HTTPServer/HTTPServer.cpp +++ b/src/HTTPServer/HTTPServer.cpp @@ -6,9 +6,9 @@ #include "Globals.h" #include "HTTPServer.h" #include "HTTPMessage.h" -#include "HTTPConnection.h" +#include "HTTPServerConnection.h" #include "HTTPFormParser.h" -#include "SslHTTPConnection.h" +#include "SslHTTPServerConnection.h" @@ -28,7 +28,7 @@ class cDebugCallbacks : public cHTTPServer::cCallbacks, protected cHTTPFormParser::cCallbacks { - virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override + virtual void OnRequestBegun(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request) override { UNUSED(a_Connection); @@ -39,7 +39,7 @@ class cDebugCallbacks : } - virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) override + virtual void OnRequestBody(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) override { UNUSED(a_Connection); @@ -51,7 +51,7 @@ class cDebugCallbacks : } - virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override + virtual void OnRequestFinished(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request) override { cHTTPFormParser * FormParser = reinterpret_cast(a_Request.GetUserData()); if (FormParser != nullptr) @@ -268,11 +268,11 @@ cTCPLink::cCallbacksPtr cHTTPServer::OnIncomingConnection(const AString & a_Remo if (m_Cert.get() != nullptr) { - return std::make_shared(*this, m_Cert, m_CertPrivKey); + return std::make_shared(*this, m_Cert, m_CertPrivKey); } else { - return std::make_shared(*this); + return std::make_shared(*this); } } @@ -280,7 +280,7 @@ cTCPLink::cCallbacksPtr cHTTPServer::OnIncomingConnection(const AString & a_Remo -void cHTTPServer::NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +void cHTTPServer::NewRequest(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request) { m_Callbacks->OnRequestBegun(a_Connection, a_Request); } @@ -289,7 +289,7 @@ void cHTTPServer::NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Re -void cHTTPServer::RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) +void cHTTPServer::RequestBody(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) { m_Callbacks->OnRequestBody(a_Connection, a_Request, a_Data, a_Size); } @@ -298,7 +298,7 @@ void cHTTPServer::RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_R -void cHTTPServer::RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +void cHTTPServer::RequestFinished(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request) { m_Callbacks->OnRequestFinished(a_Connection, a_Request); a_Connection.AwaitNextRequest(); diff --git a/src/HTTPServer/HTTPServer.h b/src/HTTPServer/HTTPServer.h index 2a094b413..2fd4cdcfc 100644 --- a/src/HTTPServer/HTTPServer.h +++ b/src/HTTPServer/HTTPServer.h @@ -23,9 +23,7 @@ class cHTTPMessage; class cHTTPRequest; class cHTTPResponse; -class cHTTPConnection; - -typedef std::vector cHTTPConnections; +class cHTTPServerConnection; @@ -42,14 +40,14 @@ public: /** Called when a new request arrives over a connection and all its headers have been parsed. The request body needn't have arrived yet. */ - virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0; + virtual void OnRequestBegun(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request) = 0; /** Called when another part of request body has arrived. May be called multiple times for a single request. */ - virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) = 0; + virtual void OnRequestBody(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t 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; + virtual void OnRequestFinished(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request) = 0; } ; cHTTPServer(void); @@ -65,8 +63,8 @@ public: void Stop(void); protected: - friend class cHTTPConnection; - friend class cSslHTTPConnection; + friend class cHTTPServerConnection; + friend class cSslHTTPServerConnection; friend class cHTTPServerListenCallbacks; /** The cNetwork API handle for the listening socket. */ @@ -86,15 +84,15 @@ protected: Returns the connection instance to be used as the cTCPLink callbacks. */ cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort); - /** Called by cHTTPConnection when it finishes parsing the request header */ - void NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + /** Called by cHTTPServerConnection when it finishes parsing the request header */ + void NewRequest(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request); /** Called by cHTTPConenction when it receives more data for the request body. May be called multiple times for a single request. */ - void RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size); + void RequestBody(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size); - /** Called by cHTTPConnection when it detects that the request has finished (all of its body has been received) */ - void RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + /** Called by cHTTPServerConnection when it detects that the request has finished (all of its body has been received) */ + void RequestFinished(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request); } ; diff --git a/src/HTTPServer/HTTPServerConnection.cpp b/src/HTTPServer/HTTPServerConnection.cpp new file mode 100644 index 000000000..3c4acc2d4 --- /dev/null +++ b/src/HTTPServer/HTTPServerConnection.cpp @@ -0,0 +1,278 @@ + +// HTTPConnection.cpp + +// Implements the cHTTPServerConnection class representing a single persistent connection in the HTTP server. + +#include "Globals.h" +#include "HTTPServerConnection.h" +#include "HTTPMessage.h" +#include "HTTPServer.h" + + + + + +cHTTPServerConnection::cHTTPServerConnection(cHTTPServer & a_HTTPServer) : + m_HTTPServer(a_HTTPServer), + m_State(wcsRecvHeaders), + m_CurrentRequest(nullptr), + m_CurrentRequestBodyRemaining(0) +{ + // LOGD("HTTP: New connection at %p", this); +} + + + + + +cHTTPServerConnection::~cHTTPServerConnection() +{ + // LOGD("HTTP: Connection deleting: %p", this); + delete m_CurrentRequest; + m_CurrentRequest = nullptr; +} + + + + + +void cHTTPServerConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response) +{ + SendData(Printf("HTTP/1.1 %d %s\r\n", a_StatusCode, a_Response.c_str())); + SendData(Printf("Content-Length: %u\r\n\r\n", static_cast(a_Response.size()))); + SendData(a_Response.data(), a_Response.size()); + m_State = wcsRecvHeaders; +} + + + + + +void cHTTPServerConnection::SendNeedAuth(const AString & a_Realm) +{ + SendData(Printf("HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Length: 0\r\n\r\n", a_Realm.c_str())); + m_State = wcsRecvHeaders; +} + + + + + +void cHTTPServerConnection::Send(const cHTTPResponse & a_Response) +{ + ASSERT(m_State == wcsRecvIdle); + AString toSend; + a_Response.AppendToData(toSend); + m_State = wcsSendingResp; + SendData(toSend); +} + + + + + +void cHTTPServerConnection::Send(const void * a_Data, size_t a_Size) +{ + ASSERT(m_State == wcsSendingResp); + // We're sending in Chunked transfer encoding + SendData(Printf(SIZE_T_FMT_HEX "\r\n", a_Size)); + SendData(a_Data, a_Size); + SendData("\r\n"); +} + + + + + +void cHTTPServerConnection::FinishResponse(void) +{ + ASSERT(m_State == wcsSendingResp); + SendData("0\r\n\r\n"); + m_State = wcsRecvHeaders; +} + + + + + +void cHTTPServerConnection::AwaitNextRequest(void) +{ + switch (m_State) + { + case wcsRecvHeaders: + { + // Nothing has been received yet, or a special response was given (SendStatusAndReason() or SendNeedAuth()) + break; + } + + case wcsRecvIdle: + { + // The client is waiting for a response, send an "Internal server error": + SendData("HTTP/1.1 500 Internal Server Error\r\n\r\n"); + m_State = wcsRecvHeaders; + break; + } + + case wcsSendingResp: + { + // The response headers have been sent, we need to terminate the response body: + SendData("0\r\n\r\n"); + m_State = wcsRecvHeaders; + break; + } + + default: + { + ASSERT(!"Unhandled state recovery"); + break; + } + } +} + + + + + +void cHTTPServerConnection::Terminate(void) +{ + if (m_CurrentRequest != nullptr) + { + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + } + m_Link.reset(); +} + + + + + +void cHTTPServerConnection::OnLinkCreated(cTCPLinkPtr a_Link) +{ + ASSERT(m_Link == nullptr); + m_Link = a_Link; +} + + + + + +void cHTTPServerConnection::OnReceivedData(const char * a_Data, size_t a_Size) +{ + ASSERT(m_Link != nullptr); + + switch (m_State) + { + case wcsRecvHeaders: + { + if (m_CurrentRequest == nullptr) + { + m_CurrentRequest = new cHTTPRequest; + } + + size_t BytesConsumed = m_CurrentRequest->ParseHeaders(a_Data, a_Size); + if (BytesConsumed == AString::npos) + { + delete m_CurrentRequest; + m_CurrentRequest = nullptr; + m_State = wcsInvalid; + m_Link->Close(); + m_Link.reset(); + return; + } + if (m_CurrentRequest->IsInHeaders()) + { + // The request headers are not yet complete + return; + } + + // The request has finished parsing its headers successfully, notify of it: + m_State = wcsRecvBody; + m_HTTPServer.NewRequest(*this, *m_CurrentRequest); + m_CurrentRequestBodyRemaining = m_CurrentRequest->GetContentLength(); + if (m_CurrentRequestBodyRemaining == AString::npos) + { + // The body length was not specified in the request, assume zero + m_CurrentRequestBodyRemaining = 0; + } + + // Process the rest of the incoming data into the request body: + if (a_Size > BytesConsumed) + { + cHTTPServerConnection::OnReceivedData(a_Data + BytesConsumed, a_Size - BytesConsumed); + return; + } + else + { + cHTTPServerConnection::OnReceivedData("", 0); // If the request has zero body length, let it be processed right-away + return; + } + } + + case wcsRecvBody: + { + ASSERT(m_CurrentRequest != nullptr); + if (m_CurrentRequestBodyRemaining > 0) + { + size_t BytesToConsume = std::min(m_CurrentRequestBodyRemaining, static_cast(a_Size)); + m_HTTPServer.RequestBody(*this, *m_CurrentRequest, a_Data, BytesToConsume); + m_CurrentRequestBodyRemaining -= BytesToConsume; + } + if (m_CurrentRequestBodyRemaining == 0) + { + m_State = wcsRecvIdle; + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + if (!m_CurrentRequest->DoesAllowKeepAlive()) + { + m_State = wcsInvalid; + m_Link->Close(); + m_Link.reset(); + return; + } + delete m_CurrentRequest; + m_CurrentRequest = nullptr; + } + break; + } + + default: + { + // TODO: Should we be receiving data in this state? + break; + } + } +} + + + + + +void cHTTPServerConnection::OnRemoteClosed(void) +{ + if (m_CurrentRequest != nullptr) + { + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + } + m_Link.reset(); +} + + + + + + +void cHTTPServerConnection::OnError(int a_ErrorCode, const AString & a_ErrorMsg) +{ + OnRemoteClosed(); +} + + + + +void cHTTPServerConnection::SendData(const void * a_Data, size_t a_Size) +{ + m_Link->Send(a_Data, a_Size); +} + + + + diff --git a/src/HTTPServer/HTTPServerConnection.h b/src/HTTPServer/HTTPServerConnection.h new file mode 100644 index 000000000..d63cc7395 --- /dev/null +++ b/src/HTTPServer/HTTPServerConnection.h @@ -0,0 +1,124 @@ + +// HTTPConnection.h + +// Declares the cHTTPConnection class representing a single persistent connection in the HTTP server. + + + + + +#pragma once + +#include "../OSSupport/Network.h" + + + + + +// fwd: +class cHTTPServer; +class cHTTPResponse; +class cHTTPRequest; + + + + + +class cHTTPServerConnection : + public cTCPLink::cCallbacks +{ +public: + + enum eState + { + wcsRecvHeaders, ///< Receiving request headers (m_CurrentRequest is created if nullptr) + wcsRecvBody, ///< Receiving request body (m_CurrentRequest is valid) + wcsRecvIdle, ///< Has received the entire body, waiting to send the response (m_CurrentRequest == nullptr) + wcsSendingResp, ///< Sending response body (m_CurrentRequest == nullptr) + wcsInvalid, ///< The request was malformed, the connection is closing + } ; + + cHTTPServerConnection(cHTTPServer & a_HTTPServer); + virtual ~cHTTPServerConnection(); + + /** Sends HTTP status code together with a_Reason (used for HTTP errors). + Sends the a_Reason as the body as well, so that browsers display it. */ + void SendStatusAndReason(int a_StatusCode, const AString & a_Reason); + + /** Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm */ + void SendNeedAuth(const AString & a_Realm); + + /** Sends the headers contained in a_Response */ + void Send(const cHTTPResponse & a_Response); + + /** Sends the data as the response (may be called multiple times) */ + void Send(const void * a_Data, size_t a_Size); + + /** Sends the data as the response (may be called multiple times) */ + void Send(const AString & a_Data) { Send(a_Data.data(), a_Data.size()); } + + /** Indicates that the current response is finished, gets ready for receiving another request (HTTP 1.1 keepalive) */ + void FinishResponse(void); + + /** Resets the internal connection state for a new request. + Depending on the state, this will send an "InternalServerError" status or a "ResponseEnd" */ + void AwaitNextRequest(void); + + /** Terminates the connection; finishes any request being currently processed */ + void Terminate(void); + +protected: + typedef std::map cNameValueMap; + + /** The parent webserver that is to be notified of events on this connection */ + cHTTPServer & m_HTTPServer; + + /** All the incoming data until the entire request header is parsed */ + AString m_IncomingHeaderData; + + /** Status in which the request currently is */ + eState m_State; + + /** The request being currently received + Valid only between having parsed the headers and finishing receiving the body. */ + cHTTPRequest * m_CurrentRequest; + + /** Number of bytes that remain to read for the complete body of the message to be received. + Valid only in wcsRecvBody */ + size_t m_CurrentRequestBodyRemaining; + + /** The network link attached to this connection. */ + cTCPLinkPtr m_Link; + + + // cTCPLink::cCallbacks overrides: + /** The link instance has been created, remember it. */ + virtual void OnLinkCreated(cTCPLinkPtr a_Link) override; + + /** Data is received from the client. */ + virtual void OnReceivedData(const char * a_Data, size_t a_Size) override; + + /** The socket has been closed for any reason. */ + virtual void OnRemoteClosed(void) override; + + /** An error has occurred on the socket. */ + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; + + // Overridable: + /** Called to send raw data over the link. Descendants may provide data transformations (SSL etc.) */ + virtual void SendData(const void * a_Data, size_t a_Size); + + /** Sends the raw data over the link. + Descendants may provide data transformations (SSL etc.) via the overridable SendData() function. */ + void SendData(const AString & a_Data) + { + SendData(a_Data.data(), a_Data.size()); + } +} ; + +typedef std::vector cHTTPServerConnections; + + + + + diff --git a/src/HTTPServer/SslHTTPConnection.cpp b/src/HTTPServer/SslHTTPConnection.cpp deleted file mode 100644 index 1cbe02312..000000000 --- a/src/HTTPServer/SslHTTPConnection.cpp +++ /dev/null @@ -1,115 +0,0 @@ - -// SslHTTPConnection.cpp - -// Implements the cSslHTTPConnection class representing a HTTP connection made over a SSL link - -#include "Globals.h" -#include "SslHTTPConnection.h" -#include "HTTPServer.h" - - - - - -cSslHTTPConnection::cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cCryptoKeyPtr & a_PrivateKey) : - super(a_HTTPServer), - m_Ssl(64000), - m_Cert(a_Cert), - m_PrivateKey(a_PrivateKey) -{ - m_Ssl.Initialize(false); - m_Ssl.SetOwnCert(a_Cert, a_PrivateKey); -} - - - - - -cSslHTTPConnection::~cSslHTTPConnection() -{ - m_Ssl.NotifyClose(); -} - - - - - -void cSslHTTPConnection::OnReceivedData(const char * a_Data, size_t a_Size) -{ - // Process the received data: - const char * Data = a_Data; - size_t Size = a_Size; - for (;;) - { - // Try to write as many bytes into Ssl's "incoming" buffer as possible: - size_t BytesWritten = 0; - if (Size > 0) - { - BytesWritten = m_Ssl.WriteIncoming(Data, Size); - Data += BytesWritten; - Size -= BytesWritten; - } - - // Try to read as many bytes from SSL's decryption as possible: - char Buffer[32000]; - int NumRead = m_Ssl.ReadPlain(Buffer, sizeof(Buffer)); - if (NumRead > 0) - { - super::OnReceivedData(Buffer, static_cast(NumRead)); - // The link may have closed while processing the data, bail out: - return; - } - else if (NumRead == POLARSSL_ERR_NET_WANT_READ) - { - // SSL requires us to send data to peer first, do so by "sending" empty data: - SendData(nullptr, 0); - } - - // If both failed, bail out: - if ((BytesWritten == 0) && (NumRead <= 0)) - { - return; - } - } -} - - - - - -void cSslHTTPConnection::SendData(const void * a_Data, size_t a_Size) -{ - const char * OutgoingData = reinterpret_cast(a_Data); - size_t pos = 0; - for (;;) - { - // Write as many bytes from our buffer to SSL's encryption as possible: - int NumWritten = 0; - if (pos < a_Size) - { - NumWritten = m_Ssl.WritePlain(OutgoingData + pos, a_Size - pos); - if (NumWritten > 0) - { - pos += static_cast(NumWritten); - } - } - - // Read as many bytes from SSL's "outgoing" buffer as possible: - char Buffer[32000]; - size_t NumBytes = m_Ssl.ReadOutgoing(Buffer, sizeof(Buffer)); - if (NumBytes > 0) - { - m_Link->Send(Buffer, NumBytes); - } - - // If both failed, bail out: - if ((NumWritten <= 0) && (NumBytes == 0)) - { - return; - } - } -} - - - - diff --git a/src/HTTPServer/SslHTTPConnection.h b/src/HTTPServer/SslHTTPConnection.h deleted file mode 100644 index b35bd8ba0..000000000 --- a/src/HTTPServer/SslHTTPConnection.h +++ /dev/null @@ -1,47 +0,0 @@ - -// SslHTTPConnection.h - -// Declared the cSslHTTPConnection class representing a HTTP connection made over a SSL link - - - - - -#pragma once - -#include "HTTPConnection.h" -#include "PolarSSL++/BufferedSslContext.h" - - - - - -class cSslHTTPConnection : - public cHTTPConnection -{ - typedef cHTTPConnection super; - -public: - /** Creates a new connection on the specified server. - Sends the specified cert as the server certificate, uses the private key for decryption. */ - cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cCryptoKeyPtr & a_PrivateKey); - - ~cSslHTTPConnection(); - -protected: - cBufferedSslContext m_Ssl; - - /** The certificate to send to the client */ - cX509CertPtr m_Cert; - - /** The private key used for the certificate */ - cCryptoKeyPtr m_PrivateKey; - - // cHTTPConnection overrides: - virtual void OnReceivedData(const char * a_Data, size_t a_Size) override; // Data is received from the client - virtual void SendData(const void * a_Data, size_t a_Size) override; // Data is to be sent to client -} ; - - - - diff --git a/src/HTTPServer/SslHTTPServerConnection.cpp b/src/HTTPServer/SslHTTPServerConnection.cpp new file mode 100644 index 000000000..547e6de3a --- /dev/null +++ b/src/HTTPServer/SslHTTPServerConnection.cpp @@ -0,0 +1,115 @@ + +// SslHTTPConnection.cpp + +// Implements the cSslHTTPServerConnection class representing a HTTP connection made over a SSL link + +#include "Globals.h" +#include "SslHTTPServerConnection.h" +#include "HTTPServer.h" + + + + + +cSslHTTPServerConnection::cSslHTTPServerConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cCryptoKeyPtr & a_PrivateKey) : + super(a_HTTPServer), + m_Ssl(64000), + m_Cert(a_Cert), + m_PrivateKey(a_PrivateKey) +{ + m_Ssl.Initialize(false); + m_Ssl.SetOwnCert(a_Cert, a_PrivateKey); +} + + + + + +cSslHTTPServerConnection::~cSslHTTPServerConnection() +{ + m_Ssl.NotifyClose(); +} + + + + + +void cSslHTTPServerConnection::OnReceivedData(const char * a_Data, size_t a_Size) +{ + // Process the received data: + const char * Data = a_Data; + size_t Size = a_Size; + for (;;) + { + // Try to write as many bytes into Ssl's "incoming" buffer as possible: + size_t BytesWritten = 0; + if (Size > 0) + { + BytesWritten = m_Ssl.WriteIncoming(Data, Size); + Data += BytesWritten; + Size -= BytesWritten; + } + + // Try to read as many bytes from SSL's decryption as possible: + char Buffer[32000]; + int NumRead = m_Ssl.ReadPlain(Buffer, sizeof(Buffer)); + if (NumRead > 0) + { + super::OnReceivedData(Buffer, static_cast(NumRead)); + // The link may have closed while processing the data, bail out: + return; + } + else if (NumRead == POLARSSL_ERR_NET_WANT_READ) + { + // SSL requires us to send data to peer first, do so by "sending" empty data: + SendData(nullptr, 0); + } + + // If both failed, bail out: + if ((BytesWritten == 0) && (NumRead <= 0)) + { + return; + } + } +} + + + + + +void cSslHTTPServerConnection::SendData(const void * a_Data, size_t a_Size) +{ + const char * OutgoingData = reinterpret_cast(a_Data); + size_t pos = 0; + for (;;) + { + // Write as many bytes from our buffer to SSL's encryption as possible: + int NumWritten = 0; + if (pos < a_Size) + { + NumWritten = m_Ssl.WritePlain(OutgoingData + pos, a_Size - pos); + if (NumWritten > 0) + { + pos += static_cast(NumWritten); + } + } + + // Read as many bytes from SSL's "outgoing" buffer as possible: + char Buffer[32000]; + size_t NumBytes = m_Ssl.ReadOutgoing(Buffer, sizeof(Buffer)); + if (NumBytes > 0) + { + m_Link->Send(Buffer, NumBytes); + } + + // If both failed, bail out: + if ((NumWritten <= 0) && (NumBytes == 0)) + { + return; + } + } +} + + + + diff --git a/src/HTTPServer/SslHTTPServerConnection.h b/src/HTTPServer/SslHTTPServerConnection.h new file mode 100644 index 000000000..6032a2bd0 --- /dev/null +++ b/src/HTTPServer/SslHTTPServerConnection.h @@ -0,0 +1,47 @@ + +// SslHTTPServerConnection.h + +// Declared the cSslHTTPServerConnection class representing a HTTP connection made over a SSL link + + + + + +#pragma once + +#include "HTTPServerConnection.h" +#include "PolarSSL++/BufferedSslContext.h" + + + + + +class cSslHTTPServerConnection : + public cHTTPServerConnection +{ + typedef cHTTPServerConnection super; + +public: + /** Creates a new connection on the specified server. + Sends the specified cert as the server certificate, uses the private key for decryption. */ + cSslHTTPServerConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cCryptoKeyPtr & a_PrivateKey); + + ~cSslHTTPServerConnection(); + +protected: + cBufferedSslContext m_Ssl; + + /** The certificate to send to the client */ + cX509CertPtr m_Cert; + + /** The private key used for the certificate */ + cCryptoKeyPtr m_PrivateKey; + + // cHTTPConnection overrides: + virtual void OnReceivedData(const char * a_Data, size_t a_Size) override; // Data is received from the client + virtual void SendData(const void * a_Data, size_t a_Size) override; // Data is to be sent to client +} ; + + + + diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp index 29e1bbccf..73ee234d3 100644 --- a/src/WebAdmin.cpp +++ b/src/WebAdmin.cpp @@ -13,7 +13,7 @@ #include "Root.h" #include "HTTPServer/HTTPMessage.h" -#include "HTTPServer/HTTPConnection.h" +#include "HTTPServer/HTTPServerConnection.h" @@ -212,7 +212,7 @@ bool cWebAdmin::LoadLoginTemplate(void) -void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request) { if (!a_Request.HasAuth()) { @@ -349,7 +349,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque -void cWebAdmin::HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +void cWebAdmin::HandleRootRequest(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request) { UNUSED(a_Request); @@ -364,7 +364,7 @@ void cWebAdmin::HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & -void cWebAdmin::HandleFileRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request) { AString FileURL = a_Request.GetURL(); std::replace(FileURL.begin(), FileURL.end(), '\\', '/'); @@ -621,7 +621,7 @@ AString cWebAdmin::GetBaseURL(const AStringVector & a_URLSplit) -void cWebAdmin::OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +void cWebAdmin::OnRequestBegun(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request) { UNUSED(a_Connection); const AString & URL = a_Request.GetURL(); @@ -645,7 +645,7 @@ void cWebAdmin::OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_ -void cWebAdmin::OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) +void cWebAdmin::OnRequestBody(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) { UNUSED(a_Connection); cRequestData * Data = reinterpret_cast(a_Request.GetUserData()); @@ -660,7 +660,7 @@ void cWebAdmin::OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_R -void cWebAdmin::OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +void cWebAdmin::OnRequestFinished(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request) { const AString & URL = a_Request.GetURL(); if ( diff --git a/src/WebAdmin.h b/src/WebAdmin.h index a3ebac43c..e5f8fc615 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -236,18 +236,18 @@ protected: cHTTPServer m_HTTPServer; /** Handles requests coming to the "/webadmin" or "/~webadmin" URLs */ - void HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + void HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request); /** Handles requests for the root page */ - void HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + void HandleRootRequest(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request); /** Handles requests for a file */ - void HandleFileRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + void HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request); // cHTTPServer::cCallbacks overrides: - virtual void OnRequestBegun (cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override; - virtual void OnRequestBody (cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) override; - virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override; + virtual void OnRequestBegun (cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request) override; + virtual void OnRequestBody (cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) override; + virtual void OnRequestFinished(cHTTPServerConnection & a_Connection, cHTTPRequest & a_Request) override; } ; // tolua_export -- cgit v1.2.3