diff options
Diffstat (limited to 'src/HTTP/HTTPResponseParser.cpp')
-rw-r--r-- | src/HTTP/HTTPResponseParser.cpp | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/src/HTTP/HTTPResponseParser.cpp b/src/HTTP/HTTPResponseParser.cpp new file mode 100644 index 000000000..9411208e2 --- /dev/null +++ b/src/HTTP/HTTPResponseParser.cpp @@ -0,0 +1,183 @@ + +// 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(); +} + + + + |