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/HTTPMessage.cpp | 198 +++++++++++++++----------------------- 1 file changed, 77 insertions(+), 121 deletions(-) (limited to 'source/HTTPServer/HTTPMessage.cpp') diff --git a/source/HTTPServer/HTTPMessage.cpp b/source/HTTPServer/HTTPMessage.cpp index 72c603295..ed5c87e84 100644 --- a/source/HTTPServer/HTTPMessage.cpp +++ b/source/HTTPServer/HTTPMessage.cpp @@ -10,11 +10,22 @@ +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cHTTPMessage: cHTTPMessage::cHTTPMessage(eKind a_Kind) : - m_Kind(a_Kind) + m_Kind(a_Kind), + m_ContentLength(-1) { } @@ -24,10 +35,12 @@ cHTTPMessage::cHTTPMessage(eKind a_Kind) : void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value) { - cNameValueMap::iterator itr = m_Headers.find(a_Key); + AString Key = a_Key; + StrToLower(Key); + cNameValueMap::iterator itr = m_Headers.find(Key); if (itr == m_Headers.end()) { - m_Headers[a_Key] = a_Value; + m_Headers[Key] = a_Value; } else { @@ -37,13 +50,13 @@ void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value) } // Special processing for well-known headers: - if (a_Key == "Content-Type") + if (Key == "content-type") { - m_ContentType = m_Headers["Content-Type"]; + m_ContentType = m_Headers[Key]; } - else if (a_Key == "Content-Length") + else if (Key == "content-length") { - m_ContentLength = atoi(m_Headers["Content-Length"].c_str()); + m_ContentLength = atoi(m_Headers[Key].c_str()); } } @@ -56,6 +69,8 @@ void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value) cHTTPRequest::cHTTPRequest(void) : super(mkRequest), + m_EnvelopeParser(*this), + m_IsValid(true), m_UserData(NULL) { } @@ -64,66 +79,75 @@ cHTTPRequest::cHTTPRequest(void) : -bool cHTTPRequest::ParseHeaders(const char * a_IncomingData, size_t a_IdxEnd) +int cHTTPRequest::ParseHeaders(const char * a_Data, int a_Size) { - // The first line contains the method and the URL: - size_t Next = ParseRequestLine(a_IncomingData, a_IdxEnd); - if (Next == AString::npos) + if (!m_IsValid) { - return false; + return -1; } - // The following lines contain headers: - AString Key; - const char * Data = a_IncomingData + Next; - size_t End = a_IdxEnd - Next; - while (End > 0) + if (m_Method.empty()) { - Next = ParseHeaderField(Data, End, Key); - if (Next == AString::npos) + // The first line hasn't been processed yet + int res = ParseRequestLine(a_Data, a_Size); + if ((res < 0) || (res == a_Size)) + { + return res; + } + int res2 = m_EnvelopeParser.Parse(a_Data + res, a_Size - res); + if (res2 < 0) { - return false; + m_IsValid = false; + return res2; } - ASSERT(End >= Next); - Data += Next; - End -= Next; + return res2 + res; } - if (!HasReceivedContentLength()) + if (m_EnvelopeParser.IsInHeaders()) { - SetContentLength(0); + int res = m_EnvelopeParser.Parse(a_Data, a_Size); + if (res < 0) + { + m_IsValid = false; + } + return res; } - return true; + return 0; } -size_t cHTTPRequest::ParseRequestLine(const char * a_Data, size_t a_IdxEnd) +int cHTTPRequest::ParseRequestLine(const char * a_Data, int a_Size) { + m_IncomingHeaderData.append(a_Data, a_Size); + size_t IdxEnd = m_IncomingHeaderData.size(); + // Ignore the initial CRLFs (HTTP spec's "should") size_t LineStart = 0; while ( - (LineStart < a_IdxEnd) && + (LineStart < IdxEnd) && ( - (a_Data[LineStart] == '\r') || - (a_Data[LineStart] == '\n') + (m_IncomingHeaderData[LineStart] == '\r') || + (m_IncomingHeaderData[LineStart] == '\n') ) ) { LineStart++; } - if (LineStart >= a_IdxEnd) + if (LineStart >= IdxEnd) { - return AString::npos; + m_IsValid = false; + return -1; } - size_t Last = LineStart; int NumSpaces = 0; - for (size_t i = LineStart; i < a_IdxEnd; i++) + size_t MethodEnd = 0; + size_t URLEnd = 0; + for (size_t i = LineStart; i < IdxEnd; i++) { - switch (a_Data[i]) + switch (m_IncomingHeaderData[i]) { case ' ': { @@ -131,124 +155,56 @@ size_t cHTTPRequest::ParseRequestLine(const char * a_Data, size_t a_IdxEnd) { case 0: { - m_Method.assign(a_Data, Last, i - Last); + MethodEnd = i; break; } case 1: { - m_URL.assign(a_Data, Last, i - Last); + URLEnd = i; break; } default: { // Too many spaces in the request - return AString::npos; + m_IsValid = false; + return -1; } } - Last = i + 1; NumSpaces += 1; break; } case '\n': { - if ((i == 0) || (a_Data[i - 1] != '\r') || (NumSpaces != 2) || (i < Last + 7)) + if ((i == 0) || (m_IncomingHeaderData[i - 1] != '\r') || (NumSpaces != 2) || (i < URLEnd + 7)) { // LF too early, without a CR, without two preceeding spaces or too soon after the second space - return AString::npos; + m_IsValid = false; + return -1; } // Check that there's HTTP/version at the end - if (strncmp(a_Data + Last, "HTTP/1.", 7) != 0) - { - return AString::npos; - } - return i + 1; - } - } // switch (a_Data[i]) - } // for i - a_Data[] - return AString::npos; -} - - - - - -size_t cHTTPRequest::ParseHeaderField(const char * a_Data, size_t a_IdxEnd, AString & a_Key) -{ - if (*a_Data <= ' ') - { - size_t res = ParseHeaderFieldContinuation(a_Data + 1, a_IdxEnd - 1, a_Key); - return (res == AString::npos) ? res : (res + 1); - } - size_t ValueIdx = 0; - AString Key; - for (size_t i = 0; i < a_IdxEnd; i++) - { - switch (a_Data[i]) - { - case '\n': - { - if ((ValueIdx == 0) || (i < ValueIdx - 2) || (i == 0) || (a_Data[i - 1] != '\r')) + if (strncmp(a_Data + URLEnd + 1, "HTTP/1.", 7) != 0) { - // Invalid header field - no colon or no CR before LF - return AString::npos; + m_IsValid = false; + return -1; } - AString Value(a_Data, ValueIdx + 1, i - ValueIdx - 2); - AddHeader(Key, Value); - a_Key = Key; + m_Method = m_IncomingHeaderData.substr(LineStart, MethodEnd - LineStart); + m_URL = m_IncomingHeaderData.substr(MethodEnd + 1, URLEnd - MethodEnd - 1); return i + 1; } - case ':': - { - if (ValueIdx == 0) - { - Key.assign(a_Data, 0, i); - ValueIdx = i; - } - break; - } - case ' ': - case '\t': - { - if (ValueIdx == i - 1) - { - // Value has started in this char, but it is whitespace, so move the start one char further - ValueIdx = i; - } - } - } // switch (char) + } // switch (m_IncomingHeaderData[i]) } // for i - m_IncomingHeaderData[] - // No header found, return the end-of-data index: - return a_IdxEnd; + + // CRLF hasn't been encountered yet, consider all data consumed + return a_Size; } -size_t cHTTPRequest::ParseHeaderFieldContinuation(const char * a_Data, size_t a_IdxEnd, AString & a_Key) +void cHTTPRequest::OnHeaderLine(const AString & a_Key, const AString & a_Value) { - size_t Start = 0; - for (size_t i = 0; i < a_IdxEnd; i++) - { - if ((a_Data[i] > ' ') && (Start == 0)) - { - Start = i; - } - else if (a_Data[i] == '\n') - { - if ((i == 0) || (a_Data[i - 1] != '\r')) - { - // There wasn't a CR before this LF - return AString::npos; - } - AString Value(a_Data, 0, i - Start - 1); - AddHeader(a_Key, Value); - return i + 1; - } - } - // LF not found, how? We found it at the header end (CRLFCRLF) - ASSERT(!"LF not found, wtf?"); - return AString::npos; + AddHeader(a_Key, a_Value); } -- cgit v1.2.3