From 6ec26c46d694a1d5291790a9c70bec25dce4b513 Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Thu, 9 Sep 2021 15:14:08 +0200 Subject: Factored out http server related code into zenhttp module since it feels out of place in zencore --- zenhttp/httpserver.cpp | 389 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 zenhttp/httpserver.cpp (limited to 'zenhttp/httpserver.cpp') diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp new file mode 100644 index 000000000..a0b17fe44 --- /dev/null +++ b/zenhttp/httpserver.cpp @@ -0,0 +1,389 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include + +#include "httpsys.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace zen { + +HttpServerRequest::HttpServerRequest() +{ +} + +HttpServerRequest::~HttpServerRequest() +{ +} + +struct CbPackageHeader +{ + uint32_t HeaderMagic; + uint32_t AttachmentCount; + uint32_t Reserved1; + uint32_t Reserved2; +}; + +static constinit uint32_t kCbPkgMagic = 0xaa77aacc; + +struct CbAttachmentEntry +{ + uint64_t AttachmentSize; + uint32_t Reserved1; + IoHash AttachmentHash; +}; + +void +HttpServerRequest::WriteResponse(HttpResponse HttpResponseCode, CbPackage Data) +{ + const std::span& Attachments = Data.GetAttachments(); + + std::vector ResponseBuffers; + ResponseBuffers.reserve(3 + Attachments.size()); // TODO: may want to use an additional fudge factor here to avoid growing since each + // attachment is likely to consist of several buffers + + uint64_t TotalAttachmentsSize = 0; + + // Fixed size header + + CbPackageHeader Hdr{.HeaderMagic = kCbPkgMagic, .AttachmentCount = gsl::narrow(Attachments.size())}; + + ResponseBuffers.push_back(IoBufferBuilder::MakeCloneFromMemory(&Hdr, sizeof Hdr)); + + // Attachment metadata array + + IoBuffer AttachmentMetadataBuffer = IoBuffer{sizeof(CbAttachmentEntry) * (Attachments.size() + /* root */ 1)}; + + CbAttachmentEntry* AttachmentInfo = reinterpret_cast(AttachmentMetadataBuffer.MutableData()); + + ResponseBuffers.push_back(AttachmentMetadataBuffer); // Attachment metadata + + // Root object + + IoBuffer RootIoBuffer = Data.GetObject().GetBuffer().AsIoBuffer(); + ResponseBuffers.push_back(RootIoBuffer); // Root object + + *AttachmentInfo++ = {.AttachmentSize = RootIoBuffer.Size(), .AttachmentHash = Data.GetObjectHash()}; + + // Attachment payloads + + for (const CbAttachment& Attachment : Attachments) + { + CompressedBuffer AttachmentBuffer = Attachment.AsCompressedBinary(); + CompositeBuffer Compressed = AttachmentBuffer.GetCompressed(); + + *AttachmentInfo++ = {.AttachmentSize = AttachmentBuffer.GetCompressedSize(), + .AttachmentHash = IoHash::FromBLAKE3(AttachmentBuffer.GetRawHash())}; + + for (const SharedBuffer& Segment : Compressed.GetSegments()) + { + ResponseBuffers.push_back(Segment.AsIoBuffer()); + TotalAttachmentsSize += Segment.GetSize(); + } + } + + return WriteResponse(HttpResponseCode, HttpContentType::kCbPackage, ResponseBuffers); +} + +void +HttpServerRequest::WriteResponse(HttpResponse HttpResponseCode, CbObject Data) +{ + SharedBuffer Buf = Data.GetBuffer(); + std::array Buffers{IoBufferBuilder::MakeCloneFromMemory(Buf.GetData(), Buf.GetSize())}; + return WriteResponse(HttpResponseCode, HttpContentType::kCbObject, Buffers); +} + +void +HttpServerRequest::WriteResponse(HttpResponse HttpResponseCode, HttpContentType ContentType, std::string_view ResponseString) +{ + return WriteResponse(HttpResponseCode, ContentType, std::u8string_view{(char8_t*)ResponseString.data(), ResponseString.size()}); +} + +void +HttpServerRequest::WriteResponse(HttpResponse HttpResponseCode, HttpContentType ContentType, IoBuffer Blob) +{ + std::array Buffers{Blob}; + return WriteResponse(HttpResponseCode, ContentType, Buffers); +} + +HttpServerRequest::QueryParams +HttpServerRequest::GetQueryParams() +{ + QueryParams Params; + + const std::string_view QStr = QueryString(); + + const char* QueryIt = QStr.data(); + const char* QueryEnd = QueryIt + QStr.size(); + + while (QueryIt != QueryEnd) + { + if (*QueryIt == '&') + { + ++QueryIt; + continue; + } + + const std::string_view Query{QueryIt, QueryEnd}; + + size_t DelimIndex = Query.find('&', 0); + + if (DelimIndex == std::string_view::npos) + { + DelimIndex = Query.size(); + } + + std::string_view ThisQuery{QueryIt, DelimIndex}; + + size_t EqIndex = ThisQuery.find('=', 0); + + if (EqIndex != std::string_view::npos) + { + std::string_view Parm{ThisQuery.data(), EqIndex}; + ThisQuery.remove_prefix(EqIndex + 1); + + Params.KvPairs.emplace_back(Parm, ThisQuery); + } + + QueryIt += DelimIndex; + } + + return Params; +} + +CbObject +HttpServerRequest::ReadPayloadObject() +{ + IoBuffer Payload = ReadPayload(); + + if (Payload) + { + return LoadCompactBinaryObject(std::move(Payload)); + } + else + { + return {}; + } +} + +CbPackage +HttpServerRequest::ReadPayloadPackage() +{ + // TODO: this should not read into a contiguous buffer! + + IoBuffer Payload = ReadPayload(); + MemoryInStream InStream(Payload); + BinaryReader Reader(InStream); + + if (!Payload) + { + return {}; + } + + CbPackage Package; + + CbPackageHeader Hdr; + Reader.Read(&Hdr, sizeof Hdr); + + if (Hdr.HeaderMagic != kCbPkgMagic) + { + // report error + return {}; + } + + uint32_t ChunkCount = Hdr.AttachmentCount + 1; + + std::unique_ptr AttachmentEntries{new CbAttachmentEntry[ChunkCount]}; + + Reader.Read(AttachmentEntries.get(), sizeof(CbAttachmentEntry) * ChunkCount); + + for (uint32_t i = 0; i < ChunkCount; ++i) + { + const uint64_t AttachmentSize = AttachmentEntries[i].AttachmentSize; + IoBuffer AttachmentBuffer{AttachmentSize}; + Reader.Read(AttachmentBuffer.MutableData(), AttachmentSize); + CompressedBuffer CompBuf(CompressedBuffer::FromCompressed(SharedBuffer(AttachmentBuffer))); + + if (i == 0) + { + Package.SetObject(LoadCompactBinaryObject(CompBuf)); + } + else + { + CbAttachment Attachment(CompBuf); + Package.AddAttachment(Attachment); + } + } + + return Package; +} + +////////////////////////////////////////////////////////////////////////// + +HttpServerException::HttpServerException(const char* Message, uint32_t Error) : m_ErrorCode(Error) +{ + using namespace fmt::literals; + + m_Message = "{} (HTTP error {})"_format(Message, m_ErrorCode); +} + +const char* +HttpServerException::what() const +{ + return m_Message.c_str(); +} + +////////////////////////////////////////////////////////////////////////// + +void +HttpRequestRouter::AddPattern(const char* Id, const char* Regex) +{ + ZEN_ASSERT(m_PatternMap.find(Id) == m_PatternMap.end()); + + m_PatternMap.insert({Id, Regex}); +} + +void +HttpRequestRouter::RegisterRoute(const char* Regex, HttpRequestRouter::HandlerFunc_t&& HandlerFunc, HttpVerb SupportedVerbs) +{ + ExtendableStringBuilder<128> ExpandedRegex; + ProcessRegexSubstitutions(Regex, ExpandedRegex); + + m_Handlers.emplace_back(ExpandedRegex.c_str(), SupportedVerbs, std::move(HandlerFunc), Regex); +} + +void +HttpRequestRouter::RegisterRoute(const char* Regex, PackageEndpointHandler& Handler) +{ + ExtendableStringBuilder<128> ExpandedRegex; + ProcessRegexSubstitutions(Regex, ExpandedRegex); + + m_Handlers.emplace_back( + ExpandedRegex.c_str(), + HttpVerb::kPost, + [&Handler](HttpRouterRequest& Request) { Handler.HandleRequest(Request); }, + Regex); +} + +void +HttpRequestRouter::ProcessRegexSubstitutions(const char* Regex, StringBuilderBase& OutExpandedRegex) +{ + size_t RegexLen = strlen(Regex); + + for (size_t i = 0; i < RegexLen;) + { + bool matched = false; + + if (Regex[i] == '{' && ((i == 0) || (Regex[i - 1] != '\\'))) + { + // Might have a pattern reference - find closing brace + + for (size_t j = i + 1; j < RegexLen; ++j) + { + if (Regex[j] == '}') + { + std::string Pattern(&Regex[i + 1], j - i - 1); + + if (auto it = m_PatternMap.find(Pattern); it != m_PatternMap.end()) + { + OutExpandedRegex.Append(it->second.c_str()); + } + else + { + // Default to anything goes (or should this just be an error?) + + OutExpandedRegex.Append("(.+?)"); + } + + // skip ahead + i = j + 1; + + matched = true; + + break; + } + } + } + + if (!matched) + { + OutExpandedRegex.Append(Regex[i++]); + } + } +} + +bool +HttpRequestRouter::HandleRequest(zen::HttpServerRequest& Request) +{ + const HttpVerb Verb = Request.RequestVerb(); + + std::string_view Uri = Request.RelativeUri(); + HttpRouterRequest RouterRequest(Request); + + for (const auto& Handler : m_Handlers) + { + if ((Handler.Verbs & Verb) == Verb && regex_match(begin(Uri), end(Uri), RouterRequest.m_Match, Handler.RegEx)) + { + Handler.Handler(RouterRequest); + + return true; // Route matched + } + } + + return false; // No route matched +} + +////////////////////////////////////////////////////////////////////////// + +Ref +CreateHttpServer() +{ + return new HttpSysServer{32}; +} + +////////////////////////////////////////////////////////////////////////// + +TEST_CASE("http") +{ + using namespace std::literals; + + SUBCASE("router") + { + HttpRequestRouter r; + r.AddPattern("a", "[[:alpha:]]+"); + r.RegisterRoute( + "{a}", + [&](auto) {}, + HttpVerb::kGet); + + // struct TestHttpServerRequest : public HttpServerRequest + //{ + // TestHttpServerRequest(std::string_view Uri) : m_uri{Uri} {} + //}; + + // TestHttpServerRequest req{}; + // r.HandleRequest(req); + } +} + +void +http_forcelink() +{ +} + +} // namespace zen -- cgit v1.2.3 From a73862b954209fd1adce0f07b8f80b8cf27f2486 Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Thu, 9 Sep 2021 16:29:24 +0200 Subject: Added compile time logic to toggle http.sys / null http implementation on/off --- zenhttp/httpserver.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'zenhttp/httpserver.cpp') diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index a0b17fe44..3999b3b6c 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -3,6 +3,7 @@ #include #include "httpsys.h" +#include "httpnull.h" #include #include @@ -353,7 +354,11 @@ HttpRequestRouter::HandleRequest(zen::HttpServerRequest& Request) Ref CreateHttpServer() { +#if ZEN_WITH_HTTPSYS return new HttpSysServer{32}; +#else + return new HttpNullServer; +#endif } ////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3 From 46aa14744ba72873975d288b568fa3b15d196a78 Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Thu, 9 Sep 2021 20:46:22 +0200 Subject: clang-format --- zenhttp/httpserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'zenhttp/httpserver.cpp') diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index 3999b3b6c..f281185b7 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -2,8 +2,8 @@ #include -#include "httpsys.h" #include "httpnull.h" +#include "httpsys.h" #include #include -- cgit v1.2.3 From 894c0f44991e9c5e8328e02aaf8e352b05990ab1 Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Fri, 10 Sep 2021 18:53:01 +0200 Subject: Added MapContentTypeToString() helper function --- zenhttp/httpserver.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'zenhttp/httpserver.cpp') diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index f281185b7..6aca43d32 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -24,6 +24,35 @@ namespace zen { +using namespace std::literals; + +std::string_view +MapContentTypeToString(HttpContentType ContentType) +{ + switch (ContentType) + { + default: + case HttpContentType::kUnknownContentType: + case HttpContentType::kBinary: + return "application/octet-stream"sv; + + case HttpContentType::kText: + return "text/plain"sv; + + case HttpContentType::kJSON: + return "application/json"sv; + + case HttpContentType::kCbObject: + return "application/x-ue-cb"sv; + + case HttpContentType::kCbPackage: + return "application/x-ue-cbpkg"sv; + + case HttpContentType::kYAML: + return "text/yaml"sv; + } +} + HttpServerRequest::HttpServerRequest() { } -- cgit v1.2.3 From e04a36f90f9e326a2e77237ba4e18399a8f60172 Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Fri, 10 Sep 2021 19:48:26 +0200 Subject: Added beginnings of a uWS http front-end --- zenhttp/httpserver.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'zenhttp/httpserver.cpp') diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index f281185b7..d1893dc6e 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -4,6 +4,7 @@ #include "httpnull.h" #include "httpsys.h" +#include "httpuws.h" #include #include @@ -354,7 +355,9 @@ HttpRequestRouter::HandleRequest(zen::HttpServerRequest& Request) Ref CreateHttpServer() { -#if ZEN_WITH_HTTPSYS +#if 1 + return new HttpUwsServer; +#elif ZEN_WITH_HTTPSYS return new HttpSysServer{32}; #else return new HttpNullServer; -- cgit v1.2.3 From 7efd2e6fe9d0a4b8afd0ddb7674af2d87abe9aaa Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Fri, 10 Sep 2021 22:06:12 +0200 Subject: Tweaked back default to http.sys --- zenhttp/httpserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'zenhttp/httpserver.cpp') diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index 73345ac7e..1012a16d3 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -384,7 +384,7 @@ HttpRequestRouter::HandleRequest(zen::HttpServerRequest& Request) Ref CreateHttpServer() { -#if 1 +#if 0 return new HttpUwsServer; #elif ZEN_WITH_HTTPSYS return new HttpSysServer{32}; -- cgit v1.2.3 From 84713500bf761b987fa0f0f4bcfa392bd7cda8a9 Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Sat, 11 Sep 2021 17:29:08 +0200 Subject: Changed worker thread defaults to reflect available hardware concurrency --- zenhttp/httpserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'zenhttp/httpserver.cpp') diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index 1012a16d3..b11feacd5 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -387,7 +387,7 @@ CreateHttpServer() #if 0 return new HttpUwsServer; #elif ZEN_WITH_HTTPSYS - return new HttpSysServer{32}; + return new HttpSysServer{std::thread::hardware_concurrency()}; #else return new HttpNullServer; #endif -- cgit v1.2.3 From 822b0b1cb3868fdfc2b7159cdbf11c3df776c9dd Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Sun, 12 Sep 2021 11:51:29 +0200 Subject: HttpResponse enum -> HttpResponseCode Also removed initial CbPackage API HttpServer changes as I have decided to take a different approach --- zenhttp/httpserver.cpp | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) (limited to 'zenhttp/httpserver.cpp') diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index b11feacd5..e283f31c9 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -80,7 +80,7 @@ struct CbAttachmentEntry }; void -HttpServerRequest::WriteResponse(HttpResponse HttpResponseCode, CbPackage Data) +HttpServerRequest::WriteResponse(HttpResponseCode ResponseCode, CbPackage Data) { const std::span& Attachments = Data.GetAttachments(); @@ -128,28 +128,28 @@ HttpServerRequest::WriteResponse(HttpResponse HttpResponseCode, CbPackage Data) } } - return WriteResponse(HttpResponseCode, HttpContentType::kCbPackage, ResponseBuffers); + return WriteResponse(ResponseCode, HttpContentType::kCbPackage, ResponseBuffers); } void -HttpServerRequest::WriteResponse(HttpResponse HttpResponseCode, CbObject Data) +HttpServerRequest::WriteResponse(HttpResponseCode ResponseCode, CbObject Data) { SharedBuffer Buf = Data.GetBuffer(); std::array Buffers{IoBufferBuilder::MakeCloneFromMemory(Buf.GetData(), Buf.GetSize())}; - return WriteResponse(HttpResponseCode, HttpContentType::kCbObject, Buffers); + return WriteResponse(ResponseCode, HttpContentType::kCbObject, Buffers); } void -HttpServerRequest::WriteResponse(HttpResponse HttpResponseCode, HttpContentType ContentType, std::string_view ResponseString) +HttpServerRequest::WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, std::string_view ResponseString) { - return WriteResponse(HttpResponseCode, ContentType, std::u8string_view{(char8_t*)ResponseString.data(), ResponseString.size()}); + return WriteResponse(ResponseCode, ContentType, std::u8string_view{(char8_t*)ResponseString.data(), ResponseString.size()}); } void -HttpServerRequest::WriteResponse(HttpResponse HttpResponseCode, HttpContentType ContentType, IoBuffer Blob) +HttpServerRequest::WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, IoBuffer Blob) { std::array Buffers{Blob}; - return WriteResponse(HttpResponseCode, ContentType, Buffers); + return WriteResponse(ResponseCode, ContentType, Buffers); } HttpServerRequest::QueryParams @@ -298,19 +298,6 @@ HttpRequestRouter::RegisterRoute(const char* Regex, HttpRequestRouter::HandlerFu m_Handlers.emplace_back(ExpandedRegex.c_str(), SupportedVerbs, std::move(HandlerFunc), Regex); } -void -HttpRequestRouter::RegisterRoute(const char* Regex, PackageEndpointHandler& Handler) -{ - ExtendableStringBuilder<128> ExpandedRegex; - ProcessRegexSubstitutions(Regex, ExpandedRegex); - - m_Handlers.emplace_back( - ExpandedRegex.c_str(), - HttpVerb::kPost, - [&Handler](HttpRouterRequest& Request) { Handler.HandleRequest(Request); }, - Regex); -} - void HttpRequestRouter::ProcessRegexSubstitutions(const char* Regex, StringBuilderBase& OutExpandedRegex) { -- cgit v1.2.3 From 835a7d00a9da37b07cdf450899ac7fd0125b3320 Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Sun, 12 Sep 2021 13:42:23 +0200 Subject: Some error handling improvements in zenhttp Primarily replaces some exception usage with std::error_code --- zenhttp/httpserver.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'zenhttp/httpserver.cpp') diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index e283f31c9..e9baf2e49 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -266,17 +266,10 @@ HttpServerRequest::ReadPayloadPackage() ////////////////////////////////////////////////////////////////////////// -HttpServerException::HttpServerException(const char* Message, uint32_t Error) : m_ErrorCode(Error) -{ - using namespace fmt::literals; - - m_Message = "{} (HTTP error {})"_format(Message, m_ErrorCode); -} - const char* HttpServerException::what() const { - return m_Message.c_str(); + return m_What.c_str(); } ////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3 From fa1f144a4eb7d201ef84b8d369d40f26fc73dff6 Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Sun, 12 Sep 2021 13:46:57 +0200 Subject: Eliminated HttpServerException and related classes --- zenhttp/httpserver.cpp | 8 -------- 1 file changed, 8 deletions(-) (limited to 'zenhttp/httpserver.cpp') diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index e9baf2e49..75678d433 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -266,14 +266,6 @@ HttpServerRequest::ReadPayloadPackage() ////////////////////////////////////////////////////////////////////////// -const char* -HttpServerException::what() const -{ - return m_What.c_str(); -} - -////////////////////////////////////////////////////////////////////////// - void HttpRequestRouter::AddPattern(const char* Id, const char* Regex) { -- cgit v1.2.3 From 1d6aeff046a8f9f3df564163b56e927096decc39 Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Mon, 13 Sep 2021 10:07:30 +0200 Subject: Implemented generic CbPackage attachments filtering Package transmission will also need to be updated (up next) for the new scheme to be effective --- zenhttp/httpserver.cpp | 212 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) (limited to 'zenhttp/httpserver.cpp') diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index 75678d433..39bec435d 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -49,11 +49,211 @@ MapContentTypeToString(HttpContentType ContentType) case HttpContentType::kCbPackage: return "application/x-ue-cbpkg"sv; + case HttpContentType::kCbPackageOffer: + return "application/x-ue-offer"sv; + case HttpContentType::kYAML: return "text/yaml"sv; } } +static const uint32_t HashBinary = HashStringDjb2("application/octet-stream"sv); +static const uint32_t HashJson = HashStringDjb2("application/json"sv); +static const uint32_t HashYaml = HashStringDjb2("text/yaml"sv); +static const uint32_t HashText = HashStringDjb2("text/plain"sv); +static const uint32_t HashCompactBinary = HashStringDjb2("application/x-ue-cb"sv); +static const uint32_t HashCompactBinaryPackage = HashStringDjb2("application/x-ue-cbpkg"sv); +static const uint32_t HashCompactBinaryPackageOffer = HashStringDjb2("application/x-ue-offer"sv); + +HttpContentType +ParseContentType(const std::string_view& ContentTypeString) +{ + if (!ContentTypeString.empty()) + { + const uint32_t CtHash = HashStringDjb2(ContentTypeString); + + if (CtHash == HashBinary) + { + return HttpContentType::kBinary; + } + else if (CtHash == HashCompactBinary) + { + return HttpContentType::kCbObject; + } + else if (CtHash == HashCompactBinaryPackage) + { + return HttpContentType::kCbPackage; + } + else if (CtHash == HashCompactBinaryPackageOffer) + { + return HttpContentType::kCbPackageOffer; + } + else if (CtHash == HashJson) + { + return HttpContentType::kJSON; + } + else if (CtHash == HashYaml) + { + return HttpContentType::kYAML; + } + else if (CtHash == HashText) + { + return HttpContentType::kText; + } + } + + return HttpContentType::kUnknownContentType; +} + +const char* +ReasonStringForHttpResultCode(int HttpCode) +{ + switch (HttpCode) + { + // 1xx Informational + + case 100: + return "Continue"; + case 101: + return "Switching Protocols"; + + // 2xx Success + + case 200: + return "OK"; + case 201: + return "Created"; + case 202: + return "Accepted"; + case 204: + return "No Content"; + case 205: + return "Reset Content"; + case 206: + return "Partial Content"; + + // 3xx Redirection + + case 300: + return "Multiple Choices"; + case 301: + return "Moved Permanently"; + case 302: + return "Found"; + case 303: + return "See Other"; + case 304: + return "Not Modified"; + case 305: + return "Use Proxy"; + case 306: + return "Switch Proxy"; + case 307: + return "Temporary Redirect"; + case 308: + return "Permanent Redirect"; + + // 4xx Client errors + + case 400: + return "Bad Request"; + case 401: + return "Unauthorized"; + case 402: + return "Payment Required"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 405: + return "Method Not Allowed"; + case 406: + return "Not Acceptable"; + case 407: + return "Proxy Authentication Required"; + case 408: + return "Request Timeout"; + case 409: + return "Conflict"; + case 410: + return "Gone"; + case 411: + return "Length Required"; + case 412: + return "Precondition Failed"; + case 413: + return "Payload Too Large"; + case 414: + return "URI Too Long"; + case 415: + return "Unsupported Media Type"; + case 416: + return "Range Not Satisifiable"; + case 417: + return "Expectation Failed"; + case 418: + return "I'm a teapot"; + case 421: + return "Misdirected Request"; + case 422: + return "Unprocessable Entity"; + case 423: + return "Locked"; + case 424: + return "Failed Dependency"; + case 425: + return "Too Early"; + case 426: + return "Upgrade Required"; + case 428: + return "Precondition Required"; + case 429: + return "Too Many Requests"; + case 431: + return "Request Header Fields Too Large"; + + // 5xx Server errors + + case 500: + return "Internal Server Error"; + case 501: + return "Not Implemented"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + case 504: + return "Gateway Timeout"; + case 505: + return "HTTP Version Not Supported"; + case 506: + return "Variant Also Negotiates"; + case 507: + return "Insufficient Storage"; + case 508: + return "Loop Detected"; + case 510: + return "Not Extended"; + case 511: + return "Network Authentication Required"; + + default: + return "Unknown Result"; + } +} + +////////////////////////////////////////////////////////////////////////// + +Ref +HttpService::HandlePackageRequest(HttpServerRequest& HttpServiceRequest) +{ + ZEN_UNUSED(HttpServiceRequest); + + return nullptr; +} + +////////////////////////////////////////////////////////////////////////// + HttpServerRequest::HttpServerRequest() { } @@ -197,6 +397,18 @@ HttpServerRequest::GetQueryParams() return Params; } +Oid +HttpServerRequest::SessionId() const +{ + return {}; +} + +uint32_t +HttpServerRequest::RequestId() const +{ + return {}; +} + CbObject HttpServerRequest::ReadPayloadObject() { -- cgit v1.2.3 From 4e2649977d034b913413d2cb35d4a88afc30393f Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Mon, 13 Sep 2021 12:24:59 +0200 Subject: Changed interface for httpServerRequest::SessionId()/RequestId() so they share storage and lazy eval logic They now call into ParseSessionId()/ParseRequestId() when required Eliminates redundant logic in derived implementations Also moved package transport code into httpshared.(cpp|h) for easier sharing with client code Added some I/O error reporting in http.sys related code Changed IHttpPackageHandler interface to support partially updated handling flow --- zenhttp/httpserver.cpp | 136 +++++++++---------------------------------------- 1 file changed, 23 insertions(+), 113 deletions(-) (limited to 'zenhttp/httpserver.cpp') diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index 39bec435d..50141167d 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -3,6 +3,7 @@ #include #include "httpnull.h" +#include "httpshared.h" #include "httpsys.h" #include "httpuws.h" @@ -262,72 +263,10 @@ HttpServerRequest::~HttpServerRequest() { } -struct CbPackageHeader -{ - uint32_t HeaderMagic; - uint32_t AttachmentCount; - uint32_t Reserved1; - uint32_t Reserved2; -}; - -static constinit uint32_t kCbPkgMagic = 0xaa77aacc; - -struct CbAttachmentEntry -{ - uint64_t AttachmentSize; - uint32_t Reserved1; - IoHash AttachmentHash; -}; - void HttpServerRequest::WriteResponse(HttpResponseCode ResponseCode, CbPackage Data) { - const std::span& Attachments = Data.GetAttachments(); - - std::vector ResponseBuffers; - ResponseBuffers.reserve(3 + Attachments.size()); // TODO: may want to use an additional fudge factor here to avoid growing since each - // attachment is likely to consist of several buffers - - uint64_t TotalAttachmentsSize = 0; - - // Fixed size header - - CbPackageHeader Hdr{.HeaderMagic = kCbPkgMagic, .AttachmentCount = gsl::narrow(Attachments.size())}; - - ResponseBuffers.push_back(IoBufferBuilder::MakeCloneFromMemory(&Hdr, sizeof Hdr)); - - // Attachment metadata array - - IoBuffer AttachmentMetadataBuffer = IoBuffer{sizeof(CbAttachmentEntry) * (Attachments.size() + /* root */ 1)}; - - CbAttachmentEntry* AttachmentInfo = reinterpret_cast(AttachmentMetadataBuffer.MutableData()); - - ResponseBuffers.push_back(AttachmentMetadataBuffer); // Attachment metadata - - // Root object - - IoBuffer RootIoBuffer = Data.GetObject().GetBuffer().AsIoBuffer(); - ResponseBuffers.push_back(RootIoBuffer); // Root object - - *AttachmentInfo++ = {.AttachmentSize = RootIoBuffer.Size(), .AttachmentHash = Data.GetObjectHash()}; - - // Attachment payloads - - for (const CbAttachment& Attachment : Attachments) - { - CompressedBuffer AttachmentBuffer = Attachment.AsCompressedBinary(); - CompositeBuffer Compressed = AttachmentBuffer.GetCompressed(); - - *AttachmentInfo++ = {.AttachmentSize = AttachmentBuffer.GetCompressedSize(), - .AttachmentHash = IoHash::FromBLAKE3(AttachmentBuffer.GetRawHash())}; - - for (const SharedBuffer& Segment : Compressed.GetSegments()) - { - ResponseBuffers.push_back(Segment.AsIoBuffer()); - TotalAttachmentsSize += Segment.GetSize(); - } - } - + std::vector ResponseBuffers = FormatPackageMessage(Data); return WriteResponse(ResponseCode, HttpContentType::kCbPackage, ResponseBuffers); } @@ -400,13 +339,27 @@ HttpServerRequest::GetQueryParams() Oid HttpServerRequest::SessionId() const { - return {}; + if (m_Flags & kHaveSessionId) + { + return m_SessionId; + } + + m_SessionId = ParseSessionId(); + m_Flags |= kHaveSessionId; + return m_SessionId; } uint32_t HttpServerRequest::RequestId() const { - return {}; + if (m_Flags & kHaveRequestId) + { + return m_RequestId; + } + + m_RequestId = ParseRequestId(); + m_Flags |= kHaveRequestId; + return m_RequestId; } CbObject @@ -418,62 +371,19 @@ HttpServerRequest::ReadPayloadObject() { return LoadCompactBinaryObject(std::move(Payload)); } - else - { - return {}; - } + + return {}; } CbPackage HttpServerRequest::ReadPayloadPackage() { - // TODO: this should not read into a contiguous buffer! - - IoBuffer Payload = ReadPayload(); - MemoryInStream InStream(Payload); - BinaryReader Reader(InStream); - - if (!Payload) - { - return {}; - } - - CbPackage Package; - - CbPackageHeader Hdr; - Reader.Read(&Hdr, sizeof Hdr); - - if (Hdr.HeaderMagic != kCbPkgMagic) - { - // report error - return {}; - } - - uint32_t ChunkCount = Hdr.AttachmentCount + 1; - - std::unique_ptr AttachmentEntries{new CbAttachmentEntry[ChunkCount]}; - - Reader.Read(AttachmentEntries.get(), sizeof(CbAttachmentEntry) * ChunkCount); - - for (uint32_t i = 0; i < ChunkCount; ++i) + if (IoBuffer Payload = ReadPayload()) { - const uint64_t AttachmentSize = AttachmentEntries[i].AttachmentSize; - IoBuffer AttachmentBuffer{AttachmentSize}; - Reader.Read(AttachmentBuffer.MutableData(), AttachmentSize); - CompressedBuffer CompBuf(CompressedBuffer::FromCompressed(SharedBuffer(AttachmentBuffer))); - - if (i == 0) - { - Package.SetObject(LoadCompactBinaryObject(CompBuf)); - } - else - { - CbAttachment Attachment(CompBuf); - Package.AddAttachment(Attachment); - } + return ParsePackageMessage(Payload); } - return Package; + return {}; } ////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3 From a2fae041836cf7af8c8d7c77ddb817714fd151fd Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Mon, 13 Sep 2021 16:57:36 +0200 Subject: SImplified some payload access code --- zenhttp/httpserver.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'zenhttp/httpserver.cpp') diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index 50141167d..3effb44fc 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -365,9 +365,7 @@ HttpServerRequest::RequestId() const CbObject HttpServerRequest::ReadPayloadObject() { - IoBuffer Payload = ReadPayload(); - - if (Payload) + if (IoBuffer Payload = ReadPayload()) { return LoadCompactBinaryObject(std::move(Payload)); } @@ -380,7 +378,7 @@ HttpServerRequest::ReadPayloadPackage() { if (IoBuffer Payload = ReadPayload()) { - return ParsePackageMessage(Payload); + return ParsePackageMessage(std::move(Payload)); } return {}; -- cgit v1.2.3 From b3c260277474730c3048cf5c377e586f1acfc3d7 Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Mon, 13 Sep 2021 21:41:34 +0200 Subject: Moved some shared http definitions into httpcommon.h Added CompositeBuffer HttpServerRequest::WriteResponse() helper function --- zenhttp/httpserver.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'zenhttp/httpserver.cpp') diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index 3effb44fc..f97ac0067 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -291,6 +291,21 @@ HttpServerRequest::WriteResponse(HttpResponseCode ResponseCode, HttpContentType return WriteResponse(ResponseCode, ContentType, Buffers); } +void +HttpServerRequest::WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, CompositeBuffer& Payload) +{ + std::span Segments = Payload.GetSegments(); + + std::vector Buffers; + + for (auto& Segment : Segments) + { + Buffers.push_back(Segment.AsIoBuffer()); + } + + WriteResponse(ResponseCode, ContentType, Payload); +} + HttpServerRequest::QueryParams HttpServerRequest::GetQueryParams() { -- cgit v1.2.3