summaryrefslogtreecommitdiffstats
path: root/src/HTTP/HTTPResponseParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/HTTP/HTTPResponseParser.cpp')
-rw-r--r--src/HTTP/HTTPResponseParser.cpp183
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();
+}
+
+
+
+