1
0
cuberite-2a/src/HTTPServer/HTTPMessage.cpp
madmaxoft fab726282c HTTP connections aren't kept alive unless explicitly enabled.
Only the client can decide that the connection can be kept alive, we must close the socket if the client doesn't indicate keepalive support.
This will provide a fix for #390 when #560 is fixed; until then the issue remains, just it's no longer HTTPServer's fault.
2014-01-18 20:20:56 +01:00

285 lines
5.3 KiB
C++

// HTTPMessage.cpp
// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes
#include "Globals.h"
#include "HTTPMessage.h"
// 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_ContentLength(-1)
{
}
void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value)
{
AString Key = a_Key;
StrToLower(Key);
cNameValueMap::iterator itr = m_Headers.find(Key);
if (itr == m_Headers.end())
{
m_Headers[Key] = a_Value;
}
else
{
// The header-field key is specified multiple times, combine into comma-separated list (RFC 2616 @ 4.2)
itr->second.append(", ");
itr->second.append(a_Value);
}
// Special processing for well-known headers:
if (Key == "content-type")
{
m_ContentType = m_Headers[Key];
}
else if (Key == "content-length")
{
m_ContentLength = atoi(m_Headers[Key].c_str());
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cHTTPRequest:
cHTTPRequest::cHTTPRequest(void) :
super(mkRequest),
m_EnvelopeParser(*this),
m_IsValid(true),
m_UserData(NULL),
m_HasAuth(false),
m_AllowKeepAlive(false)
{
}
int cHTTPRequest::ParseHeaders(const char * a_Data, int a_Size)
{
if (!m_IsValid)
{
return -1;
}
if (m_Method.empty())
{
// 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)
{
m_IsValid = false;
return res2;
}
return res2 + res;
}
if (m_EnvelopeParser.IsInHeaders())
{
int res = m_EnvelopeParser.Parse(a_Data, a_Size);
if (res < 0)
{
m_IsValid = false;
}
return res;
}
return 0;
}
AString cHTTPRequest::GetBareURL(void) const
{
size_t idxQM = m_URL.find('?');
if (idxQM != AString::npos)
{
return m_URL.substr(0, idxQM);
}
else
{
return m_URL;
}
}
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 < IdxEnd) &&
(
(m_IncomingHeaderData[LineStart] == '\r') ||
(m_IncomingHeaderData[LineStart] == '\n')
)
)
{
LineStart++;
}
if (LineStart >= IdxEnd)
{
m_IsValid = false;
return -1;
}
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 -1;
}
}
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 -1;
}
// Check that there's HTTP/version at the end
if (strncmp(a_Data + URLEnd + 1, "HTTP/1.", 7) != 0)
{
m_IsValid = false;
return -1;
}
m_Method = m_IncomingHeaderData.substr(LineStart, MethodEnd - LineStart);
m_URL = m_IncomingHeaderData.substr(MethodEnd + 1, URLEnd - MethodEnd - 1);
return i + 1;
}
} // switch (m_IncomingHeaderData[i])
} // for i - m_IncomingHeaderData[]
// CRLF hasn't been encountered yet, consider all data consumed
return a_Size;
}
void cHTTPRequest::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);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cHTTPResponse:
cHTTPResponse::cHTTPResponse(void) :
super(mkResponse)
{
}
void cHTTPResponse::AppendToData(AString & a_DataStream) const
{
a_DataStream.append("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: ");
a_DataStream.append(m_ContentType);
a_DataStream.append("\r\n");
for (cNameValueMap::const_iterator itr = m_Headers.begin(), end = m_Headers.end(); itr != end; ++itr)
{
if ((itr->first == "Content-Type") || (itr->first == "Content-Length"))
{
continue;
}
a_DataStream.append(itr->first);
a_DataStream.append(": ");
a_DataStream.append(itr->second);
a_DataStream.append("\r\n");
} // for itr - m_Headers[]
a_DataStream.append("\r\n");
}