aboutsummaryrefslogtreecommitdiff
path: root/zenhttp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-04-20 12:09:47 +0200
committerStefan Boberg <[email protected]>2023-04-20 12:09:47 +0200
commit74ff3745511e80ad4529620858604890f222af74 (patch)
tree2b6c836442791d84bdedd5cc0e81154d9e36159d /zenhttp
parent#pragma once added to some headers (diff)
parentoops: clang-format (diff)
downloadzen-74ff3745511e80ad4529620858604890f222af74.tar.xz
zen-74ff3745511e80ad4529620858604890f222af74.zip
Merge branch 'main' of https://github.com/EpicGames/zen
Diffstat (limited to 'zenhttp')
-rw-r--r--zenhttp/httpasio.cpp17
-rw-r--r--zenhttp/httpserver.cpp70
-rw-r--r--zenhttp/httpsys.cpp16
-rw-r--r--zenhttp/include/zenhttp/httpcommon.h9
-rw-r--r--zenhttp/include/zenhttp/httpserver.h2
-rw-r--r--zenhttp/xmake.lua1
-rw-r--r--zenhttp/zenhttp.cpp9
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