// HTTPRequestParser.cpp
// Implements the cHTTPRequestParser class representing the parser for incoming HTTP requests
#include "Globals.h"
#include "HTTPRequestParser.h"
cHTTPRequestParser::cHTTPRequestParser(void) :
super(mkRequest),
m_EnvelopeParser(*this),
m_IsValid(true),
m_UserData(nullptr),
m_HasAuth(false),
m_AllowKeepAlive(false)
{
}
size_t cHTTPRequestParser::ParseHeaders(const char * a_Data, size_t a_Size)
{
if (!m_IsValid)
{
return AString::npos;
}
if (m_Method.empty())
{
// The first line hasn't been processed yet
size_t res = ParseRequestLine(a_Data, a_Size);
ASSERT((res == AString::npos) || (res <= a_Size));
if ((res == AString::npos) || (res == a_Size))
{
return res;
}
size_t res2 = m_EnvelopeParser.Parse(a_Data + res, a_Size - res);
ASSERT((res2 == AString::npos) || (res2 <= a_Size - res));
if (res2 == AString::npos)
{
m_IsValid = false;
return res2;
}
return res2 + res;
}
if (m_EnvelopeParser.IsInHeaders())
{
size_t res = m_EnvelopeParser.Parse(a_Data, a_Size);
ASSERT((res == AString::npos) || (res <= a_Size));
if (res == AString::npos)
{
m_IsValid = false;
}
return res;
}
return 0;
}
AString cHTTPRequestParser::GetBareURL(void) const
{
size_t idxQM = m_URL.find('?');
if (idxQM != AString::npos)
{
return m_URL.substr(0, idxQM);
}
else
{
return m_URL;
}
}
size_t cHTTPRequestParser::ParseRequestLine(const char * a_Data, size_t a_Size)
{
auto inBufferSoFar = m_IncomingHeaderData.size();
m_IncomingHeaderData.append(a_Data, a_Size);
auto IdxEnd = m_IncomingHeaderData.size();
// Ignore the initial CRLFs (HTTP spec's "should")
size_t LineStart = 0;
while (
(LineStart < IdxEnd) &&
(
(m_IncomingHeaderData[LineStart] == '\r') ||
(m_IncomingHeaderData[LineStart] == '\n')
)
)
{
LineStart++;
}
if (LineStart >= IdxEnd)
{
m_IsValid = false;
return AString::npos;
}
int NumSpaces = 0;
size_t MethodEnd = 0;
size_t URLEnd = 0;
for (size_t i = LineStart; i < IdxEnd; i++)
{
switch (m_IncomingHeaderData[i])
{
case ' ':
{
switch (NumSpaces)
{
case 0:
{
MethodEnd = i;
break;
}
case 1:
{
URLEnd = i;
break;
}
default:
{
// Too many spaces in the request
m_IsValid = false;
return AString::npos;
}
}
NumSpaces += 1;
break;
}
case '\n':
{
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
m_IsValid = false;
return AString::npos;
}
// Check that there's HTTP / version at the end
if (strncmp(m_IncomingHeaderData.c_str() + URLEnd + 1, "HTTP/1.", 7) != 0)
{
m_IsValid = false;
return AString::npos;
}
m_Method = m_IncomingHeaderData.substr(LineStart, MethodEnd - LineStart);
m_URL = m_IncomingHeaderData.substr(MethodEnd + 1, URLEnd - MethodEnd - 1);
return i + 1 - inBufferSoFar;
}
} // switch (m_IncomingHeaderData[i])
} // for i - m_IncomingHeaderData[]
// CRLF hasn't been encountered yet, consider all data consumed
return a_Size;
}
void cHTTPRequestParser::OnHeaderLine(const AString & a_Key, const AString & a_Value)
{
if (
(NoCaseCompare(a_Key, "Authorization") == 0) &&
(strncmp(a_Value.c_str(), "Basic ", 6) == 0)
)
{
AString UserPass = Base64Decode(a_Value.substr(6));
size_t idxCol = UserPass.find(':');
if (idxCol != AString::npos)
{
m_AuthUsername = UserPass.substr(0, idxCol);
m_AuthPassword = UserPass.substr(idxCol + 1);
m_HasAuth = true;
}
}
if ((a_Key == "Connection") && (NoCaseCompare(a_Value, "keep-alive") == 0))
{
m_AllowKeepAlive = true;
}
AddHeader(a_Key, a_Value);
}