From fea556ca1b8aeec975f5276d5d829ee6275841d9 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 3 Jan 2016 15:59:55 +0100 Subject: Renamed HTTPServer folder to HTTP. It contains client code as well. --- src/HTTP/HTTPServerConnection.cpp | 278 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 src/HTTP/HTTPServerConnection.cpp (limited to 'src/HTTP/HTTPServerConnection.cpp') diff --git a/src/HTTP/HTTPServerConnection.cpp b/src/HTTP/HTTPServerConnection.cpp new file mode 100644 index 000000000..0ee9f3ce7 --- /dev/null +++ b/src/HTTP/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 "HTTPRequestParser.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 cHTTPRequestParser; + } + + 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); +} + + + + -- cgit v1.2.3 From 52d18b4559cbaca949f722aa6901a6eb5f505f02 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 20 Feb 2016 11:50:52 +0100 Subject: WebAdmin uses the new HTTP parser framework. --- src/HTTP/HTTPServerConnection.cpp | 224 ++++++++++++++++---------------------- 1 file changed, 93 insertions(+), 131 deletions(-) (limited to 'src/HTTP/HTTPServerConnection.cpp') diff --git a/src/HTTP/HTTPServerConnection.cpp b/src/HTTP/HTTPServerConnection.cpp index 0ee9f3ce7..0439c8b93 100644 --- a/src/HTTP/HTTPServerConnection.cpp +++ b/src/HTTP/HTTPServerConnection.cpp @@ -5,7 +5,8 @@ #include "Globals.h" #include "HTTPServerConnection.h" -#include "HTTPRequestParser.h" +#include "HTTPMessage.h" +#include "HTTPMessageParser.h" #include "HTTPServer.h" @@ -14,9 +15,8 @@ cHTTPServerConnection::cHTTPServerConnection(cHTTPServer & a_HTTPServer) : m_HTTPServer(a_HTTPServer), - m_State(wcsRecvHeaders), - m_CurrentRequest(nullptr), - m_CurrentRequestBodyRemaining(0) + m_Parser(*this), + m_CurrentRequest(nullptr) { // LOGD("HTTP: New connection at %p", this); } @@ -28,8 +28,7 @@ cHTTPServerConnection::cHTTPServerConnection(cHTTPServer & a_HTTPServer) : cHTTPServerConnection::~cHTTPServerConnection() { // LOGD("HTTP: Connection deleting: %p", this); - delete m_CurrentRequest; - m_CurrentRequest = nullptr; + m_CurrentRequest.reset(); } @@ -41,7 +40,8 @@ void cHTTPServerConnection::SendStatusAndReason(int a_StatusCode, const AString 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; + m_CurrentRequest.reset(); + m_Parser.Reset(); } @@ -51,7 +51,8 @@ void cHTTPServerConnection::SendStatusAndReason(int a_StatusCode, const AString 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; + m_CurrentRequest.reset(); + m_Parser.Reset(); } @@ -60,10 +61,9 @@ void cHTTPServerConnection::SendNeedAuth(const AString & a_Realm) void cHTTPServerConnection::Send(const cHTTPResponse & a_Response) { - ASSERT(m_State == wcsRecvIdle); + ASSERT(m_CurrentRequest != nullptr); AString toSend; a_Response.AppendToData(toSend); - m_State = wcsSendingResp; SendData(toSend); } @@ -73,7 +73,7 @@ void cHTTPServerConnection::Send(const cHTTPResponse & a_Response) void cHTTPServerConnection::Send(const void * a_Data, size_t a_Size) { - ASSERT(m_State == wcsSendingResp); + ASSERT(m_CurrentRequest != nullptr); // We're sending in Chunked transfer encoding SendData(Printf(SIZE_T_FMT_HEX "\r\n", a_Size)); SendData(a_Data, a_Size); @@ -86,47 +86,10 @@ void cHTTPServerConnection::Send(const void * a_Data, size_t a_Size) void cHTTPServerConnection::FinishResponse(void) { - ASSERT(m_State == wcsSendingResp); + ASSERT(m_CurrentRequest != nullptr); 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; - } - } + m_CurrentRequest.reset(); + m_Parser.Reset(); } @@ -160,86 +123,7 @@ 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 cHTTPRequestParser; - } - - 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; - } - } + m_Parser.Parse(a_Data, a_Size); } @@ -268,6 +152,84 @@ void cHTTPServerConnection::OnError(int a_ErrorCode, const AString & a_ErrorMsg) + +void cHTTPServerConnection::OnError(const AString & a_ErrorDescription) +{ + OnRemoteClosed(); +} + + + + + +void cHTTPServerConnection::OnFirstLine(const AString & a_FirstLine) +{ + // Create a new request object for this request: + auto split = StringSplit(a_FirstLine, " "); + if (split.size() < 2) + { + // Invalid request line. We need at least the Method and URL + OnRemoteClosed(); + return; + } + m_CurrentRequest.reset(new cHTTPIncomingRequest(split[0], split[1])); +} + + + + + +void cHTTPServerConnection::OnHeaderLine(const AString & a_Key, const AString & a_Value) +{ + if (m_CurrentRequest == nullptr) + { + return; + } + m_CurrentRequest->AddHeader(a_Key, a_Value); +} + + + + + +void cHTTPServerConnection::OnHeadersFinished(void) +{ + if (m_CurrentRequest == nullptr) + { + return; + } + m_HTTPServer.NewRequest(*this, *m_CurrentRequest); +} + + + + + +void cHTTPServerConnection::OnBodyData(const void * a_Data, size_t a_Size) +{ + if (m_CurrentRequest == nullptr) + { + return; + } + m_HTTPServer.RequestBody(*this, *m_CurrentRequest, a_Data, a_Size); +} + + + + + +void cHTTPServerConnection::OnBodyFinished(void) +{ + // Process the request and reset: + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + m_CurrentRequest.reset(); + m_Parser.Reset(); +} + + + + + void cHTTPServerConnection::SendData(const void * a_Data, size_t a_Size) { m_Link->Send(a_Data, a_Size); -- cgit v1.2.3 From 71a1fa81f00cf897d91e8f76cc7e646dd61cc2ff Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 20 Feb 2016 12:33:27 +0100 Subject: Renamed HTTPResponse to HTTPOutgoingResponse. --- src/HTTP/HTTPServerConnection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/HTTP/HTTPServerConnection.cpp') diff --git a/src/HTTP/HTTPServerConnection.cpp b/src/HTTP/HTTPServerConnection.cpp index 0439c8b93..9edec2886 100644 --- a/src/HTTP/HTTPServerConnection.cpp +++ b/src/HTTP/HTTPServerConnection.cpp @@ -59,7 +59,7 @@ void cHTTPServerConnection::SendNeedAuth(const AString & a_Realm) -void cHTTPServerConnection::Send(const cHTTPResponse & a_Response) +void cHTTPServerConnection::Send(const cHTTPOutgoingResponse & a_Response) { ASSERT(m_CurrentRequest != nullptr); AString toSend; -- cgit v1.2.3