// 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 a_Size; } // If still waiting for the status line, add to buffer and try parsing it: if (m_StatusLine.empty()) { m_Buffer.append(a_Data, a_Size); if (!ParseStatusLine()) { // All data used, but not a complete status line yet. return 0; } 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; } 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(); return ParseBody(m_Buffer.data(), m_Buffer.size()); } return 0; } // 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 ParseBody(a_Data + bytesConsumed, a_Size - bytesConsumed); } return 0; } // 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: return 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(); }