summaryrefslogblamecommitdiffstats
path: root/source/HTTPServer/HTTPConnection.cpp
blob: 61f4b3a31d2eb0d11f25e6f79eea627d165e9c31 (plain) (tree)


























                                                                                                       
                                                  










                                                            
                                                  










                                                            

                                                  








                                          
                                           
                                 
                                                  





 
































                                                                                                       





                                                                   
                                                     
                         
                                                                    
                         


                                                                                           






                                                                    






                                                                                                   

                                                                          
                                                                                             




                                                                                                

                                                                                       
                                                   
                         
                                                                                             


                            
                                                                                                                            






                                                         







                                                                                                           
                                                      
                                                                                       

                                                        
                         



































                                                                           

// 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)
{
}




void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response)
{
	AppendPrintf(m_OutgoingData, "%d %s\r\n\r\n", a_StatusCode, a_Response.c_str());
	m_HTTPServer.NotifyConnectionWrite(*this);
}





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 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::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);
				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);
	}
}