aboutsummaryrefslogtreecommitdiff
path: root/src/zenhttp/servers/httpparser.cpp
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2025-09-04 13:17:47 +0200
committerGitHub Enterprise <[email protected]>2025-09-04 13:17:47 +0200
commit4b7a193ee5533ad82ef1c6b7a673db42b1e8a36f (patch)
treefa463ea20972d8e5ff24e35029743cb9296795e6 /src/zenhttp/servers/httpparser.cpp
parentadd validation of compact binary payloads before reading them (#483) (diff)
downloadzen-4b7a193ee5533ad82ef1c6b7a673db42b1e8a36f.tar.xz
zen-4b7a193ee5533ad82ef1c6b7a673db42b1e8a36f.zip
asio large header support (#484)
- Improvement: Allow large headers support in asio server implementation
Diffstat (limited to 'src/zenhttp/servers/httpparser.cpp')
-rw-r--r--src/zenhttp/servers/httpparser.cpp148
1 files changed, 78 insertions, 70 deletions
diff --git a/src/zenhttp/servers/httpparser.cpp b/src/zenhttp/servers/httpparser.cpp
index 9bb354a5e..93094e21b 100644
--- a/src/zenhttp/servers/httpparser.cpp
+++ b/src/zenhttp/servers/httpparser.cpp
@@ -6,6 +6,8 @@
#include <zencore/logging.h>
#include <zencore/string.h>
+#include <limits>
+
namespace zen {
using namespace std::literals;
@@ -69,23 +71,21 @@ HttpRequestParser::ConsumeData(const char* InputData, size_t DataSize)
int
HttpRequestParser::OnUrl(const char* Data, size_t Bytes)
{
- if (!m_Url)
+ const size_t RemainingBufferSpace = std::numeric_limits<std::uint32_t>::max() - m_HeaderData.size();
+ if (RemainingBufferSpace < Bytes)
{
- ZEN_ASSERT_SLOW(m_UrlLength == 0);
- m_Url = m_HeaderCursor;
+ ZEN_WARN("HTTP parser does not have enough space for incoming request headers, need {} more bytes", Bytes - RemainingBufferSpace);
+ return 1;
}
- const size_t RemainingBufferSpace = sizeof m_HeaderBuffer + m_HeaderBuffer - m_HeaderCursor;
-
- if (RemainingBufferSpace < Bytes)
+ if (m_UrlRange.Length == 0)
{
- ZEN_WARN("HTTP parser does not have enough space for incoming request, need {} more bytes", Bytes - RemainingBufferSpace);
- return 1;
+ ZEN_ASSERT_SLOW(m_UrlRange.Offset == 0);
+ m_UrlRange.Offset = (uint32_t)m_HeaderData.size();
}
- memcpy(m_HeaderCursor, Data, Bytes);
- m_HeaderCursor += Bytes;
- m_UrlLength += Bytes;
+ m_HeaderData.insert(m_HeaderData.end(), Data, &Data[Bytes]);
+ m_UrlRange.Length += (uint32_t)Bytes;
return 0;
}
@@ -93,59 +93,66 @@ HttpRequestParser::OnUrl(const char* Data, size_t Bytes)
int
HttpRequestParser::OnHeader(const char* Data, size_t Bytes)
{
- if (m_CurrentHeaderValueLength)
+ const size_t RemainingBufferSpace = std::numeric_limits<std::uint32_t>::max() - m_HeaderData.size();
+ if (RemainingBufferSpace < Bytes)
{
- AppendCurrentHeader();
+ ZEN_WARN("HTTP parser does not have enough space for incoming request headers, need {} more bytes", Bytes - RemainingBufferSpace);
+ return 1;
+ }
- m_CurrentHeaderNameLength = 0;
- m_CurrentHeaderValueLength = 0;
- m_CurrentHeaderName = m_HeaderCursor;
+ if (m_HeaderEntries.empty())
+ {
+ m_HeaderEntries.resize(1);
}
- else if (m_CurrentHeaderName == nullptr)
+ HeaderEntry* CurrentHeaderEntry = &m_HeaderEntries.back();
+ if (CurrentHeaderEntry->ValueRange.Length)
{
- m_CurrentHeaderName = m_HeaderCursor;
+ ParseCurrentHeader();
+ m_HeaderEntries.emplace_back(HeaderEntry{.NameRange = {.Offset = (uint32_t)m_HeaderData.size()}});
+ CurrentHeaderEntry = &m_HeaderEntries.back();
}
-
- const size_t RemainingBufferSpace = sizeof m_HeaderBuffer + m_HeaderBuffer - m_HeaderCursor;
- if (RemainingBufferSpace < Bytes)
+ else if (CurrentHeaderEntry->NameRange.Length == 0)
{
- ZEN_WARN("HTTP parser does not have enough space for incoming header name, need {} more bytes", Bytes - RemainingBufferSpace);
- return 1;
+ m_HeaderEntries.emplace_back(HeaderEntry{.NameRange = {.Offset = (uint32_t)m_HeaderData.size()}});
+ CurrentHeaderEntry = &m_HeaderEntries.back();
}
- memcpy(m_HeaderCursor, Data, Bytes);
- m_HeaderCursor += Bytes;
- m_CurrentHeaderNameLength += Bytes;
+ m_HeaderData.insert(m_HeaderData.end(), Data, &Data[Bytes]);
+ CurrentHeaderEntry->NameRange.Length += (uint32_t)Bytes;
return 0;
}
void
-HttpRequestParser::AppendCurrentHeader()
+HttpRequestParser::ParseCurrentHeader()
{
- std::string_view HeaderName(m_CurrentHeaderName, m_CurrentHeaderNameLength);
- if (m_Headers.size() == std::numeric_limits<int8_t>::max())
+ ZEN_ASSERT_SLOW(!m_HeaderEntries.empty());
+ const HeaderEntry& CurrentHeaderEntry = m_HeaderEntries.back();
+ const size_t CurrentHeaderCount = m_HeaderEntries.size();
+ const std::string_view HeaderName(GetHeaderSubString(CurrentHeaderEntry.NameRange));
+ if (CurrentHeaderCount > std::numeric_limits<int8_t>::max())
{
ZEN_WARN("HttpRequestParser parser only supports up to {} headers, can't store header '{}'. Dropping it.",
std::numeric_limits<int8_t>::max(),
HeaderName);
return;
}
- std::string_view HeaderValue(m_CurrentHeaderValue, m_CurrentHeaderValueLength);
+ const std::string_view HeaderValue(GetHeaderSubString(CurrentHeaderEntry.ValueRange));
- const uint32_t HeaderHash = HashStringAsLowerDjb2(HeaderName);
+ const uint32_t HeaderHash = HashStringAsLowerDjb2(HeaderName);
+ const int8_t CurrentHeaderIndex = int8_t(CurrentHeaderCount - 1);
if (HeaderHash == HashContentLength)
{
- m_ContentLengthHeaderIndex = (int8_t)m_Headers.size();
+ m_ContentLengthHeaderIndex = CurrentHeaderIndex;
}
else if (HeaderHash == HashAccept)
{
- m_AcceptHeaderIndex = (int8_t)m_Headers.size();
+ m_AcceptHeaderIndex = CurrentHeaderIndex;
}
else if (HeaderHash == HashContentType)
{
- m_ContentTypeHeaderIndex = (int8_t)m_Headers.size();
+ m_ContentTypeHeaderIndex = CurrentHeaderIndex;
}
else if (HeaderHash == HashSession)
{
@@ -169,38 +176,38 @@ HttpRequestParser::AppendCurrentHeader()
}
else if (HeaderHash == HashRange)
{
- m_RangeHeaderIndex = (int8_t)m_Headers.size();
+ m_RangeHeaderIndex = CurrentHeaderIndex;
}
-
- m_Headers.emplace_back(HeaderName, HeaderValue);
}
int
HttpRequestParser::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;
+ const size_t RemainingBufferSpace = std::numeric_limits<std::uint32_t>::max() - m_HeaderData.size();
if (RemainingBufferSpace < Bytes)
{
- ZEN_WARN("HTTP parser does not have enough space for incoming header value, need {} more bytes", Bytes - RemainingBufferSpace);
+ ZEN_WARN("HTTP parser does not have enough space for incoming request headers, need {} more bytes", Bytes - RemainingBufferSpace);
return 1;
}
- memcpy(m_HeaderCursor, Data, Bytes);
- m_HeaderCursor += Bytes;
- m_CurrentHeaderValueLength += Bytes;
+ ZEN_ASSERT_SLOW(!m_HeaderEntries.empty());
+ HeaderEntry& CurrentHeaderEntry = m_HeaderEntries.back();
+ if (CurrentHeaderEntry.ValueRange.Length == 0)
+ {
+ CurrentHeaderEntry.ValueRange.Offset = (uint32_t)m_HeaderData.size();
+ }
+ m_HeaderData.insert(m_HeaderData.end(), Data, &Data[Bytes]);
+ CurrentHeaderEntry.ValueRange.Length += (uint32_t)Bytes;
return 0;
}
static void
-NormalizeUrlPath(const char* Url, size_t UrlLength, std::string& NormalizedUrl)
+NormalizeUrlPath(std::string_view InUrl, std::string& NormalizedUrl)
{
- bool LastCharWasSeparator = false;
+ bool LastCharWasSeparator = false;
+ const char* Url = InUrl.data();
+ const size_t UrlLength = InUrl.length();
for (std::string_view::size_type UrlIndex = 0; UrlIndex < UrlLength; ++UrlIndex)
{
const char UrlChar = Url[UrlIndex];
@@ -233,9 +240,13 @@ HttpRequestParser::OnHeadersComplete()
{
try
{
- if (m_CurrentHeaderValueLength)
+ if (!m_HeaderEntries.empty())
{
- AppendCurrentHeader();
+ HeaderEntry& CurrentHeaderEntry = m_HeaderEntries.back();
+ if (CurrentHeaderEntry.NameRange.Length)
+ {
+ ParseCurrentHeader();
+ }
}
m_KeepAlive = !!http_should_keep_alive(&m_Parser);
@@ -275,21 +286,21 @@ HttpRequestParser::OnHeadersComplete()
break;
}
- std::string_view Url(m_Url, m_UrlLength);
+ std::string_view FullUrl(GetHeaderSubString(m_UrlRange));
- if (auto QuerySplit = Url.find_first_of('?'); QuerySplit != std::string_view::npos)
+ if (auto QuerySplit = FullUrl.find_first_of('?'); QuerySplit != std::string_view::npos)
{
- m_UrlLength = QuerySplit;
- m_QueryString = m_Url + QuerySplit + 1;
- m_QueryLength = Url.size() - QuerySplit - 1;
+ m_UrlRange.Length = uint32_t(QuerySplit);
+ m_QueryStringRange = {.Offset = uint32_t(m_UrlRange.Offset + QuerySplit + 1),
+ .Length = uint32_t(FullUrl.size() - QuerySplit - 1)};
}
- NormalizeUrlPath(m_Url, m_UrlLength, m_NormalizedUrl);
+ NormalizeUrlPath(FullUrl, m_NormalizedUrl);
- if (m_ContentLengthHeaderIndex >= 0)
+ std::string_view Value = GetHeaderValue(m_ContentLengthHeaderIndex);
+ if (!Value.empty())
{
- std::string_view& Value = m_Headers[m_ContentLengthHeaderIndex].Value;
- uint64_t ContentLength = 0;
+ uint64_t ContentLength = 0;
std::from_chars(Value.data(), Value.data() + Value.size(), ContentLength);
if (ContentLength)
@@ -337,15 +348,11 @@ HttpRequestParser::OnBody(const char* Data, size_t Bytes)
void
HttpRequestParser::ResetState()
{
- m_HeaderCursor = m_HeaderBuffer;
- m_CurrentHeaderName = nullptr;
- m_CurrentHeaderNameLength = 0;
- m_CurrentHeaderValue = nullptr;
- m_CurrentHeaderValueLength = 0;
- m_Url = nullptr;
- m_UrlLength = 0;
- m_QueryString = nullptr;
- m_QueryLength = 0;
+ m_UrlRange = {};
+ m_QueryStringRange = {};
+
+ m_HeaderEntries.clear();
+
m_ContentLengthHeaderIndex = -1;
m_AcceptHeaderIndex = -1;
m_ContentTypeHeaderIndex = -1;
@@ -353,7 +360,8 @@ HttpRequestParser::ResetState()
m_Expect100Continue = false;
m_BodyBuffer = {};
m_BodyPosition = 0;
- m_Headers.clear();
+
+ m_HeaderData.clear();
m_NormalizedUrl.clear();
}