From d0b9e817956a57389f17a3d8e00df51cbe8cc309 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Sep 2013 19:34:46 +0200 Subject: Split cHTTPConnection implementation into a separate file. --- source/HTTPServer/HTTPConnection.cpp | 147 +++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 source/HTTPServer/HTTPConnection.cpp (limited to 'source/HTTPServer/HTTPConnection.cpp') diff --git a/source/HTTPServer/HTTPConnection.cpp b/source/HTTPServer/HTTPConnection.cpp new file mode 100644 index 000000000..f7318c6ae --- /dev/null +++ b/source/HTTPServer/HTTPConnection.cpp @@ -0,0 +1,147 @@ + +// 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(NULL) +{ +} + + + + +void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response) +{ + AppendPrintf(m_OutgoingData, "%d %s\r\n\r\n", a_StatusCode, a_Response.c_str()); +} + + + + + +void cHTTPConnection::Send(const cHTTPResponse & a_Response) +{ + ASSERT(m_State = wcsRecvIdle); + a_Response.AppendToData(m_OutgoingData); + m_State = wcsSendingResp; +} + + + + + +void cHTTPConnection::Send(const void * a_Data, int a_Size) +{ + ASSERT(m_State == wcsSendingResp); + AppendPrintf(m_OutgoingData, "%x\r\n", a_Size); + m_OutgoingData.append((const char *)a_Data, a_Size); +} + + + + + +void cHTTPConnection::FinishResponse(void) +{ + ASSERT(m_State == wcsSendingResp); + m_OutgoingData.append("0\r\n"); + m_State = wcsRecvHeaders; +} + + + + + +void cHTTPConnection::DataReceived(const char * a_Data, int a_Size) +{ + switch (m_State) + { + case wcsRecvHeaders: + { + ASSERT(m_CurrentRequest == NULL); + + // Start searching 3 chars from the end of the already received data, if available: + size_t SearchStart = m_IncomingHeaderData.size(); + SearchStart = (SearchStart > 3) ? SearchStart - 3 : 0; + + m_IncomingHeaderData.append(a_Data, a_Size); + + // Parse the header, if it is complete: + size_t idxEnd = m_IncomingHeaderData.find("\r\n\r\n", SearchStart); + if (idxEnd == AString::npos) + { + return; + } + m_CurrentRequest = new cHTTPRequest; + if (!m_CurrentRequest->ParseHeaders(m_IncomingHeaderData.c_str(), idxEnd + 2)) + { + delete m_CurrentRequest; + m_CurrentRequest = NULL; + m_State = wcsInvalid; + m_HTTPServer.CloseConnection(*this); + return; + } + m_State = wcsRecvBody; + m_HTTPServer.NewRequest(*this, *m_CurrentRequest); + + // Process the rest of the incoming data into the request body: + if (m_IncomingHeaderData.size() > idxEnd + 4) + { + m_IncomingHeaderData.erase(0, idxEnd + 4); + DataReceived(m_IncomingHeaderData.c_str(), m_IncomingHeaderData.size()); + } + break; + } + + case wcsRecvBody: + { + ASSERT(m_CurrentRequest != NULL); + // TODO: Receive the body, and the next request (If HTTP/1.1 keepalive) + break; + } + + default: + { + // TODO: Should we be receiving data in this state? + break; + } + } +} + + + + + +void cHTTPConnection::GetOutgoingData(AString & a_Data) +{ + std::swap(a_Data, m_OutgoingData); +} + + + + + +void cHTTPConnection::SocketClosed(void) +{ + if (m_CurrentRequest != NULL) + { + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + } +} + + + + + -- cgit v1.2.3 From 0c3fd5e77d681c25757efaab6acb305d0b5630c1 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Sep 2013 20:33:18 +0200 Subject: Fixed parsing and implemented write nofitication. The web connection finally works with a browser. --- source/HTTPServer/HTTPConnection.cpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'source/HTTPServer/HTTPConnection.cpp') diff --git a/source/HTTPServer/HTTPConnection.cpp b/source/HTTPServer/HTTPConnection.cpp index f7318c6ae..59fe8f878 100644 --- a/source/HTTPServer/HTTPConnection.cpp +++ b/source/HTTPServer/HTTPConnection.cpp @@ -25,6 +25,7 @@ cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) : void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response) { AppendPrintf(m_OutgoingData, "%d %s\r\n\r\n", a_StatusCode, a_Response.c_str()); + m_HTTPServer.NotifyConnectionWrite(*this); } @@ -36,6 +37,7 @@ void cHTTPConnection::Send(const cHTTPResponse & a_Response) ASSERT(m_State = wcsRecvIdle); a_Response.AppendToData(m_OutgoingData); m_State = wcsSendingResp; + m_HTTPServer.NotifyConnectionWrite(*this); } @@ -47,6 +49,8 @@ void cHTTPConnection::Send(const void * a_Data, int a_Size) ASSERT(m_State == wcsSendingResp); AppendPrintf(m_OutgoingData, "%x\r\n", a_Size); m_OutgoingData.append((const char *)a_Data, a_Size); + m_OutgoingData.append("\r\n"); + m_HTTPServer.NotifyConnectionWrite(*this); } @@ -56,8 +60,9 @@ void cHTTPConnection::Send(const void * a_Data, int a_Size) void cHTTPConnection::FinishResponse(void) { ASSERT(m_State == wcsSendingResp); - m_OutgoingData.append("0\r\n"); + m_OutgoingData.append("0\r\n\r\n"); m_State = wcsRecvHeaders; + m_HTTPServer.NotifyConnectionWrite(*this); } @@ -95,12 +100,19 @@ void cHTTPConnection::DataReceived(const char * a_Data, int a_Size) } m_State = wcsRecvBody; m_HTTPServer.NewRequest(*this, *m_CurrentRequest); + m_CurrentRequestBodyRemaining = m_CurrentRequest->GetContentLength(); // Process the rest of the incoming data into the request body: if (m_IncomingHeaderData.size() > idxEnd + 4) { m_IncomingHeaderData.erase(0, idxEnd + 4); DataReceived(m_IncomingHeaderData.c_str(), m_IncomingHeaderData.size()); + m_IncomingHeaderData.clear(); + } + else + { + m_IncomingHeaderData.clear(); + DataReceived("", 0); // If the request has zero body length, let it be processed right-away } break; } @@ -108,7 +120,17 @@ void cHTTPConnection::DataReceived(const char * a_Data, int a_Size) case wcsRecvBody: { ASSERT(m_CurrentRequest != NULL); - // TODO: Receive the body, and the next request (If HTTP/1.1 keepalive) + if (m_CurrentRequestBodyRemaining > 0) + { + int BytesToConsume = std::min(m_CurrentRequestBodyRemaining, a_Size); + m_HTTPServer.RequestBody(*this, *m_CurrentRequest, a_Data, BytesToConsume); + m_CurrentRequestBodyRemaining -= BytesToConsume; + } + if (m_CurrentRequestBodyRemaining == 0) + { + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + m_State = wcsRecvIdle; + } break; } -- cgit v1.2.3 From 8c57c5c1f22e54884100e0de1378e4522665a79c Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Sep 2013 20:48:44 +0200 Subject: Fixed leaking HTTPRequest objects --- source/HTTPServer/HTTPConnection.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'source/HTTPServer/HTTPConnection.cpp') diff --git a/source/HTTPServer/HTTPConnection.cpp b/source/HTTPServer/HTTPConnection.cpp index 59fe8f878..2265d970f 100644 --- a/source/HTTPServer/HTTPConnection.cpp +++ b/source/HTTPServer/HTTPConnection.cpp @@ -129,6 +129,8 @@ void cHTTPConnection::DataReceived(const char * a_Data, int a_Size) if (m_CurrentRequestBodyRemaining == 0) { m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + delete m_CurrentRequest; + m_CurrentRequest = NULL; m_State = wcsRecvIdle; } break; -- cgit v1.2.3 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 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'source/HTTPServer/HTTPConnection.cpp') 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; } -- cgit v1.2.3 From 1012fd82fda9e9bc75d2308a3c68cb3b3738bf1b Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 4 Oct 2013 13:07:57 +0200 Subject: HTTP Server can now parse multipart/form-data forms; better architecture. --- source/HTTPServer/HTTPConnection.cpp | 38 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'source/HTTPServer/HTTPConnection.cpp') diff --git a/source/HTTPServer/HTTPConnection.cpp b/source/HTTPServer/HTTPConnection.cpp index c36b07d3d..61f4b3a31 100644 --- a/source/HTTPServer/HTTPConnection.cpp +++ b/source/HTTPServer/HTTPConnection.cpp @@ -108,22 +108,13 @@ void cHTTPConnection::DataReceived(const char * a_Data, int a_Size) { case wcsRecvHeaders: { - ASSERT(m_CurrentRequest == NULL); - - // Start searching 3 chars from the end of the already received data, if available: - size_t SearchStart = m_IncomingHeaderData.size(); - SearchStart = (SearchStart > 3) ? SearchStart - 3 : 0; - - m_IncomingHeaderData.append(a_Data, a_Size); - - // Parse the header, if it is complete: - size_t idxEnd = m_IncomingHeaderData.find("\r\n\r\n", SearchStart); - if (idxEnd == AString::npos) + if (m_CurrentRequest == NULL) { - return; + m_CurrentRequest = new cHTTPRequest; } - m_CurrentRequest = new cHTTPRequest; - if (!m_CurrentRequest->ParseHeaders(m_IncomingHeaderData.c_str(), idxEnd + 2)) + + int BytesConsumed = m_CurrentRequest->ParseHeaders(a_Data, a_Size); + if (BytesConsumed < 0) { delete m_CurrentRequest; m_CurrentRequest = NULL; @@ -131,20 +122,29 @@ void cHTTPConnection::DataReceived(const char * a_Data, int a_Size) m_HTTPServer.CloseConnection(*this); 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 < 0) + { + // 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 (m_IncomingHeaderData.size() > idxEnd + 4) + if (a_Size > BytesConsumed) { - m_IncomingHeaderData.erase(0, idxEnd + 4); - DataReceived(m_IncomingHeaderData.c_str(), m_IncomingHeaderData.size()); - m_IncomingHeaderData.clear(); + DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed); } else { - m_IncomingHeaderData.clear(); DataReceived("", 0); // If the request has zero body length, let it be processed right-away } break; -- cgit v1.2.3 From db3d83b38dd61b90466a0721fa9104e742f3fb8b Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 4 Oct 2013 20:28:30 +0200 Subject: Added Basic auth support to cHTTPRequest. --- source/HTTPServer/HTTPConnection.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'source/HTTPServer/HTTPConnection.cpp') diff --git a/source/HTTPServer/HTTPConnection.cpp b/source/HTTPServer/HTTPConnection.cpp index 61f4b3a31..e3a6be494 100644 --- a/source/HTTPServer/HTTPConnection.cpp +++ b/source/HTTPServer/HTTPConnection.cpp @@ -26,6 +26,18 @@ void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Re { AppendPrintf(m_OutgoingData, "%d %s\r\n\r\n", a_StatusCode, a_Response.c_str()); m_HTTPServer.NotifyConnectionWrite(*this); + m_State = wcsRecvHeaders; +} + + + + + +void cHTTPConnection::SendNeedAuth(const AString & a_Realm) +{ + AppendPrintf(m_OutgoingData, "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"%s\"\r\n\r\n", a_Realm.c_str()); + m_HTTPServer.NotifyConnectionWrite(*this); + m_State = wcsRecvHeaders; } @@ -73,6 +85,12 @@ 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": -- cgit v1.2.3 From 2b8bddbdc315abf109c9767025448d64cb5c8224 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sat, 5 Oct 2013 21:52:14 +0200 Subject: cHTTPConnection sends Content-Length with HTTP errors, too. --- source/HTTPServer/HTTPConnection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'source/HTTPServer/HTTPConnection.cpp') diff --git a/source/HTTPServer/HTTPConnection.cpp b/source/HTTPServer/HTTPConnection.cpp index e3a6be494..2addf4cfc 100644 --- a/source/HTTPServer/HTTPConnection.cpp +++ b/source/HTTPServer/HTTPConnection.cpp @@ -24,7 +24,7 @@ cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) : void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response) { - AppendPrintf(m_OutgoingData, "%d %s\r\n\r\n", a_StatusCode, a_Response.c_str()); + AppendPrintf(m_OutgoingData, "%d %s\r\nContent-Length: 0\r\n\r\n", a_StatusCode, a_Response.c_str()); m_HTTPServer.NotifyConnectionWrite(*this); m_State = wcsRecvHeaders; } @@ -35,7 +35,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\n\r\n", a_Realm.c_str()); + 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); m_State = wcsRecvHeaders; } -- cgit v1.2.3 From f55b77a98a41ba784109842cde39ba0e1d2bc262 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sun, 6 Oct 2013 16:40:28 +0200 Subject: Fixed memory leaks in the HTTP framework --- source/HTTPServer/HTTPConnection.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'source/HTTPServer/HTTPConnection.cpp') diff --git a/source/HTTPServer/HTTPConnection.cpp b/source/HTTPServer/HTTPConnection.cpp index 2addf4cfc..68afdfc11 100644 --- a/source/HTTPServer/HTTPConnection.cpp +++ b/source/HTTPServer/HTTPConnection.cpp @@ -17,11 +17,22 @@ cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) : m_State(wcsRecvHeaders), m_CurrentRequest(NULL) { + // LOGD("HTTP: New connection at %p", this); } + +cHTTPConnection::~cHTTPConnection() +{ + // LOGD("HTTP: Del connection at %p", this); +} + + + + + 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()); @@ -120,6 +131,19 @@ void cHTTPConnection::AwaitNextRequest(void) +void cHTTPConnection::Terminate(void) +{ + if (m_CurrentRequest != NULL) + { + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + } + m_HTTPServer.CloseConnection(*this); +} + + + + + void cHTTPConnection::DataReceived(const char * a_Data, int a_Size) { switch (m_State) @@ -214,6 +238,7 @@ void cHTTPConnection::SocketClosed(void) { m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); } + m_HTTPServer.CloseConnection(*this); } -- cgit v1.2.3