diff options
Diffstat (limited to '')
-rw-r--r-- | src/HTTPServer/HTTPConnection.cpp | 86 | ||||
-rw-r--r-- | src/HTTPServer/HTTPConnection.h | 39 | ||||
-rw-r--r-- | src/HTTPServer/HTTPServer.cpp | 134 | ||||
-rw-r--r-- | src/HTTPServer/HTTPServer.h | 52 | ||||
-rw-r--r-- | src/HTTPServer/SslHTTPConnection.cpp | 33 | ||||
-rw-r--r-- | src/HTTPServer/SslHTTPConnection.h | 4 |
6 files changed, 182 insertions, 166 deletions
diff --git a/src/HTTPServer/HTTPConnection.cpp b/src/HTTPServer/HTTPConnection.cpp index d5dbf0199..de12b36ce 100644 --- a/src/HTTPServer/HTTPConnection.cpp +++ b/src/HTTPServer/HTTPConnection.cpp @@ -38,8 +38,7 @@ cHTTPConnection::~cHTTPConnection() void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response) { - AppendPrintf(m_OutgoingData, "%d %s\r\nContent-Length: 0\r\n\r\n", a_StatusCode, a_Response.c_str()); - m_HTTPServer.NotifyConnectionWrite(*this); + SendData(Printf("%d %s\r\nContent-Length: 0\r\n\r\n", a_StatusCode, a_Response.c_str())); m_State = wcsRecvHeaders; } @@ -49,8 +48,7 @@ void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Re void cHTTPConnection::SendNeedAuth(const AString & a_Realm) { - AppendPrintf(m_OutgoingData, "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Length: 0\r\n\r\n", a_Realm.c_str()); - m_HTTPServer.NotifyConnectionWrite(*this); + 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; } @@ -61,9 +59,10 @@ void cHTTPConnection::SendNeedAuth(const AString & a_Realm) void cHTTPConnection::Send(const cHTTPResponse & a_Response) { ASSERT(m_State == wcsRecvIdle); - a_Response.AppendToData(m_OutgoingData); + AString toSend; + a_Response.AppendToData(toSend); m_State = wcsSendingResp; - m_HTTPServer.NotifyConnectionWrite(*this); + SendData(toSend); } @@ -73,10 +72,10 @@ void cHTTPConnection::Send(const cHTTPResponse & a_Response) void cHTTPConnection::Send(const void * a_Data, size_t a_Size) { ASSERT(m_State == wcsSendingResp); - AppendPrintf(m_OutgoingData, SIZE_T_FMT_HEX "\r\n", a_Size); - m_OutgoingData.append((const char *)a_Data, a_Size); - m_OutgoingData.append("\r\n"); - m_HTTPServer.NotifyConnectionWrite(*this); + // 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"); } @@ -86,9 +85,8 @@ void cHTTPConnection::Send(const void * a_Data, size_t a_Size) void cHTTPConnection::FinishResponse(void) { ASSERT(m_State == wcsSendingResp); - m_OutgoingData.append("0\r\n\r\n"); + SendData("0\r\n\r\n"); m_State = wcsRecvHeaders; - m_HTTPServer.NotifyConnectionWrite(*this); } @@ -108,8 +106,7 @@ void cHTTPConnection::AwaitNextRequest(void) 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); + SendData("HTTP/1.1 500 Internal Server Error\r\n\r\n"); m_State = wcsRecvHeaders; break; } @@ -117,7 +114,7 @@ void cHTTPConnection::AwaitNextRequest(void) case wcsSendingResp: { // The response headers have been sent, we need to terminate the response body: - m_OutgoingData.append("0\r\n\r\n"); + SendData("0\r\n\r\n"); m_State = wcsRecvHeaders; break; } @@ -140,15 +137,27 @@ void cHTTPConnection::Terminate(void) { m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); } - m_HTTPServer.CloseConnection(*this); + m_Link.reset(); } -bool cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) +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: @@ -164,13 +173,14 @@ bool cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) delete m_CurrentRequest; m_CurrentRequest = nullptr; m_State = wcsInvalid; - m_HTTPServer.CloseConnection(*this); - return true; + m_Link->Close(); + m_Link.reset(); + return; } if (m_CurrentRequest->IsInHeaders()) { // The request headers are not yet complete - return false; + return; } // The request has finished parsing its headers successfully, notify of it: @@ -186,11 +196,13 @@ bool cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) // Process the rest of the incoming data into the request body: if (a_Size > BytesConsumed) { - return cHTTPConnection::DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed); + cHTTPConnection::OnReceivedData(a_Data + BytesConsumed, a_Size - BytesConsumed); + return; } else { - return cHTTPConnection::DataReceived("", 0); // If the request has zero body length, let it be processed right-away + cHTTPConnection::OnReceivedData("", 0); // If the request has zero body length, let it be processed right-away + return; } } @@ -210,8 +222,9 @@ bool cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) if (!m_CurrentRequest->DoesAllowKeepAlive()) { m_State = wcsInvalid; - m_HTTPServer.CloseConnection(*this); - return true; + m_Link->Close(); + m_Link.reset(); + return; } delete m_CurrentRequest; m_CurrentRequest = nullptr; @@ -225,32 +238,39 @@ bool cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) break; } } - return false; } -void cHTTPConnection::GetOutgoingData(AString & a_Data) +void cHTTPConnection::OnRemoteClosed(void) { - std::swap(a_Data, m_OutgoingData); + if (m_CurrentRequest != nullptr) + { + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + } + m_Link.reset(); } -void cHTTPConnection::SocketClosed(void) + +void cHTTPConnection::OnError(int a_ErrorCode, const AString & a_ErrorMsg) { - if (m_CurrentRequest != nullptr) - { - m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); - } - m_HTTPServer.CloseConnection(*this); + 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 index ccbf26466..8ecc4a4d4 100644 --- a/src/HTTPServer/HTTPConnection.h +++ b/src/HTTPServer/HTTPConnection.h @@ -9,7 +9,7 @@ #pragma once -#include "../OSSupport/SocketThreads.h" +#include "../OSSupport/Network.h" @@ -25,7 +25,7 @@ class cHTTPRequest; class cHTTPConnection : - public cSocketThreads::cCallback + public cTCPLink::cCallbacks { public: @@ -78,9 +78,6 @@ protected: /** Status in which the request currently is */ eState m_State; - /** Data that is queued for sending, once the socket becomes writable */ - AString m_OutgoingData; - /** The request being currently received Valid only between having parsed the headers and finishing receiving the body. */ cHTTPRequest * m_CurrentRequest; @@ -88,18 +85,34 @@ protected: /** 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; - // cSocketThreads::cCallback overrides: - /** Data is received from the client. - Returns true if the connection has been closed as the result of parsing the data. */ - virtual bool DataReceived(const char * a_Data, size_t a_Size) override; + // 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; - /** Data can be sent to client */ - virtual void GetOutgoingData(AString & a_Data) override; + /** The socket has been closed for any reason. */ + virtual void OnRemoteClosed(void) override; - /** The socket has been closed for any reason */ - virtual void SocketClosed(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<cHTTPConnection *> cHTTPConnections; diff --git a/src/HTTPServer/HTTPServer.cpp b/src/HTTPServer/HTTPServer.cpp index 9ab030a1f..3bcf0783a 100644 --- a/src/HTTPServer/HTTPServer.cpp +++ b/src/HTTPServer/HTTPServer.cpp @@ -119,11 +119,45 @@ class cDebugCallbacks : //////////////////////////////////////////////////////////////////////////////// +// cHTTPServerListenCallbacks: + +class cHTTPServerListenCallbacks: + public cNetwork::cListenCallbacks +{ +public: + cHTTPServerListenCallbacks(cHTTPServer & a_HTTPServer, UInt16 a_Port): + m_HTTPServer(a_HTTPServer), + m_Port(a_Port) + { + } + +protected: + /** The HTTP server instance that we're attached to. */ + cHTTPServer & m_HTTPServer; + + /** The port for which this instance is responsible. */ + UInt16 m_Port; + + // cNetwork::cListenCallbacks overrides: + virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) override + { + return m_HTTPServer.OnIncomingConnection(a_RemoteIPAddress, a_RemotePort); + } + virtual void OnAccepted(cTCPLink & a_Link) override {} + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override + { + LOGWARNING("HTTP server error on port %d: %d (%s)", m_Port, a_ErrorCode, a_ErrorMsg.c_str()); + } +}; + + + + + +//////////////////////////////////////////////////////////////////////////////// // cHTTPServer: cHTTPServer::cHTTPServer(void) : - m_ListenThreadIPv4(*this, cSocket::IPv4, "WebServer"), - m_ListenThreadIPv6(*this, cSocket::IPv6, "WebServer"), m_Callbacks(nullptr) { } @@ -141,7 +175,7 @@ cHTTPServer::~cHTTPServer() -bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6) +bool cHTTPServer::Initialize(void) { // Read the HTTPS cert + key: AString CertFile = cFile::ReadWholeFile("webadmin/httpscert.crt"); @@ -177,18 +211,6 @@ bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_Port { LOGINFO("WebServer: The server is running in secure HTTPS mode."); } - - // Open up requested ports: - bool HasAnyPort; - m_ListenThreadIPv4.SetReuseAddr(true); - m_ListenThreadIPv6.SetReuseAddr(true); - HasAnyPort = m_ListenThreadIPv4.Initialize(a_PortsIPv4); - HasAnyPort = m_ListenThreadIPv6.Initialize(a_PortsIPv6) || HasAnyPort; - if (!HasAnyPort) - { - return false; - } - return true; } @@ -196,19 +218,28 @@ bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_Port -bool cHTTPServer::Start(cCallbacks & a_Callbacks) +bool cHTTPServer::Start(cCallbacks & a_Callbacks, const AStringVector & a_Ports) { m_Callbacks = &a_Callbacks; - if (!m_ListenThreadIPv4.Start()) - { - return false; - } - if (!m_ListenThreadIPv6.Start()) + + // Open up requested ports: + for (auto port : a_Ports) { - m_ListenThreadIPv4.Stop(); - return false; - } - return true; + UInt16 PortNum = static_cast<UInt16>(atoi(port.c_str())); + if (PortNum == 0) + { + LOGWARNING("WebServer: Invalid port value: \"%s\". Ignoring.", port.c_str()); + continue; + } + auto Handle = cNetwork::Listen(PortNum, std::make_shared<cHTTPServerListenCallbacks>(*this, PortNum)); + if (Handle->IsListening()) + { + m_ServerHandles.push_back(Handle); + } + } // for port - a_Ports[] + + // Report success if at least one port opened successfully: + return !m_ServerHandles.empty(); } @@ -217,63 +248,30 @@ bool cHTTPServer::Start(cCallbacks & a_Callbacks) void cHTTPServer::Stop(void) { - m_ListenThreadIPv4.Stop(); - m_ListenThreadIPv6.Stop(); - - // Drop all current connections: - cCSLock Lock(m_CSConnections); - while (!m_Connections.empty()) + for (auto handle : m_ServerHandles) { - m_Connections.front()->Terminate(); - } // for itr - m_Connections[] + handle->Close(); + } + m_ServerHandles.clear(); } -void cHTTPServer::OnConnectionAccepted(cSocket & a_Socket) +cTCPLink::cCallbacksPtr cHTTPServer::OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) { - cHTTPConnection * Connection; + UNUSED(a_RemoteIPAddress); + UNUSED(a_RemotePort); + if (m_Cert.get() != nullptr) { - Connection = new cSslHTTPConnection(*this, m_Cert, m_CertPrivKey); + return std::make_shared<cSslHTTPConnection>(*this, m_Cert, m_CertPrivKey); } else { - Connection = new cHTTPConnection(*this); + return std::make_shared<cHTTPConnection>(*this); } - m_SocketThreads.AddClient(a_Socket, Connection); - cCSLock Lock(m_CSConnections); - m_Connections.push_back(Connection); -} - - - - - -void cHTTPServer::CloseConnection(cHTTPConnection & a_Connection) -{ - m_SocketThreads.RemoveClient(&a_Connection); - cCSLock Lock(m_CSConnections); - for (cHTTPConnections::iterator itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) - { - if (*itr == &a_Connection) - { - m_Connections.erase(itr); - break; - } - } - delete &a_Connection; -} - - - - - -void cHTTPServer::NotifyConnectionWrite(cHTTPConnection & a_Connection) -{ - m_SocketThreads.NotifyWrite(&a_Connection); } diff --git a/src/HTTPServer/HTTPServer.h b/src/HTTPServer/HTTPServer.h index 73d4cbdd0..d626fb475 100644 --- a/src/HTTPServer/HTTPServer.h +++ b/src/HTTPServer/HTTPServer.h @@ -9,8 +9,7 @@ #pragma once -#include "../OSSupport/ListenThread.h" -#include "../OSSupport/SocketThreads.h" +#include "../OSSupport/Network.h" #include "../IniFile.h" #include "PolarSSL++/RsaPrivateKey.h" #include "PolarSSL++/CryptoKey.h" @@ -33,8 +32,7 @@ typedef std::vector<cHTTPConnection *> cHTTPConnections; -class cHTTPServer : - public cListenThread::cCallback +class cHTTPServer { public: class cCallbacks @@ -42,44 +40,39 @@ public: public: virtual ~cCallbacks() {} - /** Called when a new request arrives over a connection and its headers have been parsed. - The request body needn't have arrived yet. - */ + /** 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; /** 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; - /// Called when the request body has been fully received in previous calls to OnRequestBody() + /** 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); virtual ~cHTTPServer(); - /// Initializes the server on the specified ports - bool Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6); + /** Initializes the server - reads the cert files etc. */ + bool Initialize(void); - /// Starts the server and assigns the callbacks to use for incoming requests - bool Start(cCallbacks & a_Callbacks); + /** Starts the server and assigns the callbacks to use for incoming requests */ + bool Start(cCallbacks & a_Callbacks, const AStringVector & a_Ports); - /// Stops the server, drops all current connections + /** Stops the server, drops all current connections */ void Stop(void); protected: friend class cHTTPConnection; friend class cSslHTTPConnection; + friend class cHTTPServerListenCallbacks; - cListenThread m_ListenThreadIPv4; - cListenThread m_ListenThreadIPv6; + /** The cNetwork API handle for the listening socket. */ + cServerHandlePtrs m_ServerHandles; - cSocketThreads m_SocketThreads; - - cCriticalSection m_CSConnections; - cHTTPConnections m_Connections; ///< All the connections that are currently being serviced - - /// The callbacks to call for various events + /** The callbacks to call for various events */ cCallbacks * m_Callbacks; /** The server certificate to use for the SSL connections */ @@ -89,23 +82,18 @@ protected: cCryptoKeyPtr m_CertPrivKey; - // cListenThread::cCallback overrides: - virtual void OnConnectionAccepted(cSocket & a_Socket) override; - - /// Called by cHTTPConnection to close the connection (presumably due to an error) - void CloseConnection(cHTTPConnection & a_Connection); - - /// Called by cHTTPConnection to notify SocketThreads that there's data to be sent for the connection - void NotifyConnectionWrite(cHTTPConnection & a_Connection); - - /// Called by cHTTPConnection when it finishes parsing the request header + /** Called by cHTTPServerListenCallbacks when there's a new incoming connection. + 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 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); - /// Called by cHTTPConnection when it detects that the request has finished (all of its body has been received) + /** 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); } ; diff --git a/src/HTTPServer/SslHTTPConnection.cpp b/src/HTTPServer/SslHTTPConnection.cpp index d237089d9..f09daac8f 100644 --- a/src/HTTPServer/SslHTTPConnection.cpp +++ b/src/HTTPServer/SslHTTPConnection.cpp @@ -25,14 +25,8 @@ cSslHTTPConnection::cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509Ce -bool cSslHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) +void cSslHTTPConnection::OnReceivedData(const char * a_Data, size_t a_Size) { - // If there is outgoing data in the queue, notify the server that it should write it out: - if (!m_OutgoingData.empty()) - { - m_HTTPServer.NotifyConnectionWrite(*this); - } - // Process the received data: const char * Data = a_Data; size_t Size = a_Size; @@ -52,17 +46,18 @@ bool cSslHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) int NumRead = m_Ssl.ReadPlain(Buffer, sizeof(Buffer)); if (NumRead > 0) { - if (super::DataReceived(Buffer, (size_t)NumRead)) - { - // The socket has been closed, and the object is already deleted. Bail out. - return true; - } + super::OnReceivedData(Buffer, (size_t)NumRead); + } + 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 false; + return; } } } @@ -71,18 +66,20 @@ bool cSslHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) -void cSslHTTPConnection::GetOutgoingData(AString & a_Data) +void cSslHTTPConnection::SendData(const void * a_Data, size_t a_Size) { + const char * OutgoingData = reinterpret_cast<const char *>(a_Data); + size_t pos = 0; for (;;) { // Write as many bytes from our buffer to SSL's encryption as possible: int NumWritten = 0; - if (!m_OutgoingData.empty()) + if (pos < a_Size) { - NumWritten = m_Ssl.WritePlain(m_OutgoingData.data(), m_OutgoingData.size()); + NumWritten = m_Ssl.WritePlain(OutgoingData + pos, a_Size - pos); if (NumWritten > 0) { - m_OutgoingData.erase(0, (size_t)NumWritten); + pos += static_cast<size_t>(NumWritten); } } @@ -91,7 +88,7 @@ void cSslHTTPConnection::GetOutgoingData(AString & a_Data) size_t NumBytes = m_Ssl.ReadOutgoing(Buffer, sizeof(Buffer)); if (NumBytes > 0) { - a_Data.append(Buffer, NumBytes); + m_Link->Send(Buffer, NumBytes); } // If both failed, bail out: diff --git a/src/HTTPServer/SslHTTPConnection.h b/src/HTTPServer/SslHTTPConnection.h index c2c1585cd..dc54b1eff 100644 --- a/src/HTTPServer/SslHTTPConnection.h +++ b/src/HTTPServer/SslHTTPConnection.h @@ -36,8 +36,8 @@ protected: cCryptoKeyPtr m_PrivateKey; // cHTTPConnection overrides: - virtual bool DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client - virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client + 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 } ; |