1
0
cuberite-2a/src/HTTPServer/HTTPConnection.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

254 lines
4.9 KiB
C++

// 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)
{
// 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());
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\nContent-Length: 0\r\n\r\n", a_Realm.c_str());
m_HTTPServer.NotifyConnectionWrite(*this);
m_State = wcsRecvHeaders;
}
void cHTTPConnection::Send(const cHTTPResponse & a_Response)
{
ASSERT(m_State == wcsRecvIdle);
a_Response.AppendToData(m_OutgoingData);
m_State = wcsSendingResp;
m_HTTPServer.NotifyConnectionWrite(*this);
}
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);
}
void cHTTPConnection::FinishResponse(void)
{
ASSERT(m_State == wcsSendingResp);
m_OutgoingData.append("0\r\n\r\n");
m_State = wcsRecvHeaders;
m_HTTPServer.NotifyConnectionWrite(*this);
}
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":
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::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)
{
case wcsRecvHeaders:
{
if (m_CurrentRequest == NULL)
{
m_CurrentRequest = new cHTTPRequest;
}
int BytesConsumed = m_CurrentRequest->ParseHeaders(a_Data, a_Size);
if (BytesConsumed < 0)
{
delete m_CurrentRequest;
m_CurrentRequest = NULL;
m_State = wcsInvalid;
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 (a_Size > BytesConsumed)
{
DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed);
}
else
{
DataReceived("", 0); // If the request has zero body length, let it be processed right-away
}
break;
}
case wcsRecvBody:
{
ASSERT(m_CurrentRequest != NULL);
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_State = wcsRecvIdle;
m_HTTPServer.RequestFinished(*this, *m_CurrentRequest);
if (!m_CurrentRequest->DoesAllowKeepAlive())
{
m_State = wcsInvalid;
m_HTTPServer.CloseConnection(*this);
return;
}
delete m_CurrentRequest;
m_CurrentRequest = NULL;
}
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);
}
m_HTTPServer.CloseConnection(*this);
}