diff options
| author | Stefan Boberg <[email protected]> | 2023-04-20 12:09:47 +0200 |
|---|---|---|
| committer | Stefan Boberg <[email protected]> | 2023-04-20 12:09:47 +0200 |
| commit | 74ff3745511e80ad4529620858604890f222af74 (patch) | |
| tree | 2b6c836442791d84bdedd5cc0e81154d9e36159d /zenhttp | |
| parent | #pragma once added to some headers (diff) | |
| parent | oops: clang-format (diff) | |
| download | zen-74ff3745511e80ad4529620858604890f222af74.tar.xz zen-74ff3745511e80ad4529620858604890f222af74.zip | |
Merge branch 'main' of https://github.com/EpicGames/zen
Diffstat (limited to 'zenhttp')
| -rw-r--r-- | zenhttp/httpasio.cpp | 17 | ||||
| -rw-r--r-- | zenhttp/httpserver.cpp | 70 | ||||
| -rw-r--r-- | zenhttp/httpsys.cpp | 16 | ||||
| -rw-r--r-- | zenhttp/include/zenhttp/httpcommon.h | 9 | ||||
| -rw-r--r-- | zenhttp/include/zenhttp/httpserver.h | 2 | ||||
| -rw-r--r-- | zenhttp/xmake.lua | 1 | ||||
| -rw-r--r-- | zenhttp/zenhttp.cpp | 9 |
7 files changed, 120 insertions, 4 deletions
diff --git a/zenhttp/httpasio.cpp b/zenhttp/httpasio.cpp index 450b5a1fc..f270c9d2b 100644 --- a/zenhttp/httpasio.cpp +++ b/zenhttp/httpasio.cpp @@ -7,6 +7,7 @@ #include <deque> #include <memory> +#include <string_view> #include <vector> ZEN_THIRD_PARTY_INCLUDES_START @@ -41,6 +42,7 @@ static constinit uint32_t HashAccept = HashStringAsLowerDjb2("Accept"sv); static constinit uint32_t HashExpect = HashStringAsLowerDjb2("Expect"sv); static constinit uint32_t HashSession = HashStringAsLowerDjb2("UE-Session"sv); static constinit uint32_t HashRequest = HashStringAsLowerDjb2("UE-Request"sv); +static constinit uint32_t HashRange = HashStringAsLowerDjb2("Range"sv); inline spdlog::logger& InitLogger() @@ -103,6 +105,7 @@ public: virtual void WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, std::span<IoBuffer> Blobs) override; virtual void WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, std::u8string_view ResponseString) override; virtual void WriteResponseAsync(std::function<void(HttpServerRequest&)>&& ContinuationHandler) override; + virtual bool TryGetRanges(HttpRanges& Ranges) override; using HttpServerRequest::WriteResponse; @@ -151,6 +154,8 @@ struct HttpRequest 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 { @@ -176,6 +181,7 @@ private: 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; @@ -740,6 +746,10 @@ HttpRequest::AppendCurrentHeader() ZEN_INFO("Unexpected expect - Expect: {}", HeaderValue); } } + else if (HeaderHash == HashRange) + { + m_RangeHeaderIndex = (int8_t)m_Headers.size(); + } m_Headers.emplace_back(HeaderName, HeaderValue); } @@ -912,6 +922,7 @@ HttpRequest::ResetState() m_ContentLengthHeaderIndex = -1; m_AcceptHeaderIndex = -1; m_ContentTypeHeaderIndex = -1; + m_RangeHeaderIndex = -1; m_Expect100Continue = false; m_BodyBuffer = {}; m_BodyPosition = 0; @@ -1178,6 +1189,12 @@ HttpAsioServerRequest::WriteResponseAsync(std::function<void(HttpServerRequest&) ContinuationHandler(*this); } +bool +HttpAsioServerRequest::TryGetRanges(HttpRanges& Ranges) +{ + return TryParseHttpRangeHeader(m_Request.RangeHeader(), Ranges); +} + ////////////////////////////////////////////////////////////////////////// HttpAsioServerImpl::HttpAsioServerImpl() diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index 840b90931..3576d9b3d 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -4,7 +4,6 @@ #include "httpasio.h" #include "httpnull.h" -#include "httpsys.h" #include <zencore/compactbinary.h> #include <zencore/compactbinarybuilder.h> @@ -185,6 +184,70 @@ ParseContentTypeInit(const std::string_view& ContentTypeString) HttpContentType (*ParseContentType)(const std::string_view& ContentTypeString) = &ParseContentTypeInit; +bool +TryParseHttpRangeHeader(std::string_view RangeHeader, HttpRanges& Ranges) +{ + if (RangeHeader.empty()) + { + return false; + } + + const size_t Count = Ranges.size(); + + std::size_t UnitDelim = RangeHeader.find_first_of('='); + if (UnitDelim == std::string_view::npos) + { + return false; + } + + // only bytes for now + std::string_view Unit = RangeHeader.substr(0, UnitDelim); + if (Unit != "bytes"sv) + { + return false; + } + + std::string_view Tokens = RangeHeader.substr(UnitDelim); + while (!Tokens.empty()) + { + // Skip =, + Tokens = Tokens.substr(1); + + size_t Delim = Tokens.find_first_of(','); + if (Delim == std::string_view::npos) + { + Delim = Tokens.length(); + } + + std::string_view Token = Tokens.substr(0, Delim); + Tokens = Tokens.substr(Delim); + + Delim = Token.find_first_of('-'); + if (Delim == std::string_view::npos) + { + return false; + } + + const auto Start = ParseInt<uint32_t>(Token.substr(0, Delim)); + const auto End = ParseInt<uint32_t>(Token.substr(Delim + 1)); + + if (Start.has_value() && End.has_value() && End.value() > Start.value()) + { + Ranges.push_back({.Start = Start.value(), .End = End.value()}); + } + else if (Start) + { + Ranges.push_back({.Start = Start.value()}); + } + else if (End) + { + Ranges.push_back({.End = End.value()}); + } + } + + return Count != Ranges.size(); +} + ////////////////////////////////////////////////////////////////////////// const std::string_view @@ -641,6 +704,9 @@ enum class HttpServerClass kHttpNull }; +// Implemented in httpsys.cpp +Ref<HttpServer> CreateHttpSysServer(int Concurrency, int BackgroundWorkerThreads); + Ref<HttpServer> CreateHttpServer(std::string_view ServerClass) { @@ -677,7 +743,7 @@ CreateHttpServer(std::string_view ServerClass) #if ZEN_WITH_HTTPSYS case HttpServerClass::kHttpSys: ZEN_INFO("using http.sys server implementation"); - return new HttpSysServer(std::thread::hardware_concurrency(), /* background worker threads */ 16); + return CreateHttpSysServer(std::thread::hardware_concurrency(), /* background worker threads */ 16); #endif case HttpServerClass::kHttpNull: diff --git a/zenhttp/httpsys.cpp b/zenhttp/httpsys.cpp index f6f8024ca..16ec135cd 100644 --- a/zenhttp/httpsys.cpp +++ b/zenhttp/httpsys.cpp @@ -188,6 +188,7 @@ public: virtual void WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, std::span<IoBuffer> Blobs) override; virtual void WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, std::u8string_view ResponseString) override; virtual void WriteResponseAsync(std::function<void(HttpServerRequest&)>&& ContinuationHandler) override; + virtual bool TryGetRanges(HttpRanges& Ranges) override; using HttpServerRequest::WriteResponse; @@ -1408,6 +1409,15 @@ HttpSysServerRequest::WriteResponseAsync(std::function<void(HttpServerRequest&)> } } +bool +HttpSysServerRequest::TryGetRanges(HttpRanges& Ranges) +{ + HTTP_REQUEST* Req = m_HttpTx.HttpRequest(); + const HTTP_KNOWN_HEADER& RangeHeader = Req->Headers.KnownHeaders[HttpHeaderRange]; + + return TryParseHttpRangeHeader({RangeHeader.pRawValue, RangeHeader.RawValueLength}, Ranges); +} + ////////////////////////////////////////////////////////////////////////// InitialRequestHandler::InitialRequestHandler(HttpSysTransaction& InRequest) : HttpSysRequestHandler(InRequest) @@ -1652,5 +1662,11 @@ HttpSysServer::RegisterService(HttpService& Service) RegisterService(Service.BaseUri(), Service); } +Ref<HttpServer> +CreateHttpSysServer(int Concurrency, int BackgroundWorkerThreads) +{ + return Ref<HttpServer>(new HttpSysServer(Concurrency, BackgroundWorkerThreads)); +} + } // namespace zen #endif diff --git a/zenhttp/include/zenhttp/httpcommon.h b/zenhttp/include/zenhttp/httpcommon.h index 3e213ece4..19fda8db4 100644 --- a/zenhttp/include/zenhttp/httpcommon.h +++ b/zenhttp/include/zenhttp/httpcommon.h @@ -17,9 +17,18 @@ class CbObject; class CbPackage; class StringBuilderBase; +struct HttpRange +{ + uint32_t Start = ~uint32_t(0); + uint32_t End = ~uint32_t(0); +}; + +using HttpRanges = std::vector<HttpRange>; + std::string_view MapContentTypeToString(HttpContentType ContentType); extern HttpContentType (*ParseContentType)(const std::string_view& ContentTypeString); std::string_view ReasonStringForHttpResultCode(int HttpCode); +bool TryParseHttpRangeHeader(std::string_view RangeHeader, HttpRanges& Ranges); [[nodiscard]] inline bool IsHttpSuccessCode(int HttpCode) diff --git a/zenhttp/include/zenhttp/httpserver.h b/zenhttp/include/zenhttp/httpserver.h index 5bd51740a..451a47b4a 100644 --- a/zenhttp/include/zenhttp/httpserver.h +++ b/zenhttp/include/zenhttp/httpserver.h @@ -59,6 +59,8 @@ public: } }; + virtual bool TryGetRanges(HttpRanges&) { return false; } + QueryParams GetQueryParams(); inline HttpVerb RequestVerb() const { return m_Verb; } diff --git a/zenhttp/xmake.lua b/zenhttp/xmake.lua index 528b72d52..b0dbdbc79 100644 --- a/zenhttp/xmake.lua +++ b/zenhttp/xmake.lua @@ -4,6 +4,7 @@ target('zenhttp') set_kind("static") add_headerfiles("**.h") add_files("**.cpp") + add_files("httpsys.cpp", {unity_ignored=true}) add_includedirs("include", {public=true}) add_deps("zencore") add_packages( diff --git a/zenhttp/zenhttp.cpp b/zenhttp/zenhttp.cpp index 0194abdcb..4bd6a5697 100644 --- a/zenhttp/zenhttp.cpp +++ b/zenhttp/zenhttp.cpp @@ -2,8 +2,11 @@ #include <zenhttp/zenhttp.h> -#include <zenhttp/httpserver.h> -#include <zenhttp/httpshared.h> +#if ZEN_WITH_TESTS + +# include <zenhttp/httpclient.h> +# include <zenhttp/httpserver.h> +# include <zenhttp/httpshared.h> namespace zen { @@ -15,3 +18,5 @@ zenhttp_forcelinktests() } } // namespace zen + +#endif |