diff options
Diffstat (limited to 'zenhttp/httpsys.cpp')
| -rw-r--r-- | zenhttp/httpsys.cpp | 406 |
1 files changed, 173 insertions, 233 deletions
diff --git a/zenhttp/httpsys.cpp b/zenhttp/httpsys.cpp index 8aa30344b..f5f9d4249 100644 --- a/zenhttp/httpsys.cpp +++ b/zenhttp/httpsys.cpp @@ -2,6 +2,8 @@ #include "httpsys.h" +#include <zencore/compactbinary.h> +#include <zencore/compactbinarybuilder.h> #include <zencore/except.h> #include <zencore/logging.h> #include <zencore/scopeguard.h> @@ -59,197 +61,14 @@ UTF8_to_wstring(const char* in) namespace zen { +using namespace std::literals; + class HttpSysServer; class HttpSysTransaction; class HttpMessageResponseRequest; - -////////////////////////////////////////////////////////////////////////// - -using namespace std::literals; - -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); - -HttpContentType -MapContentType(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 == 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"; - } -} - HttpVerb TranslateHttpVerb(HTTP_VERB ReqVerb) { @@ -296,14 +115,14 @@ HttpContentType GetContentType(const HTTP_REQUEST* HttpRequest) { const HTTP_KNOWN_HEADER& CtHdr = HttpRequest->Headers.KnownHeaders[HttpHeaderContentType]; - return MapContentType({CtHdr.pRawValue, CtHdr.RawValueLength}); + return ParseContentType({CtHdr.pRawValue, CtHdr.RawValueLength}); }; HttpContentType GetAcceptType(const HTTP_REQUEST* HttpRequest) { const HTTP_KNOWN_HEADER& CtHdr = HttpRequest->Headers.KnownHeaders[HttpHeaderAccept]; - return MapContentType({CtHdr.pRawValue, CtHdr.RawValueLength}); + return ParseContentType({CtHdr.pRawValue, CtHdr.RawValueLength}); }; /** @@ -357,11 +176,16 @@ public: HttpSysServerRequest(HttpSysTransaction& Tx, HttpService& Service, IoBuffer PayloadBuffer); ~HttpSysServerRequest() = default; + virtual Oid SessionId() const override; + virtual uint32_t RequestId() const override; + virtual IoBuffer ReadPayload() override; virtual void WriteResponse(HttpResponseCode ResponseCode) override; virtual void WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, std::span<IoBuffer> Blobs) override; virtual void WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, std::u8string_view ResponseString) override; + using HttpServerRequest::WriteResponse; + HttpSysTransaction& m_HttpTx; HttpMessageResponseRequest* m_Response = nullptr; // TODO: make this more general IoBuffer m_PayloadBuffer; @@ -400,12 +224,18 @@ public: inline HttpSysServer& Server() { return m_HttpServer; } inline HTTP_REQUEST* HttpRequest() { return m_InitialHttpHandler.HttpRequest(); } + HttpSysServerRequest& InvokeRequestHandler(HttpService& Service, IoBuffer Payload); + private: - OVERLAPPED m_HttpOverlapped{}; - HttpSysServer& m_HttpServer; - HttpSysRequestHandler* m_CompletionHandler{nullptr}; // Tracks which handler is due to handle the next I/O completion event - RwLock m_CompletionMutex; - InitialRequestHandler m_InitialHttpHandler{*this}; + OVERLAPPED m_HttpOverlapped{}; + HttpSysServer& m_HttpServer; + + // Tracks which handler is due to handle the next I/O completion event + HttpSysRequestHandler* m_CompletionHandler = nullptr; + RwLock m_CompletionMutex; + InitialRequestHandler m_InitialHttpHandler{*this}; + std::optional<HttpSysServerRequest> m_HandlerRequest; + Ref<IHttpPackageHandler> m_PackageHandler; }; ////////////////////////////////////////////////////////////////////////// @@ -435,22 +265,19 @@ public: virtual void IssueRequest(std::error_code& ErrorCode) override final; virtual HttpSysRequestHandler* HandleCompletion(ULONG IoResult, ULONG_PTR NumberOfBytesTransferred) override; - - void SuppressResponseBody(); + void SuppressResponseBody(); // typically used for HEAD requests private: std::vector<HTTP_DATA_CHUNK> m_HttpDataChunks; - uint64_t m_TotalDataSize = 0; // Sum of all chunk sizes - - uint16_t m_ResponseCode = 0; - uint32_t m_NextDataChunkOffset = 0; // This is used for responses where the number of chunks exceed the maximum number for one API call - uint32_t m_RemainingChunkCount = 0; - bool m_IsInitialResponse = true; - HttpContentType m_ContentType = HttpContentType::kBinary; - - void Initialize(uint16_t ResponseCode, std::span<IoBuffer> Blobs); - - std::vector<IoBuffer> m_DataBuffers; + uint64_t m_TotalDataSize = 0; // Sum of all chunk sizes + uint16_t m_ResponseCode = 0; + uint32_t m_NextDataChunkOffset = 0; // Cursor used for very large chunk lists + uint32_t m_RemainingChunkCount = 0; // Backlog for multi-call sends + bool m_IsInitialResponse = true; + HttpContentType m_ContentType = HttpContentType::kBinary; + std::vector<IoBuffer> m_DataBuffers; + + void InitializeForPayload(uint16_t ResponseCode, std::span<IoBuffer> Blobs); }; HttpMessageResponseRequest::HttpMessageResponseRequest(HttpSysTransaction& InRequest, uint16_t ResponseCode) @@ -458,7 +285,7 @@ HttpMessageResponseRequest::HttpMessageResponseRequest(HttpSysTransaction& InReq { std::array<IoBuffer, 0> EmptyBufferList; - Initialize(ResponseCode, EmptyBufferList); + InitializeForPayload(ResponseCode, EmptyBufferList); } HttpMessageResponseRequest::HttpMessageResponseRequest(HttpSysTransaction& InRequest, uint16_t ResponseCode, std::string_view Message) @@ -468,7 +295,7 @@ HttpMessageResponseRequest::HttpMessageResponseRequest(HttpSysTransaction& InReq IoBuffer MessageBuffer(IoBuffer::Wrap, Message.data(), Message.size()); std::array<IoBuffer, 1> SingleBufferList({MessageBuffer}); - Initialize(ResponseCode, SingleBufferList); + InitializeForPayload(ResponseCode, SingleBufferList); } HttpMessageResponseRequest::HttpMessageResponseRequest(HttpSysTransaction& InRequest, @@ -482,7 +309,7 @@ HttpMessageResponseRequest::HttpMessageResponseRequest(HttpSysTransaction& InReq IoBuffer MessageBuffer(IoBuffer::Wrap, Payload, PayloadSize); std::array<IoBuffer, 1> SingleBufferList({MessageBuffer}); - Initialize(ResponseCode, SingleBufferList); + InitializeForPayload(ResponseCode, SingleBufferList); } HttpMessageResponseRequest::HttpMessageResponseRequest(HttpSysTransaction& InRequest, @@ -490,10 +317,9 @@ HttpMessageResponseRequest::HttpMessageResponseRequest(HttpSysTransaction& InReq HttpContentType ContentType, std::span<IoBuffer> BlobList) : HttpSysRequestHandler(InRequest) +, m_ContentType(ContentType) { - Initialize(ResponseCode, BlobList); - - ZEN_UNUSED(ContentType); + InitializeForPayload(ResponseCode, BlobList); } HttpMessageResponseRequest::~HttpMessageResponseRequest() @@ -501,7 +327,7 @@ HttpMessageResponseRequest::~HttpMessageResponseRequest() } void -HttpMessageResponseRequest::Initialize(uint16_t ResponseCode, std::span<IoBuffer> BlobList) +HttpMessageResponseRequest::InitializeForPayload(uint16_t ResponseCode, std::span<IoBuffer> BlobList) { m_ResponseCode = ResponseCode; @@ -908,10 +734,10 @@ HttpSysServer::IssueNewRequestMaybe() std::unique_ptr<HttpSysTransaction> Request = std::make_unique<HttpSysTransaction>(*this); - std::error_code ec; - Request->IssueInitialRequest(ec); + std::error_code ErrorCode; + Request->IssueInitialRequest(ErrorCode); - if (ec) + if (ErrorCode) { // No request was actually issued. What is the appropriate response? @@ -1054,12 +880,12 @@ HttpSysTransaction::HandleCompletion(ULONG IoResult, ULONG_PTR NumberOfBytesTran { try { - std::error_code ec; - m_CompletionHandler->IssueRequest(ec); + std::error_code ErrorCode; + m_CompletionHandler->IssueRequest(ErrorCode); - if (ec) + if (ErrorCode) { - spdlog::error("IssueRequest() failed {}"sv, ec.message()); + spdlog::error("IssueRequest() failed {}"sv, ErrorCode.message()); } else { @@ -1095,6 +921,66 @@ HttpSysTransaction::HandleCompletion(ULONG IoResult, ULONG_PTR NumberOfBytesTran return Status::kDone; } +HttpSysServerRequest& +HttpSysTransaction::InvokeRequestHandler(HttpService& Service, IoBuffer Payload) +{ + HttpSysServerRequest& ThisRequest = m_HandlerRequest.emplace(*this, Service, Payload); + + if ((ThisRequest.RequestContentType() == HttpContentType::kCbPackageOffer) && (ThisRequest.RequestVerb() == HttpVerb::kPost)) + { + // The client is presenting us with a package attachments offer, we need + // to filter it down to the list of attachments we need them to send in + // the follow-up request + + m_PackageHandler = Service.HandlePackageRequest(ThisRequest); + + if (m_PackageHandler) + { + CbObject OfferMessage = LoadCompactBinaryObject(Payload); + + std::vector<IoHash> OfferCids; + + for (auto& CidEntry : OfferMessage["offer"]) + { + if (!CidEntry.IsHash()) + { + // Should yield bad request response? + + continue; + } + + OfferCids.push_back(CidEntry.AsHash(IoHash::Zero)); + } + + m_PackageHandler->FilterOffer(OfferCids); + + CbObjectWriter ResponseWriter; + ResponseWriter.BeginArray("need"); + + for (const IoHash& Cid : OfferCids) + { + ResponseWriter.AddHash(Cid); + } + + ResponseWriter.EndArray(); + + // Emit filter response + ThisRequest.WriteResponse(HttpResponseCode::OK, ResponseWriter.Save()); + + return ThisRequest; + } + } + else if ((ThisRequest.RequestContentType() == HttpContentType::kCbPackage) && (ThisRequest.RequestVerb() == HttpVerb::kPost)) + { + } + + // Default request handling + + Service.HandleRequest(ThisRequest); + + return ThisRequest; +} + ////////////////////////////////////////////////////////////////////////// HttpSysServerRequest::HttpSysServerRequest(HttpSysTransaction& Tx, HttpService& Service, IoBuffer PayloadBuffer) @@ -1137,6 +1023,62 @@ HttpSysServerRequest::HttpSysServerRequest(HttpSysTransaction& Tx, HttpService& m_AcceptType = GetAcceptType(HttpRequestPtr); } +Oid +HttpSysServerRequest::SessionId() const +{ + if (m_Flags & kHaveSessionId) + { + return m_SessionId; + } + + const HTTP_REQUEST* HttpRequestPtr = m_HttpTx.HttpRequest(); + + for (int i = 0; i < HttpRequestPtr->Headers.UnknownHeaderCount; ++i) + { + HTTP_UNKNOWN_HEADER& Header = HttpRequestPtr->Headers.pUnknownHeaders[i]; + std::string_view HeaderName{Header.pName, Header.NameLength}; + + if (HeaderName == "UE-Session"sv) + { + if (Header.RawValueLength == Oid::StringLength) + { + m_SessionId = Oid::FromHexString({Header.pRawValue, Header.RawValueLength}); + } + } + } + + m_Flags |= kHaveSessionId; + + return m_SessionId; +} + +uint32_t +HttpSysServerRequest::RequestId() const +{ + if (m_Flags & kHaveRequestId) + { + return m_RequestId; + } + + const HTTP_REQUEST* HttpRequestPtr = m_HttpTx.HttpRequest(); + + for (int i = 0; i < HttpRequestPtr->Headers.UnknownHeaderCount; ++i) + { + HTTP_UNKNOWN_HEADER& Header = HttpRequestPtr->Headers.pUnknownHeaders[i]; + std::string_view HeaderName{Header.pName, Header.NameLength}; + + if (HeaderName == "UE-Request"sv) + { + std::string_view RequestValue{Header.pRawValue, Header.RawValueLength}; + std::from_chars(RequestValue.data(), RequestValue.data() + RequestValue.size(), m_RequestId); + } + } + + m_Flags |= kHaveRequestId; + + return m_RequestId; +} + IoBuffer HttpSysServerRequest::ReadPayload() { @@ -1146,47 +1088,47 @@ HttpSysServerRequest::ReadPayload() void HttpSysServerRequest::WriteResponse(HttpResponseCode ResponseCode) { - ZEN_ASSERT(m_IsHandled == false); + ZEN_ASSERT(IsHandled() == false); m_Response = new HttpMessageResponseRequest(m_HttpTx, (uint16_t)ResponseCode); - if (m_SuppressBody) + if (SuppressBody()) { m_Response->SuppressResponseBody(); } - m_IsHandled = true; + SetIsHandled(); } void HttpSysServerRequest::WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, std::span<IoBuffer> Blobs) { - ZEN_ASSERT(m_IsHandled == false); + ZEN_ASSERT(IsHandled() == false); m_Response = new HttpMessageResponseRequest(m_HttpTx, (uint16_t)ResponseCode, ContentType, Blobs); - if (m_SuppressBody) + if (SuppressBody()) { m_Response->SuppressResponseBody(); } - m_IsHandled = true; + SetIsHandled(); } void HttpSysServerRequest::WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, std::u8string_view ResponseString) { - ZEN_ASSERT(m_IsHandled == false); + ZEN_ASSERT(IsHandled() == false); m_Response = new HttpMessageResponseRequest(m_HttpTx, (uint16_t)ResponseCode, ContentType, ResponseString.data(), ResponseString.size()); - if (m_SuppressBody) + if (SuppressBody()) { m_Response->SuppressResponseBody(); } - m_IsHandled = true; + SetIsHandled(); } ////////////////////////////////////////////////////////////////////////// @@ -1388,18 +1330,16 @@ InitialRequestHandler::HandleCompletion(ULONG IoResult, ULONG_PTR NumberOfBytesT // Body received completely - call request handler - HttpSysServerRequest ThisRequest(Transaction(), *Service, m_PayloadBuffer); - - Service->HandleRequest(ThisRequest); + HttpSysServerRequest& ThisRequest = Transaction().InvokeRequestHandler(*Service, m_PayloadBuffer); if (!ThisRequest.IsHandled()) { return new HttpMessageResponseRequest(Transaction(), 404, "Not found"sv); } - if (ThisRequest.m_Response) + if (HttpMessageResponseRequest* Response = ThisRequest.m_Response) { - return ThisRequest.m_Response; + return Response; } } else |