aboutsummaryrefslogtreecommitdiff
path: root/src/zenhttp/httpasio.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-10-03 18:15:35 +0200
committerGitHub <[email protected]>2023-10-03 18:15:35 +0200
commitc10f6ce83c4460fa162641a22243faed27abe541 (patch)
treeeb32786f786e8ec50ba7c6f86f231128ef5df10e /src/zenhttp/httpasio.cpp
parentfaster accesstime save restore (#439) (diff)
downloadzen-c10f6ce83c4460fa162641a22243faed27abe541.tar.xz
zen-c10f6ce83c4460fa162641a22243faed27abe541.zip
factored out http parser from asio into separate files (#444)
factored out http request parsing from httpasio into separate files to enable code to be reused for different transports
Diffstat (limited to 'src/zenhttp/httpasio.cpp')
-rw-r--r--src/zenhttp/httpasio.cpp465
1 files changed, 15 insertions, 450 deletions
diff --git a/src/zenhttp/httpasio.cpp b/src/zenhttp/httpasio.cpp
index f29b3132e..702ca11fd 100644
--- a/src/zenhttp/httpasio.cpp
+++ b/src/zenhttp/httpasio.cpp
@@ -7,6 +7,8 @@
#include <zencore/trace.h>
#include <zenhttp/httpserver.h>
+#include "httpparser.h"
+
#include <deque>
#include <memory>
#include <string_view>
@@ -17,7 +19,6 @@ ZEN_THIRD_PARTY_INCLUDES_START
# include <conio.h>
# include <mstcpip.h>
#endif
-#include <http_parser.h>
#include <asio.hpp>
ZEN_THIRD_PARTY_INCLUDES_END
@@ -34,7 +35,6 @@ namespace zen::asio_http {
using namespace std::literals;
struct HttpAcceptor;
-struct HttpRequest;
struct HttpResponse;
struct HttpServerConnection;
@@ -96,7 +96,7 @@ public:
class HttpAsioServerRequest : public HttpServerRequest
{
public:
- HttpAsioServerRequest(asio_http::HttpRequest& Request, HttpService& Service, IoBuffer PayloadBuffer);
+ HttpAsioServerRequest(HttpRequestParser& Request, HttpService& Service, IoBuffer PayloadBuffer);
~HttpAsioServerRequest();
virtual Oid ParseSessionId() const override;
@@ -114,101 +114,11 @@ public:
HttpAsioServerRequest(const HttpAsioServerRequest&) = delete;
HttpAsioServerRequest& operator=(const HttpAsioServerRequest&) = delete;
- asio_http::HttpRequest& m_Request;
+ HttpRequestParser& m_Request;
IoBuffer m_PayloadBuffer;
std::unique_ptr<HttpResponse> m_Response;
};
-struct HttpRequest
-{
- explicit HttpRequest(HttpServerConnection& Connection) : m_Connection(Connection) {}
-
- void Initialize();
- size_t ConsumeData(const char* InputData, size_t DataSize);
- void ResetState();
-
- HttpVerb RequestVerb() const { return m_RequestVerb; }
- bool IsKeepAlive() const { return m_KeepAlive; }
- std::string_view Url() const { return m_NormalizedUrl.empty() ? std::string_view(m_Url, m_UrlLength) : m_NormalizedUrl; }
- std::string_view QueryString() const { return std::string_view(m_QueryString, m_QueryLength); }
- IoBuffer Body() { return m_BodyBuffer; }
-
- inline HttpContentType ContentType()
- {
- if (m_ContentTypeHeaderIndex < 0)
- {
- return HttpContentType::kUnknownContentType;
- }
-
- return ParseContentType(m_Headers[m_ContentTypeHeaderIndex].Value);
- }
-
- inline HttpContentType AcceptType()
- {
- if (m_AcceptHeaderIndex < 0)
- {
- return HttpContentType::kUnknownContentType;
- }
-
- return ParseContentType(m_Headers[m_AcceptHeaderIndex].Value);
- }
-
- Oid SessionId() const { return m_SessionId; }
- int RequestId() const { return m_RequestId; }
-
- std::string_view RangeHeader() const { return m_RangeHeaderIndex != -1 ? m_Headers[m_RangeHeaderIndex].Value : std::string_view(); }
-
-private:
- struct HeaderEntry
- {
- HeaderEntry() = default;
-
- HeaderEntry(std::string_view InName, std::string_view InValue) : Name(InName), Value(InValue) {}
-
- std::string_view Name;
- std::string_view Value;
- };
-
- HttpServerConnection& m_Connection;
- char* m_HeaderCursor = m_HeaderBuffer;
- char* m_Url = nullptr;
- size_t m_UrlLength = 0;
- char* m_QueryString = nullptr;
- size_t m_QueryLength = 0;
- char* m_CurrentHeaderName = nullptr; // Used while parsing headers
- size_t m_CurrentHeaderNameLength = 0;
- char* m_CurrentHeaderValue = nullptr; // Used while parsing headers
- size_t m_CurrentHeaderValueLength = 0;
- std::vector<HeaderEntry> m_Headers;
- int8_t m_ContentLengthHeaderIndex;
- int8_t m_AcceptHeaderIndex;
- int8_t m_ContentTypeHeaderIndex;
- int8_t m_RangeHeaderIndex;
- HttpVerb m_RequestVerb;
- bool m_KeepAlive = false;
- bool m_Expect100Continue = false;
- int m_RequestId = -1;
- Oid m_SessionId{};
- IoBuffer m_BodyBuffer;
- uint64_t m_BodyPosition = 0;
- http_parser m_Parser;
- char m_HeaderBuffer[1024];
- std::string m_NormalizedUrl;
-
- void AppendCurrentHeader();
-
- int OnMessageBegin();
- int OnUrl(const char* Data, size_t Bytes);
- int OnHeader(const char* Data, size_t Bytes);
- int OnHeaderValue(const char* Data, size_t Bytes);
- int OnHeadersComplete();
- int OnBody(const char* Data, size_t Bytes);
- int OnMessageComplete();
-
- static HttpRequest* GetThis(http_parser* Parser) { return reinterpret_cast<HttpRequest*>(Parser->data); }
- static http_parser_settings s_ParserSettings;
-};
-
struct HttpResponse
{
public:
@@ -304,17 +214,19 @@ private:
//////////////////////////////////////////////////////////////////////////
-struct HttpServerConnection : std::enable_shared_from_this<HttpServerConnection>
+struct HttpServerConnection : public HttpConnectionBase, std::enable_shared_from_this<HttpServerConnection>
{
HttpServerConnection(HttpAsioServerImpl& Server, std::unique_ptr<asio::ip::tcp::socket>&& Socket);
~HttpServerConnection();
- void HandleNewRequest();
- void TerminateConnection();
- void HandleRequest();
-
std::shared_ptr<HttpServerConnection> AsSharedPtr() { return shared_from_this(); }
+ // HttpConnectionBase implementation
+
+ virtual void HandleNewRequest() override;
+ virtual void TerminateConnection() override;
+ virtual void HandleRequest() override;
+
private:
enum class RequestState
{
@@ -327,8 +239,8 @@ private:
kTerminated
};
- RequestState m_RequestState = RequestState::kInitialState;
- HttpRequest m_RequestData{*this};
+ RequestState m_RequestState = RequestState::kInitialState;
+ HttpRequestParser m_RequestData{*this};
void EnqueueRead();
void OnDataReceived(const asio::error_code& Ec, std::size_t ByteCount);
@@ -662,353 +574,6 @@ HttpServerConnection::HandleRequest()
}
//////////////////////////////////////////////////////////////////////////
-//
-// HttpRequest
-//
-
-http_parser_settings HttpRequest::s_ParserSettings{
- .on_message_begin = [](http_parser* p) { return GetThis(p)->OnMessageBegin(); },
- .on_url = [](http_parser* p, const char* Data, size_t ByteCount) { return GetThis(p)->OnUrl(Data, ByteCount); },
- .on_status =
- [](http_parser* p, const char* Data, size_t ByteCount) {
- ZEN_UNUSED(p, Data, ByteCount);
- return 0;
- },
- .on_header_field = [](http_parser* p, const char* Data, size_t ByteCount) { return GetThis(p)->OnHeader(Data, ByteCount); },
- .on_header_value = [](http_parser* p, const char* Data, size_t ByteCount) { return GetThis(p)->OnHeaderValue(Data, ByteCount); },
- .on_headers_complete = [](http_parser* p) { return GetThis(p)->OnHeadersComplete(); },
- .on_body = [](http_parser* p, const char* Data, size_t ByteCount) { return GetThis(p)->OnBody(Data, ByteCount); },
- .on_message_complete = [](http_parser* p) { return GetThis(p)->OnMessageComplete(); },
- .on_chunk_header{},
- .on_chunk_complete{}};
-
-void
-HttpRequest::Initialize()
-{
- http_parser_init(&m_Parser, HTTP_REQUEST);
- m_Parser.data = this;
-
- ResetState();
-}
-
-size_t
-HttpRequest::ConsumeData(const char* InputData, size_t DataSize)
-{
- const size_t ConsumedBytes = http_parser_execute(&m_Parser, &s_ParserSettings, InputData, DataSize);
-
- http_errno HttpErrno = HTTP_PARSER_ERRNO((&m_Parser));
-
- if (HttpErrno && HttpErrno != HPE_INVALID_EOF_STATE)
- {
- ZEN_WARN("HTTP parser error {} ('{}'). Closing connection", http_errno_name(HttpErrno), http_errno_description(HttpErrno));
- return ~0ull;
- }
-
- return ConsumedBytes;
-}
-
-int
-HttpRequest::OnUrl(const char* Data, size_t Bytes)
-{
- if (!m_Url)
- {
- ZEN_ASSERT_SLOW(m_UrlLength == 0);
- m_Url = m_HeaderCursor;
- }
-
- const size_t RemainingBufferSpace = sizeof m_HeaderBuffer + m_HeaderBuffer - m_HeaderCursor;
-
- if (RemainingBufferSpace < Bytes)
- {
- ZEN_WARN("HTTP parser does not have enough space for incoming request, need {} more bytes", Bytes - RemainingBufferSpace);
- return 1;
- }
-
- memcpy(m_HeaderCursor, Data, Bytes);
- m_HeaderCursor += Bytes;
- m_UrlLength += Bytes;
-
- return 0;
-}
-
-int
-HttpRequest::OnHeader(const char* Data, size_t Bytes)
-{
- if (m_CurrentHeaderValueLength)
- {
- AppendCurrentHeader();
-
- m_CurrentHeaderNameLength = 0;
- m_CurrentHeaderValueLength = 0;
- m_CurrentHeaderName = m_HeaderCursor;
- }
- else if (m_CurrentHeaderName == nullptr)
- {
- m_CurrentHeaderName = m_HeaderCursor;
- }
-
- const size_t RemainingBufferSpace = sizeof m_HeaderBuffer + m_HeaderBuffer - m_HeaderCursor;
- if (RemainingBufferSpace < Bytes)
- {
- ZEN_WARN("HTTP parser does not have enough space for incoming header name, need {} more bytes", Bytes - RemainingBufferSpace);
- return 1;
- }
-
- memcpy(m_HeaderCursor, Data, Bytes);
- m_HeaderCursor += Bytes;
- m_CurrentHeaderNameLength += Bytes;
-
- return 0;
-}
-
-void
-HttpRequest::AppendCurrentHeader()
-{
- std::string_view HeaderName(m_CurrentHeaderName, m_CurrentHeaderNameLength);
- std::string_view HeaderValue(m_CurrentHeaderValue, m_CurrentHeaderValueLength);
-
- const uint32_t HeaderHash = HashStringAsLowerDjb2(HeaderName);
-
- if (HeaderHash == HashContentLength)
- {
- m_ContentLengthHeaderIndex = (int8_t)m_Headers.size();
- }
- else if (HeaderHash == HashAccept)
- {
- m_AcceptHeaderIndex = (int8_t)m_Headers.size();
- }
- else if (HeaderHash == HashContentType)
- {
- m_ContentTypeHeaderIndex = (int8_t)m_Headers.size();
- }
- else if (HeaderHash == HashSession)
- {
- m_SessionId = Oid::FromHexString(HeaderValue);
- }
- else if (HeaderHash == HashRequest)
- {
- std::from_chars(HeaderValue.data(), HeaderValue.data() + HeaderValue.size(), m_RequestId);
- }
- else if (HeaderHash == HashExpect)
- {
- if (HeaderValue == "100-continue"sv)
- {
- // We don't currently do anything with this
- m_Expect100Continue = true;
- }
- else
- {
- ZEN_INFO("Unexpected expect - Expect: {}", HeaderValue);
- }
- }
- else if (HeaderHash == HashRange)
- {
- m_RangeHeaderIndex = (int8_t)m_Headers.size();
- }
-
- m_Headers.emplace_back(HeaderName, HeaderValue);
-}
-
-int
-HttpRequest::OnHeaderValue(const char* Data, size_t Bytes)
-{
- if (m_CurrentHeaderValueLength == 0)
- {
- m_CurrentHeaderValue = m_HeaderCursor;
- }
-
- const size_t RemainingBufferSpace = sizeof m_HeaderBuffer + m_HeaderBuffer - m_HeaderCursor;
- if (RemainingBufferSpace < Bytes)
- {
- ZEN_WARN("HTTP parser does not have enough space for incoming header value, need {} more bytes", Bytes - RemainingBufferSpace);
- return 1;
- }
-
- memcpy(m_HeaderCursor, Data, Bytes);
- m_HeaderCursor += Bytes;
- m_CurrentHeaderValueLength += Bytes;
-
- return 0;
-}
-
-static void
-NormalizeUrlPath(const char* Url, size_t UrlLength, std::string& NormalizedUrl)
-{
- bool LastCharWasSeparator = false;
- for (std::string_view::size_type UrlIndex = 0; UrlIndex < UrlLength; ++UrlIndex)
- {
- const char UrlChar = Url[UrlIndex];
- const bool IsSeparator = (UrlChar == '/');
-
- if (IsSeparator && LastCharWasSeparator)
- {
- if (NormalizedUrl.empty())
- {
- NormalizedUrl.reserve(UrlLength);
- NormalizedUrl.append(Url, UrlIndex);
- }
-
- if (!LastCharWasSeparator)
- {
- NormalizedUrl.push_back('/');
- }
- }
- else if (!NormalizedUrl.empty())
- {
- NormalizedUrl.push_back(UrlChar);
- }
-
- LastCharWasSeparator = IsSeparator;
- }
-}
-
-int
-HttpRequest::OnHeadersComplete()
-{
- try
- {
- if (m_CurrentHeaderValueLength)
- {
- AppendCurrentHeader();
- }
-
- m_KeepAlive = !!http_should_keep_alive(&m_Parser);
-
- switch (m_Parser.method)
- {
- case HTTP_GET:
- m_RequestVerb = HttpVerb::kGet;
- break;
-
- case HTTP_POST:
- m_RequestVerb = HttpVerb::kPost;
- break;
-
- case HTTP_PUT:
- m_RequestVerb = HttpVerb::kPut;
- break;
-
- case HTTP_DELETE:
- m_RequestVerb = HttpVerb::kDelete;
- break;
-
- case HTTP_HEAD:
- m_RequestVerb = HttpVerb::kHead;
- break;
-
- case HTTP_COPY:
- m_RequestVerb = HttpVerb::kCopy;
- break;
-
- case HTTP_OPTIONS:
- m_RequestVerb = HttpVerb::kOptions;
- break;
-
- default:
- ZEN_WARN("invalid HTTP method: '{}'", http_method_str((http_method)m_Parser.method));
- break;
- }
-
- std::string_view Url(m_Url, m_UrlLength);
-
- if (auto QuerySplit = Url.find_first_of('?'); QuerySplit != std::string_view::npos)
- {
- m_UrlLength = QuerySplit;
- m_QueryString = m_Url + QuerySplit + 1;
- m_QueryLength = Url.size() - QuerySplit - 1;
- }
-
- NormalizeUrlPath(m_Url, m_UrlLength, m_NormalizedUrl);
-
- if (m_ContentLengthHeaderIndex >= 0)
- {
- std::string_view& Value = m_Headers[m_ContentLengthHeaderIndex].Value;
- uint64_t ContentLength = 0;
- std::from_chars(Value.data(), Value.data() + Value.size(), ContentLength);
-
- if (ContentLength)
- {
- m_BodyBuffer = IoBuffer(ContentLength);
- }
-
- m_BodyBuffer.SetContentType(ContentType());
-
- m_BodyPosition = 0;
- }
- }
- catch (const std::exception& Ex)
- {
- ZEN_WARN("HttpRequest::OnHeadersComplete failed. Reason '{}'", Ex.what());
- return -1;
- }
- return 0;
-}
-
-int
-HttpRequest::OnBody(const char* Data, size_t Bytes)
-{
- if (m_BodyPosition + Bytes > m_BodyBuffer.Size())
- {
- ZEN_WARN("HTTP parser incoming body is larger than content size, need {} more bytes",
- (m_BodyPosition + Bytes) - m_BodyBuffer.Size());
- return 1;
- }
- memcpy(reinterpret_cast<uint8_t*>(m_BodyBuffer.MutableData()) + m_BodyPosition, Data, Bytes);
- m_BodyPosition += Bytes;
-
- if (http_body_is_final(&m_Parser))
- {
- if (m_BodyPosition != m_BodyBuffer.Size())
- {
- ZEN_WARN("Body mismatch! {} != {}", m_BodyPosition, m_BodyBuffer.Size());
- return 1;
- }
- }
-
- return 0;
-}
-
-void
-HttpRequest::ResetState()
-{
- m_HeaderCursor = m_HeaderBuffer;
- m_CurrentHeaderName = nullptr;
- m_CurrentHeaderNameLength = 0;
- m_CurrentHeaderValue = nullptr;
- m_CurrentHeaderValueLength = 0;
- m_CurrentHeaderName = nullptr;
- m_Url = nullptr;
- m_UrlLength = 0;
- m_QueryString = nullptr;
- m_QueryLength = 0;
- m_ContentLengthHeaderIndex = -1;
- m_AcceptHeaderIndex = -1;
- m_ContentTypeHeaderIndex = -1;
- m_RangeHeaderIndex = -1;
- m_Expect100Continue = false;
- m_BodyBuffer = {};
- m_BodyPosition = 0;
- m_Headers.clear();
- m_NormalizedUrl.clear();
-}
-
-int
-HttpRequest::OnMessageBegin()
-{
- return 0;
-}
-
-int
-HttpRequest::OnMessageComplete()
-{
- m_Connection.HandleRequest();
-
- ResetState();
-
- return 0;
-}
-
-//////////////////////////////////////////////////////////////////////////
struct HttpAcceptor
{
@@ -1136,7 +701,7 @@ private:
//////////////////////////////////////////////////////////////////////////
-HttpAsioServerRequest::HttpAsioServerRequest(asio_http::HttpRequest& Request, HttpService& Service, IoBuffer PayloadBuffer)
+HttpAsioServerRequest::HttpAsioServerRequest(HttpRequestParser& Request, HttpService& Service, IoBuffer PayloadBuffer)
: m_Request(Request)
, m_PayloadBuffer(std::move(PayloadBuffer))
{
@@ -1369,7 +934,7 @@ HttpAsioServer::HttpAsioServer(unsigned int ThreadCount)
: m_ThreadCount(ThreadCount != 0 ? ThreadCount : Max(std::thread::hardware_concurrency(), 8u))
, m_Impl(std::make_unique<asio_http::HttpAsioServerImpl>())
{
- ZEN_DEBUG("Request object size: {} ({:#x})", sizeof(asio_http::HttpRequest), sizeof(asio_http::HttpRequest));
+ ZEN_DEBUG("Request object size: {} ({:#x})", sizeof(HttpRequestParser), sizeof(HttpRequestParser));
}
HttpAsioServer::~HttpAsioServer()