summaryrefslogblamecommitdiffstats
path: root/src/HTTP/HTTPResponseParser.cpp
blob: 5469666b648ffb8f9c8ddb1aa2b09925c72e47ec (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15














                                                                                               
                             














                                                                                     
                         


                                                                                  
                                             





                                                                             
                                      












                                                                                               
                                                               



                                                                                                                  
                                          





                                                                               
                 
                              















                                                                                                                  
                                                                                                         
                 
                              



































                                                                                                                      

                                                                                                               















                                                                                                               



                                                                                                                             




































                                                                                      
                            





                                     

// HTTPResponseParser.cpp

// Implements the cHTTPResponseParser class representing the parser for incoming HTTP responses

#include "Globals.h"
#include "HTTPResponseParser.h"





cHTTPResponseParser::cHTTPResponseParser(cHTTPResponseParser::cCallbacks & a_Callbacks):
	Super(mkResponse),
	m_Callbacks(a_Callbacks),
	m_HasHadError(false),
	m_IsInHeaders(true),
	m_IsFinished(false),
	m_EnvelopeParser(*this)
{
}





size_t cHTTPResponseParser::Parse(const char * a_Data, size_t a_Size)
{
	// If parsing already finished or errorred, let the caller keep all the data:
	if (m_IsFinished || m_HasHadError)
	{
		return 0;
	}

	// If still waiting for the status line, add to buffer and try parsing it:
	auto inBufferSoFar = m_Buffer.size();
	if (m_StatusLine.empty())
	{
		m_Buffer.append(a_Data, a_Size);
		if (!ParseStatusLine())
		{
			// All data used, but not a complete status line yet.
			return a_Size;
		}
		if (m_HasHadError)
		{
			return AString::npos;
		}
		// Status line completed, feed the rest of the buffer into the envelope parser:
		auto bytesConsumed = m_EnvelopeParser.Parse(m_Buffer.data(), m_Buffer.size());
		if (bytesConsumed == AString::npos)
		{
			m_HasHadError = true;
			m_Callbacks.OnError("Failed to parse the envelope");
			return AString::npos;
		}
		ASSERT(bytesConsumed < inBufferSoFar + a_Size);
		m_Buffer.erase(0, bytesConsumed);
		if (!m_Buffer.empty())
		{
			// Headers finished and there's still data left in the buffer, process it as message body:
			HeadersFinished();
			auto res = ParseBody(m_Buffer.data(), m_Buffer.size());
			if (res == AString::npos)
			{
				return AString::npos;
			}
			return res + bytesConsumed - inBufferSoFar;
		}
		return a_Size;
	}  // if (m_StatusLine.empty())

	// If still parsing headers, send them to the envelope parser:
	if (m_IsInHeaders)
	{
		auto bytesConsumed = m_EnvelopeParser.Parse(a_Data, a_Size);
		if (bytesConsumed == AString::npos)
		{
			m_HasHadError = true;
			m_Callbacks.OnError("Failed to parse the envelope");
			return AString::npos;
		}
		if (bytesConsumed < a_Size)
		{
			// Headers finished and there's still data left in the buffer, process it as message body:
			HeadersFinished();
			return bytesConsumed + ParseBody(a_Data + bytesConsumed, a_Size - bytesConsumed);
		}
		return a_Size;
	}

	// Already parsing the body
	return ParseBody(a_Data, a_Size);
}





bool cHTTPResponseParser::ParseStatusLine(void)
{
	auto idxLineEnd = m_Buffer.find("\r\n");
	if (idxLineEnd == AString::npos)
	{
		// Not a complete line yet
		return false;
	}
	m_StatusLine = m_Buffer.substr(0, idxLineEnd);
	m_Buffer.erase(0, idxLineEnd + 2);
	m_Callbacks.OnStatusLine(m_StatusLine);
	return true;
}




size_t cHTTPResponseParser::ParseBody(const char * a_Data, size_t a_Size)
{
	if (m_TransferEncodingParser == nullptr)
	{
		// We have no Transfer-encoding parser assigned. This should have happened when finishing the envelope
		return AString::npos;
	}

	// Parse the body using the transfer encoding parser:
	// (Note that TE parser returns the number of bytes left, while we return the number of bytes consumed)
	return a_Size - m_TransferEncodingParser->Parse(a_Data, a_Size);
}





void cHTTPResponseParser::HeadersFinished(void)
{
	m_IsInHeaders = false;
	m_Callbacks.OnHeadersFinished();

	auto transferEncoding = m_Headers.find("transfer-encoding");
	if (transferEncoding == m_Headers.end())
	{
		m_TransferEncodingParser = cTransferEncodingParser::Create(*this, "identity", m_ContentLength);
	}
	else
	{
		m_TransferEncodingParser = cTransferEncodingParser::Create(*this, transferEncoding->second, m_ContentLength);
	}
}





void cHTTPResponseParser::OnHeaderLine(const AString & a_Key, const AString & a_Value)
{
	AddHeader(a_Key, a_Value);
	m_Callbacks.OnHeaderLine(a_Key, a_Value);
}





void cHTTPResponseParser::OnError(const AString & a_ErrorDescription)
{
	m_HasHadError = true;
	m_Callbacks.OnError(a_ErrorDescription);
}





void cHTTPResponseParser::OnBodyData(const void * a_Data, size_t a_Size)
{
	m_Callbacks.OnBodyData(a_Data, a_Size);
}





void cHTTPResponseParser::OnBodyFinished(void)
{
	m_IsFinished = true;
	m_Callbacks.OnBodyFinished();
}