summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormadmaxoft <github@xoft.cz>2013-10-04 13:07:57 +0200
committermadmaxoft <github@xoft.cz>2013-10-04 13:13:34 +0200
commit1012fd82fda9e9bc75d2308a3c68cb3b3738bf1b (patch)
treec03e55ed3e05de4d771510d9249a4bbdef1bf26e
parentFixed MultiPartParser's boundary parsing. (diff)
downloadcuberite-1012fd82fda9e9bc75d2308a3c68cb3b3738bf1b.tar
cuberite-1012fd82fda9e9bc75d2308a3c68cb3b3738bf1b.tar.gz
cuberite-1012fd82fda9e9bc75d2308a3c68cb3b3738bf1b.tar.bz2
cuberite-1012fd82fda9e9bc75d2308a3c68cb3b3738bf1b.tar.lz
cuberite-1012fd82fda9e9bc75d2308a3c68cb3b3738bf1b.tar.xz
cuberite-1012fd82fda9e9bc75d2308a3c68cb3b3738bf1b.tar.zst
cuberite-1012fd82fda9e9bc75d2308a3c68cb3b3738bf1b.zip
Diffstat (limited to '')
-rw-r--r--VC2008/MCServer.vcproj24
-rw-r--r--source/HTTPServer/HTTPConnection.cpp38
-rw-r--r--source/HTTPServer/HTTPConnection.h2
-rw-r--r--source/HTTPServer/HTTPFormParser.cpp143
-rw-r--r--source/HTTPServer/HTTPFormParser.h59
-rw-r--r--source/HTTPServer/HTTPMessage.cpp198
-rw-r--r--source/HTTPServer/HTTPMessage.h41
-rw-r--r--source/HTTPServer/HTTPServer.cpp22
8 files changed, 342 insertions, 185 deletions
diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj
index 1e17cc722..6cc83b4db 100644
--- a/VC2008/MCServer.vcproj
+++ b/VC2008/MCServer.vcproj
@@ -2720,6 +2720,14 @@
Name="HTTPServer"
>
<File
+ RelativePath="..\source\HTTPServer\EnvelopeParser.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\HTTPServer\EnvelopeParser.h"
+ >
+ </File>
+ <File
RelativePath="..\source\HTTPServer\HTTPConnection.cpp"
>
</File>
@@ -2751,6 +2759,22 @@
RelativePath="..\source\HTTPServer\HTTPServer.h"
>
</File>
+ <File
+ RelativePath="..\source\HTTPServer\MultipartParser.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\HTTPServer\MultipartParser.h"
+ >
+ </File>
+ <File
+ RelativePath="..\source\HTTPServer\NameValueParser.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\HTTPServer\NameValueParser.h"
+ >
+ </File>
</Filter>
</Filter>
<Filter
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;
diff --git a/source/HTTPServer/HTTPConnection.h b/source/HTTPServer/HTTPConnection.h
index 46c36a8a2..9e05d342b 100644
--- a/source/HTTPServer/HTTPConnection.h
+++ b/source/HTTPServer/HTTPConnection.h
@@ -31,7 +31,7 @@ public:
enum eState
{
- wcsRecvHeaders, ///< Receiving request headers (m_CurrentRequest == NULL)
+ wcsRecvHeaders, ///< Receiving request headers (m_CurrentRequest is created if NULL)
wcsRecvBody, ///< Receiving request body (m_CurrentRequest is valid)
wcsRecvIdle, ///< Has received the entire body, waiting to send the response (m_CurrentRequest == NULL)
wcsSendingResp, ///< Sending response body (m_CurrentRequest == NULL)
diff --git a/source/HTTPServer/HTTPFormParser.cpp b/source/HTTPServer/HTTPFormParser.cpp
index 631424391..85a789f7d 100644
--- a/source/HTTPServer/HTTPFormParser.cpp
+++ b/source/HTTPServer/HTTPFormParser.cpp
@@ -6,19 +6,15 @@
#include "Globals.h"
#include "HTTPFormParser.h"
#include "HTTPMessage.h"
+#include "MultipartParser.h"
+#include "NameValueParser.h"
-AString cHTTPFormParser::m_FormURLEncoded("application/x-www-form-urlencoded");
-AString cHTTPFormParser::m_MultipartFormData("multipart/form-data");
-
-
-
-
-
-cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request) :
+cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks) :
+ m_Callbacks(a_Callbacks),
m_IsValid(true)
{
if (a_Request.GetMethod() == "GET")
@@ -36,14 +32,15 @@ cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request) :
}
if ((a_Request.GetMethod() == "POST") || (a_Request.GetMethod() == "PUT"))
{
- if (a_Request.GetContentType() == m_FormURLEncoded)
+ if (a_Request.GetContentType() == "application/x-www-form-urlencoded")
{
m_Kind = fpkFormUrlEncoded;
return;
}
- if (a_Request.GetContentType().substr(0, m_MultipartFormData.length()) == m_MultipartFormData)
+ if (strncmp(a_Request.GetContentType().c_str(), "multipart/form-data", 19) == 0)
{
m_Kind = fpkMultipart;
+ BeginMultipart(a_Request);
return;
}
}
@@ -56,18 +53,24 @@ cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request) :
void cHTTPFormParser::Parse(const char * a_Data, int a_Size)
{
- m_IncomingData.append(a_Data, a_Size);
+ if (!m_IsValid)
+ {
+ return;
+ }
+
switch (m_Kind)
{
case fpkURL:
case fpkFormUrlEncoded:
{
// This format is used for smaller forms (not file uploads), so we can delay parsing it until Finish()
+ m_IncomingData.append(a_Data, a_Size);
break;
}
case fpkMultipart:
{
- ParseMultipart();
+ ASSERT(m_MultipartParser.get() != NULL);
+ m_MultipartParser->Parse(a_Data, a_Size);
break;
}
default:
@@ -105,8 +108,8 @@ bool cHTTPFormParser::HasFormData(const cHTTPRequest & a_Request)
{
const AString & ContentType = a_Request.GetContentType();
return (
- (ContentType == m_FormURLEncoded) ||
- (ContentType.substr(0, m_MultipartFormData.length()) == m_MultipartFormData) ||
+ (ContentType == "application/x-www-form-urlencoded") ||
+ (strncmp(ContentType.c_str(), "multipart/form-data", 19) == 0) ||
(
(a_Request.GetMethod() == "GET") &&
(a_Request.GetURL().find('?') != AString::npos)
@@ -119,6 +122,16 @@ bool cHTTPFormParser::HasFormData(const cHTTPRequest & a_Request)
+void cHTTPFormParser::BeginMultipart(const cHTTPRequest & a_Request)
+{
+ ASSERT(m_MultipartParser.get() == NULL);
+ m_MultipartParser.reset(new cMultipartParser(a_Request.GetContentType(), *this));
+}
+
+
+
+
+
void cHTTPFormParser::ParseFormUrlEncoded(void)
{
// Parse m_IncomingData for all the variables; no more data is incoming, since this is called from Finish()
@@ -156,9 +169,107 @@ void cHTTPFormParser::ParseFormUrlEncoded(void)
-void cHTTPFormParser::ParseMultipart(void)
+void cHTTPFormParser::OnPartStart(void)
{
- // TODO
+ m_CurrentPartFileName.clear();
+ m_CurrentPartName.clear();
+ m_IsCurrentPartFile = false;
+ m_FileHasBeenAnnounced = false;
+}
+
+
+
+
+
+void cHTTPFormParser::OnPartHeader(const AString & a_Key, const AString & a_Value)
+{
+ if (NoCaseCompare(a_Key, "Content-Disposition") == 0)
+ {
+ size_t len = a_Value.size();
+ size_t ParamsStart = AString::npos;
+ for (size_t i = 0; i < len; ++i)
+ {
+ if (a_Value[i] > ' ')
+ {
+ if (strncmp(a_Value.c_str() + i, "form-data", 9) != 0)
+ {
+ // Content disposition is not "form-data", mark the whole form invalid
+ m_IsValid = false;
+ return;
+ }
+ ParamsStart = a_Value.find(';', i + 9);
+ break;
+ }
+ }
+ if (ParamsStart == AString::npos)
+ {
+ // There is data missing in the Content-Disposition field, mark the whole form invalid:
+ m_IsValid = false;
+ return;
+ }
+
+ // Parse the field name and optional filename from this header:
+ cNameValueParser Parser(a_Value.data() + ParamsStart, a_Value.size() - ParamsStart);
+ Parser.Finish();
+ m_CurrentPartName = Parser["name"];
+ if (!Parser.IsValid() || m_CurrentPartName.empty())
+ {
+ // The required parameter "name" is missing, mark the whole form invalid:
+ m_IsValid = false;
+ return;
+ }
+ m_CurrentPartFileName = Parser["filename"];
+ }
+}
+
+
+
+
+
+void cHTTPFormParser::OnPartData(const char * a_Data, int a_Size)
+{
+ if (m_CurrentPartName.empty())
+ {
+ // Prologue, epilogue or invalid part
+ return;
+ }
+ if (m_CurrentPartFileName.empty())
+ {
+ // This is a variable, store it in the map
+ iterator itr = find(m_CurrentPartName);
+ if (itr == end())
+ {
+ (*this)[m_CurrentPartName] = AString(a_Data, a_Size);
+ }
+ else
+ {
+ itr->second.append(a_Data, a_Size);
+ }
+ }
+ else
+ {
+ // This is a file, pass it on through the callbacks
+ if (!m_FileHasBeenAnnounced)
+ {
+ m_Callbacks.OnFileStart(*this, m_CurrentPartFileName);
+ m_FileHasBeenAnnounced = true;
+ }
+ m_Callbacks.OnFileData(*this, a_Data, a_Size);
+ }
+}
+
+
+
+
+
+void cHTTPFormParser::OnPartEnd(void)
+{
+ if (m_FileHasBeenAnnounced)
+ {
+ m_Callbacks.OnFileEnd(*this);
+ }
+ m_CurrentPartName.clear();
+ m_CurrentPartFileName.clear();
}
diff --git a/source/HTTPServer/HTTPFormParser.h b/source/HTTPServer/HTTPFormParser.h
index 01446e865..b92ef9d3c 100644
--- a/source/HTTPServer/HTTPFormParser.h
+++ b/source/HTTPServer/HTTPFormParser.h
@@ -8,6 +8,8 @@
#pragma once
+#include "MultipartParser.h"
+
@@ -20,10 +22,25 @@ class cHTTPRequest;
class cHTTPFormParser :
- public std::map<AString, AString>
+ public std::map<AString, AString>,
+ public cMultipartParser::cCallbacks
{
public:
- cHTTPFormParser(cHTTPRequest & a_Request);
+ class cCallbacks
+ {
+ public:
+ /// Called when a new file part is encountered in the form data
+ virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) = 0;
+
+ /// Called when more file data has come for the current file in the form data
+ virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) = 0;
+
+ /// Called when the current file part has ended in the form data
+ virtual void OnFileEnd(cHTTPFormParser & a_Parser) = 0;
+ } ;
+
+
+ cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks);
/// Adds more data into the parser, as the request body is received
void Parse(const char * a_Data, int a_Size);
@@ -41,26 +58,48 @@ protected:
{
fpkURL, ///< The form has been transmitted as parameters to a GET request
fpkFormUrlEncoded, ///< The form has been POSTed or PUT, with Content-Type of "application/x-www-form-urlencoded"
- fpkMultipart, ///< The form has been POSTed or PUT, with Content-Type of "multipart/*". Currently unsupported
+ fpkMultipart, ///< The form has been POSTed or PUT, with Content-Type of "multipart/form-data"
};
+
+ /// The callbacks to call for incoming file data
+ cCallbacks & m_Callbacks;
/// The kind of the parser (decided in the constructor, used in Parse()
eKind m_Kind;
-
+
+ /// Buffer for the incoming data until it's parsed
AString m_IncomingData;
+ /// True if the information received so far is a valid form; set to false on first problem. Further parsing is skipped when false.
bool m_IsValid;
- /// Simple static objects to hold the various strings for comparison with request's content-type
- static AString m_FormURLEncoded;
- static AString m_MultipartFormData;
-
+ /// The parser for the multipart data, if used
+ std::auto_ptr<cMultipartParser> m_MultipartParser;
+
+ /// Name of the currently parsed part in multipart data
+ AString m_CurrentPartName;
+
+ /// True if the currently parsed part in multipart data is a file
+ bool m_IsCurrentPartFile;
+
+ /// Filename of the current parsed part in multipart data (for file uploads)
+ AString m_CurrentPartFileName;
+
+ /// Set to true after m_Callbacks.OnFileStart() has been called, reset to false on PartEnd
+ bool m_FileHasBeenAnnounced;
+
+
+ /// Sets up the object for parsing a fpkMultipart request
+ void BeginMultipart(const cHTTPRequest & a_Request);
/// Parses m_IncomingData as form-urlencoded data (fpkURL or fpkFormUrlEncoded kinds)
void ParseFormUrlEncoded(void);
- /// Parses m_IncomingData as multipart data (fpkMultipart kind)
- void ParseMultipart(void);
+ // cMultipartParser::cCallbacks overrides:
+ virtual void OnPartStart (void) override;
+ virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override;
+ virtual void OnPartData (const char * a_Data, int a_Size) override;
+ virtual void OnPartEnd (void) override;
} ;
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);
}
diff --git a/source/HTTPServer/HTTPMessage.h b/source/HTTPServer/HTTPMessage.h
index 1c2514739..ef8e12ca4 100644
--- a/source/HTTPServer/HTTPMessage.h
+++ b/source/HTTPServer/HTTPMessage.h
@@ -9,6 +9,8 @@
#pragma once
+#include "EnvelopeParser.h"
+
@@ -58,15 +60,18 @@ protected:
class cHTTPRequest :
- public cHTTPMessage
+ public cHTTPMessage,
+ protected cEnvelopeParser::cCallbacks
{
typedef cHTTPMessage super;
public:
cHTTPRequest(void);
- /// Parses the headers information from the received data in the specified string of incoming data. Returns true if successful.
- bool ParseHeaders(const char * a_IncomingData, size_t a_idxEnd);
+ /** Parses the request line and then headers from the received data.
+ Returns the number of bytes consumed or a negative number for error
+ */
+ int ParseHeaders(const char * a_Data, int a_Size);
/// Returns true if the request did contain a Content-Length header
bool HasReceivedContentLength(void) const { return (m_ContentLength >= 0); }
@@ -83,7 +88,19 @@ public:
/// Retrieves the UserData pointer that has been stored within this request.
void * GetUserData(void) const { return m_UserData; }
+ /// Returns true if more data is expected for the request headers
+ bool IsInHeaders(void) const { return m_EnvelopeParser.IsInHeaders(); }
+
protected:
+ /// Parser for the envelope data
+ cEnvelopeParser m_EnvelopeParser;
+
+ /// True if the data received so far is parsed successfully. When false, all further parsing is skipped
+ bool m_IsValid;
+
+ /// Bufferred incoming data, while parsing for the request line
+ AString m_IncomingHeaderData;
+
/// Method of the request (GET / PUT / POST / ...)
AString m_Method;
@@ -94,21 +111,13 @@ protected:
void * m_UserData;
- /** Parses the RequestLine out of a_Data, up to index a_IdxEnd
- Returns the index to the next line, or npos if invalid request
+ /** Parses the incoming data for the first line (RequestLine)
+ Returns the number of bytes consumed, or -1 for an error
*/
- size_t ParseRequestLine(const char * a_Data, size_t a_IdxEnd);
+ int ParseRequestLine(const char * a_Data, int a_Size);
- /** Parses one header field out of a_Data, up to offset a_IdxEnd.
- Returns the index to the next line (relative to a_Data), or npos if invalid request.
- a_Key is set to the key that was parsed (used for multi-line headers)
- */
- size_t ParseHeaderField(const char * a_Data, size_t a_IdxEnd, AString & a_Key);
-
- /** Parses one header field that is known to be a continuation of previous header.
- Returns the index to the next line, or npos if invalid request.
- */
- size_t ParseHeaderFieldContinuation(const char * a_Data, size_t a_IdxEnd, AString & a_Key);
+ // cEnvelopeParser::cCallbacks overrides:
+ virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override;
} ;
diff --git a/source/HTTPServer/HTTPServer.cpp b/source/HTTPServer/HTTPServer.cpp
index ac21acb24..4102d1047 100644
--- a/source/HTTPServer/HTTPServer.cpp
+++ b/source/HTTPServer/HTTPServer.cpp
@@ -24,13 +24,14 @@
class cDebugCallbacks :
- public cHTTPServer::cCallbacks
+ public cHTTPServer::cCallbacks,
+ protected cHTTPFormParser::cCallbacks
{
virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override
{
if (cHTTPFormParser::HasFormData(a_Request))
{
- a_Request.SetUserData(new cHTTPFormParser(a_Request));
+ a_Request.SetUserData(new cHTTPFormParser(a_Request, *this));
}
}
@@ -79,6 +80,23 @@ class cDebugCallbacks :
}
+ virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override
+ {
+ // TODO
+ }
+
+
+ virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override
+ {
+ // TODO
+ }
+
+
+ virtual void OnFileEnd(cHTTPFormParser & a_Parser) override
+ {
+ // TODO
+ }
+
} g_DebugCallbacks;