diff options
| author | Stefan Boberg <[email protected]> | 2023-05-02 10:01:47 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-02 10:01:47 +0200 |
| commit | 075d17f8ada47e990fe94606c3d21df409223465 (patch) | |
| tree | e50549b766a2f3c354798a54ff73404217b4c9af /zenhttp/include | |
| parent | fix: bundle shouldn't append content zip to zen (diff) | |
| download | zen-075d17f8ada47e990fe94606c3d21df409223465.tar.xz zen-075d17f8ada47e990fe94606c3d21df409223465.zip | |
moved source directories into `/src` (#264)
* moved source directories into `/src`
* updated bundle.lua for new `src` path
* moved some docs, icon
* removed old test trees
Diffstat (limited to 'zenhttp/include')
| -rw-r--r-- | zenhttp/include/zenhttp/httpclient.h | 47 | ||||
| -rw-r--r-- | zenhttp/include/zenhttp/httpcommon.h | 181 | ||||
| -rw-r--r-- | zenhttp/include/zenhttp/httpserver.h | 315 | ||||
| -rw-r--r-- | zenhttp/include/zenhttp/httpshared.h | 163 | ||||
| -rw-r--r-- | zenhttp/include/zenhttp/websocket.h | 256 | ||||
| -rw-r--r-- | zenhttp/include/zenhttp/zenhttp.h | 21 |
6 files changed, 0 insertions, 983 deletions
diff --git a/zenhttp/include/zenhttp/httpclient.h b/zenhttp/include/zenhttp/httpclient.h deleted file mode 100644 index 8316a9b9f..000000000 --- a/zenhttp/include/zenhttp/httpclient.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "zenhttp.h" - -#include <zencore/iobuffer.h> -#include <zencore/uid.h> -#include <zenhttp/httpcommon.h> - -ZEN_THIRD_PARTY_INCLUDES_START -#include <cpr/cpr.h> -ZEN_THIRD_PARTY_INCLUDES_END - -namespace zen { - -class CbPackage; - -/** HTTP client implementation for Zen use cases - - Currently simple and synchronous, should become lean and asynchronous - */ -class HttpClient -{ -public: - HttpClient(std::string_view BaseUri); - ~HttpClient(); - - struct Response - { - int StatusCode = 0; - IoBuffer ResponsePayload; // Note: this also includes the content type - }; - - [[nodiscard]] Response Put(std::string_view Url, IoBuffer Payload); - [[nodiscard]] Response Get(std::string_view Url); - [[nodiscard]] Response TransactPackage(std::string_view Url, CbPackage Package); - [[nodiscard]] Response Delete(std::string_view Url); - -private: - std::string m_BaseUri; - std::string m_SessionId; -}; - -} // namespace zen - -void httpclient_forcelink(); // internal diff --git a/zenhttp/include/zenhttp/httpcommon.h b/zenhttp/include/zenhttp/httpcommon.h deleted file mode 100644 index 19fda8db4..000000000 --- a/zenhttp/include/zenhttp/httpcommon.h +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include <zencore/iobuffer.h> - -#include <string_view> - -#include <gsl/gsl-lite.hpp> - -namespace zen { - -using HttpContentType = ZenContentType; - -class IoBuffer; -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) -{ - return (HttpCode >= 200) && (HttpCode < 300); -} - -enum class HttpVerb : uint8_t -{ - kGet = 1 << 0, - kPut = 1 << 1, - kPost = 1 << 2, - kDelete = 1 << 3, - kHead = 1 << 4, - kCopy = 1 << 5, - kOptions = 1 << 6 -}; - -gsl_DEFINE_ENUM_BITMASK_OPERATORS(HttpVerb); - -const std::string_view ToString(HttpVerb Verb); - -enum class HttpResponseCode -{ - // 1xx - Informational - - Continue = 100, //!< Indicates that the initial part of a request has been received and has not yet been rejected by the server. - SwitchingProtocols = 101, //!< Indicates that the server understands and is willing to comply with the client's request, via the - //!< Upgrade header field, for a change in the application protocol being used on this connection. - Processing = 102, //!< Is an interim response used to inform the client that the server has accepted the complete request, but has not - //!< yet completed it. - EarlyHints = 103, //!< Indicates to the client that the server is likely to send a final response with the header fields included in - //!< the informational response. - - // 2xx - Successful - - OK = 200, //!< Indicates that the request has succeeded. - Created = 201, //!< Indicates that the request has been fulfilled and has resulted in one or more new resources being created. - Accepted = 202, //!< Indicates that the request has been accepted for processing, but the processing has not been completed. - NonAuthoritativeInformation = 203, //!< Indicates that the request was successful but the enclosed payload has been modified from that - //!< of the origin server's 200 (OK) response by a transforming proxy. - NoContent = 204, //!< Indicates that the server has successfully fulfilled the request and that there is no additional content to send - //!< in the response payload body. - ResetContent = 205, //!< Indicates that the server has fulfilled the request and desires that the user agent reset the \"document - //!< view\", which caused the request to be sent, to its original state as received from the origin server. - PartialContent = 206, //!< Indicates that the server is successfully fulfilling a range request for the target resource by transferring - //!< one or more parts of the selected representation that correspond to the satisfiable ranges found in the - //!< requests's Range header field. - MultiStatus = 207, //!< Provides status for multiple independent operations. - AlreadyReported = 208, //!< Used inside a DAV:propstat response element to avoid enumerating the internal members of multiple bindings - //!< to the same collection repeatedly. [RFC 5842] - IMUsed = 226, //!< The server has fulfilled a GET request for the resource, and the response is a representation of the result of one - //!< or more instance-manipulations applied to the current instance. - - // 3xx - Redirection - - MultipleChoices = 300, //!< Indicates that the target resource has more than one representation, each with its own more specific - //!< identifier, and information about the alternatives is being provided so that the user (or user agent) can - //!< select a preferred representation by redirecting its request to one or more of those identifiers. - MovedPermanently = 301, //!< Indicates that the target resource has been assigned a new permanent URI and any future references to this - //!< resource ought to use one of the enclosed URIs. - Found = 302, //!< Indicates that the target resource resides temporarily under a different URI. - SeeOther = 303, //!< Indicates that the server is redirecting the user agent to a different resource, as indicated by a URI in the - //!< Location header field, that is intended to provide an indirect response to the original request. - NotModified = 304, //!< Indicates that a conditional GET request has been received and would have resulted in a 200 (OK) response if it - //!< were not for the fact that the condition has evaluated to false. - UseProxy = 305, //!< \deprecated \parblock Due to security concerns regarding in-band configuration of a proxy. \endparblock - //!< The requested resource MUST be accessed through the proxy given by the Location field. - TemporaryRedirect = 307, //!< Indicates that the target resource resides temporarily under a different URI and the user agent MUST NOT - //!< change the request method if it performs an automatic redirection to that URI. - PermanentRedirect = 308, //!< The target resource has been assigned a new permanent URI and any future references to this resource - //!< ought to use one of the enclosed URIs. [...] This status code is similar to 301 Moved Permanently - //!< (Section 7.3.2 of rfc7231), except that it does not allow rewriting the request method from POST to GET. - - // 4xx - Client Error - BadRequest = 400, //!< Indicates that the server cannot or will not process the request because the received syntax is invalid, - //!< nonsensical, or exceeds some limitation on what the server is willing to process. - Unauthorized = 401, //!< Indicates that the request has not been applied because it lacks valid authentication credentials for the - //!< target resource. - PaymentRequired = 402, //!< *Reserved* - Forbidden = 403, //!< Indicates that the server understood the request but refuses to authorize it. - NotFound = 404, //!< Indicates that the origin server did not find a current representation for the target resource or is not willing - //!< to disclose that one exists. - MethodNotAllowed = 405, //!< Indicates that the method specified in the request-line is known by the origin server but not supported by - //!< the target resource. - NotAcceptable = 406, //!< Indicates that the target resource does not have a current representation that would be acceptable to the - //!< user agent, according to the proactive negotiation header fields received in the request, and the server is - //!< unwilling to supply a default representation. - ProxyAuthenticationRequired = - 407, //!< Is similar to 401 (Unauthorized), but indicates that the client needs to authenticate itself in order to use a proxy. - RequestTimeout = - 408, //!< Indicates that the server did not receive a complete request message within the time that it was prepared to wait. - Conflict = 409, //!< Indicates that the request could not be completed due to a conflict with the current state of the resource. - Gone = 410, //!< Indicates that access to the target resource is no longer available at the origin server and that this condition is - //!< likely to be permanent. - LengthRequired = 411, //!< Indicates that the server refuses to accept the request without a defined Content-Length. - PreconditionFailed = - 412, //!< Indicates that one or more preconditions given in the request header fields evaluated to false when tested on the server. - PayloadTooLarge = 413, //!< Indicates that the server is refusing to process a request because the request payload is larger than the - //!< server is willing or able to process. - URITooLong = 414, //!< Indicates that the server is refusing to service the request because the request-target is longer than the - //!< server is willing to interpret. - UnsupportedMediaType = 415, //!< Indicates that the origin server is refusing to service the request because the payload is in a format - //!< not supported by the target resource for this method. - RangeNotSatisfiable = 416, //!< Indicates that none of the ranges in the request's Range header field overlap the current extent of the - //!< selected resource or that the set of ranges requested has been rejected due to invalid ranges or an - //!< excessive request of small or overlapping ranges. - ExpectationFailed = 417, //!< Indicates that the expectation given in the request's Expect header field could not be met by at least - //!< one of the inbound servers. - ImATeapot = 418, //!< Any attempt to brew coffee with a teapot should result in the error code 418 I'm a teapot. - UnprocessableEntity = 422, //!< Means the server understands the content type of the request entity (hence a 415(Unsupported Media - //!< Type) status code is inappropriate), and the syntax of the request entity is correct (thus a 400 (Bad - //!< Request) status code is inappropriate) but was unable to process the contained instructions. - Locked = 423, //!< Means the source or destination resource of a method is locked. - FailedDependency = 424, //!< Means that the method could not be performed on the resource because the requested action depended on - //!< another action and that action failed. - UpgradeRequired = 426, //!< Indicates that the server refuses to perform the request using the current protocol but might be willing to - //!< do so after the client upgrades to a different protocol. - PreconditionRequired = 428, //!< Indicates that the origin server requires the request to be conditional. - TooManyRequests = 429, //!< Indicates that the user has sent too many requests in a given amount of time (\"rate limiting\"). - RequestHeaderFieldsTooLarge = - 431, //!< Indicates that the server is unwilling to process the request because its header fields are too large. - UnavailableForLegalReasons = - 451, //!< This status code indicates that the server is denying access to the resource in response to a legal demand. - - // 5xx - Server Error - - InternalServerError = - 500, //!< Indicates that the server encountered an unexpected condition that prevented it from fulfilling the request. - NotImplemented = 501, //!< Indicates that the server does not support the functionality required to fulfill the request. - BadGateway = 502, //!< Indicates that the server, while acting as a gateway or proxy, received an invalid response from an inbound - //!< server it accessed while attempting to fulfill the request. - ServiceUnavailable = 503, //!< Indicates that the server is currently unable to handle the request due to a temporary overload or - //!< scheduled maintenance, which will likely be alleviated after some delay. - GatewayTimeout = 504, //!< Indicates that the server, while acting as a gateway or proxy, did not receive a timely response from an - //!< upstream server it needed to access in order to complete the request. - HTTPVersionNotSupported = 505, //!< Indicates that the server does not support, or refuses to support, the protocol version that was - //!< used in the request message. - VariantAlsoNegotiates = - 506, //!< Indicates that the server has an internal configuration error: the chosen variant resource is configured to engage in - //!< transparent content negotiation itself, and is therefore not a proper end point in the negotiation process. - InsufficientStorage = 507, //!< Means the method could not be performed on the resource because the server is unable to store the - //!< representation needed to successfully complete the request. - LoopDetected = 508, //!< Indicates that the server terminated an operation because it encountered an infinite loop while processing a - //!< request with "Depth: infinity". [RFC 5842] - NotExtended = 510, //!< The policy for accessing the resource has not been met in the request. [RFC 2774] - NetworkAuthenticationRequired = 511, //!< Indicates that the client needs to authenticate to gain network access. -}; - -} // namespace zen diff --git a/zenhttp/include/zenhttp/httpserver.h b/zenhttp/include/zenhttp/httpserver.h deleted file mode 100644 index 3b9fa50b4..000000000 --- a/zenhttp/include/zenhttp/httpserver.h +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "zenhttp.h" - -#include <zencore/compactbinary.h> -#include <zencore/enumflags.h> -#include <zencore/iobuffer.h> -#include <zencore/iohash.h> -#include <zencore/refcount.h> -#include <zencore/string.h> -#include <zencore/uid.h> -#include <zenhttp/httpcommon.h> - -#include <functional> -#include <gsl/gsl-lite.hpp> -#include <list> -#include <map> -#include <regex> -#include <span> -#include <unordered_map> - -namespace zen { - -/** HTTP Server Request - */ -class HttpServerRequest -{ -public: - HttpServerRequest(); - ~HttpServerRequest(); - - // Synchronous operations - - [[nodiscard]] inline std::string_view RelativeUri() const { return m_Uri; } // Returns URI without service prefix - [[nodiscard]] std::string_view RelativeUriWithExtension() const { return m_UriWithExtension; } - [[nodiscard]] inline std::string_view QueryString() const { return m_QueryString; } - - struct QueryParams - { - std::vector<std::pair<std::string_view, std::string_view>> KvPairs; - - std::string_view GetValue(std::string_view ParamName) const - { - for (const auto& Kv : KvPairs) - { - const std::string_view& Key = Kv.first; - - if (Key.size() == ParamName.size()) - { - if (0 == StrCaseCompare(Key.data(), ParamName.data(), Key.size())) - { - return Kv.second; - } - } - } - - return std::string_view(); - } - }; - - virtual bool TryGetRanges(HttpRanges&) { return false; } - - QueryParams GetQueryParams(); - - inline HttpVerb RequestVerb() const { return m_Verb; } - inline HttpContentType RequestContentType() { return m_ContentType; } - inline HttpContentType AcceptContentType() { return m_AcceptType; } - - inline uint64_t ContentLength() const { return m_ContentLength; } - Oid SessionId() const; - uint32_t RequestId() const; - - inline bool IsHandled() const { return !!(m_Flags & kIsHandled); } - inline bool SuppressBody() const { return !!(m_Flags & kSuppressBody); } - inline void SetSuppressResponseBody() { m_Flags |= kSuppressBody; } - - /** Read POST/PUT payload for request body, which is always available without delay - */ - virtual IoBuffer ReadPayload() = 0; - - ZENCORE_API CbObject ReadPayloadObject(); - ZENCORE_API CbPackage ReadPayloadPackage(); - - /** Respond with payload - - No data will have been sent when any of these functions return. Instead, the response will be transmitted - asynchronously, after returning from a request handler function. - - Note that this is destructive in the sense that the IoBuffer instances referred to by Blobs will be - moved into our response handler array where they are kept alive, in order to reduce ref-counting storms - */ - virtual void WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, std::span<IoBuffer> Blobs) = 0; - virtual void WriteResponse(HttpResponseCode ResponseCode) = 0; - virtual void WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, std::u8string_view ResponseString) = 0; - virtual void WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, CompositeBuffer& Payload); - - void WriteResponse(HttpResponseCode ResponseCode, CbObject Data); - void WriteResponse(HttpResponseCode ResponseCode, CbArray Array); - void WriteResponse(HttpResponseCode ResponseCode, CbPackage Package); - void WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, std::string_view ResponseString); - void WriteResponse(HttpResponseCode ResponseCode, HttpContentType ContentType, IoBuffer Blob); - - virtual void WriteResponseAsync(std::function<void(HttpServerRequest&)>&& ContinuationHandler) = 0; - -protected: - enum - { - kIsHandled = 1 << 0, - kSuppressBody = 1 << 1, - kHaveRequestId = 1 << 2, - kHaveSessionId = 1 << 3, - }; - - mutable uint32_t m_Flags = 0; - HttpVerb m_Verb = HttpVerb::kGet; - HttpContentType m_ContentType = HttpContentType::kBinary; - HttpContentType m_AcceptType = HttpContentType::kUnknownContentType; - uint64_t m_ContentLength = ~0ull; - std::string_view m_Uri; - std::string_view m_UriWithExtension; - std::string_view m_QueryString; - mutable uint32_t m_RequestId = ~uint32_t(0); - mutable Oid m_SessionId = Oid::Zero; - - inline void SetIsHandled() { m_Flags |= kIsHandled; } - - virtual Oid ParseSessionId() const = 0; - virtual uint32_t ParseRequestId() const = 0; -}; - -class IHttpPackageHandler : public RefCounted -{ -public: - virtual void FilterOffer(std::vector<IoHash>& OfferCids) = 0; - virtual void OnRequestBegin() = 0; - virtual IoBuffer CreateTarget(const IoHash& Cid, uint64_t StorageSize) = 0; - virtual void OnRequestComplete() = 0; -}; - -/** - * Base class for implementing an HTTP "service" - * - * A service exposes one or more endpoints with a certain URI prefix - * - */ - -class HttpService -{ -public: - HttpService() = default; - virtual ~HttpService() = default; - - virtual const char* BaseUri() const = 0; - virtual void HandleRequest(HttpServerRequest& HttpServiceRequest) = 0; - virtual Ref<IHttpPackageHandler> HandlePackageRequest(HttpServerRequest& HttpServiceRequest); - - // Internals - - inline void SetUriPrefixLength(size_t PrefixLength) { m_UriPrefixLength = (int)PrefixLength; } - inline int UriPrefixLength() const { return m_UriPrefixLength; } - -private: - int m_UriPrefixLength = 0; -}; - -/** HTTP server - * - * Implements the main event loop to service HTTP requests, and handles routing - * requests to the appropriate handler as registered via RegisterService - */ -class HttpServer : public RefCounted -{ -public: - virtual void RegisterService(HttpService& Service) = 0; - virtual int Initialize(int BasePort) = 0; - virtual void Run(bool IsInteractiveSession) = 0; - virtual void RequestExit() = 0; -}; - -Ref<HttpServer> CreateHttpServer(std::string_view ServerClass); - -////////////////////////////////////////////////////////////////////////// - -class HttpRouterRequest -{ -public: - HttpRouterRequest(HttpServerRequest& Request) : m_HttpRequest(Request) {} - - ZENCORE_API std::string GetCapture(uint32_t Index) const; - inline HttpServerRequest& ServerRequest() { return m_HttpRequest; } - -private: - using MatchResults_t = std::match_results<std::string_view::const_iterator>; - - HttpServerRequest& m_HttpRequest; - MatchResults_t m_Match; - - friend class HttpRequestRouter; -}; - -inline std::string -HttpRouterRequest::GetCapture(uint32_t Index) const -{ - ZEN_ASSERT(Index < m_Match.size()); - - return m_Match[Index]; -} - -/** HTTP request router helper - * - * This helper class allows a service implementer to register one or more - * endpoints using pattern matching (currently using regex matching) - * - * This is intended to be initialized once only, there is no thread - * safety so you can absolutely not add or remove endpoints once the handler - * goes live - */ - -class HttpRequestRouter -{ -public: - typedef std::function<void(HttpRouterRequest&)> HandlerFunc_t; - - /** - * @brief Add pattern which can be referenced by name, commonly used for URL components - * @param Id String used to identify patterns for replacement - * @param Regex String which will replace the Id string in any registered URL paths - */ - void AddPattern(const char* Id, const char* Regex); - - /** - * @brief Register a an endpoint handler for the given route - * @param Regex Regular expression used to match the handler to a request. This may - * contain pattern aliases registered via AddPattern - * @param HandlerFunc Handler function to call for any matching request - * @param SupportedVerbs Supported HTTP verbs for this handler - */ - void RegisterRoute(const char* Regex, HandlerFunc_t&& HandlerFunc, HttpVerb SupportedVerbs); - - void ProcessRegexSubstitutions(const char* Regex, StringBuilderBase& ExpandedRegex); - - /** - * @brief HTTP request handling function - this should be called to route the - * request to a registered handler - * @param Request Request to route to a handler - * @return Function returns true if the request was routed successfully - */ - bool HandleRequest(zen::HttpServerRequest& Request); - -private: - struct HandlerEntry - { - HandlerEntry(const char* Regex, HttpVerb SupportedVerbs, HandlerFunc_t&& Handler, const char* Pattern) - : RegEx(Regex, std::regex::icase | std::regex::ECMAScript) - , Verbs(SupportedVerbs) - , Handler(std::move(Handler)) - , Pattern(Pattern) - { - } - - ~HandlerEntry() = default; - - std::regex RegEx; - HttpVerb Verbs; - HandlerFunc_t Handler; - const char* Pattern; - - private: - HandlerEntry& operator=(const HandlerEntry&) = delete; - HandlerEntry(const HandlerEntry&) = delete; - }; - - std::list<HandlerEntry> m_Handlers; - std::unordered_map<std::string, std::string> m_PatternMap; -}; - -/** HTTP RPC request helper - */ - -class RpcResult -{ - RpcResult(CbObject Result) : m_Result(std::move(Result)) {} - -private: - CbObject m_Result; -}; - -class HttpRpcHandler -{ -public: - HttpRpcHandler(); - ~HttpRpcHandler(); - - HttpRpcHandler(const HttpRpcHandler&) = delete; - HttpRpcHandler operator=(const HttpRpcHandler&) = delete; - - void AddRpc(std::string_view RpcId, std::function<void(CbObject& RpcArgs)> HandlerFunction); - -private: - struct RpcFunction - { - std::function<void(CbObject& RpcArgs)> Function; - std::string Identifier; - }; - - std::map<std::string, RpcFunction> m_Functions; -}; - -bool HandlePackageOffers(HttpService& Service, HttpServerRequest& Request, Ref<IHttpPackageHandler>& PackageHandlerRef); - -void http_forcelink(); // internal - -} // namespace zen diff --git a/zenhttp/include/zenhttp/httpshared.h b/zenhttp/include/zenhttp/httpshared.h deleted file mode 100644 index d335572c5..000000000 --- a/zenhttp/include/zenhttp/httpshared.h +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include <zencore/compactbinarypackage.h> -#include <zencore/iobuffer.h> -#include <zencore/iohash.h> - -#include <functional> -#include <gsl/gsl-lite.hpp> - -namespace zen { - -class IoBuffer; -class CbPackage; -class CompositeBuffer; - -/** _____ _ _____ _ - / ____| | | __ \ | | - | | | |__ | |__) |_ _ ___| | ____ _ __ _ ___ - | | | '_ \| ___/ _` |/ __| |/ / _` |/ _` |/ _ \ - | |____| |_) | | | (_| | (__| < (_| | (_| | __/ - \_____|_.__/|_| \__,_|\___|_|\_\__,_|\__, |\___| - __/ | - |___/ - - Structures and code related to handling CbPackage transactions - - CbPackage instances are marshaled across the wire using a distinct message - format. We don't use the CbPackage serialization format provided by the - CbPackage implementation itself since that does not provide much flexibility - in how the attachment payloads are transmitted. The scheme below separates - metadata cleanly from payloads and this enables us to more efficiently - transmit them either via sendfile/TransmitFile like mechanisms, or by - reference/memory mapping in the local case. - */ - -struct CbPackageHeader -{ - uint32_t HeaderMagic; - uint32_t AttachmentCount; // TODO: should add ability to opt out of implicit root document? - uint32_t Reserved1; - uint32_t Reserved2; -}; - -static_assert(sizeof(CbPackageHeader) == 16); - -enum : uint32_t -{ - kCbPkgMagic = 0xaa77aacc -}; - -struct CbAttachmentEntry -{ - uint64_t PayloadSize; // Size of the associated payload data in the message - uint32_t Flags; // See flags below - IoHash AttachmentHash; // Content Id for the attachment - - enum - { - kIsCompressed = (1u << 0), // Is marshaled using compressed buffer storage format - kIsObject = (1u << 1), // Is compact binary object - kIsError = (1u << 2), // Is error (compact binary formatted) object - kIsLocalRef = (1u << 3), // Is "local reference" - }; -}; - -struct CbAttachmentReferenceHeader -{ - uint64_t PayloadByteOffset = 0; - uint64_t PayloadByteSize = ~0u; - uint16_t AbsolutePathLength = 0; - - // This header will be followed by UTF8 encoded absolute path to backing file -}; - -static_assert(sizeof(CbAttachmentEntry) == 32); - -enum class FormatFlags -{ - kDefault = 0, - kAllowLocalReferences = (1u << 0), - kDenyPartialLocalReferences = (1u << 1) -}; - -gsl_DEFINE_ENUM_BITMASK_OPERATORS(FormatFlags); - -enum class RpcAcceptOptions : uint16_t -{ - kNone = 0, - kAllowLocalReferences = (1u << 0), - kAllowPartialLocalReferences = (1u << 1) -}; - -gsl_DEFINE_ENUM_BITMASK_OPERATORS(RpcAcceptOptions); - -std::vector<IoBuffer> FormatPackageMessage(const CbPackage& Data, FormatFlags Flags, int TargetProcessPid = 0); -CompositeBuffer FormatPackageMessageBuffer(const CbPackage& Data, FormatFlags Flags, int TargetProcessPid = 0); -CbPackage ParsePackageMessage( - IoBuffer Payload, - std::function<IoBuffer(const IoHash& Cid, uint64_t Size)> CreateBuffer = [](const IoHash&, uint64_t Size) -> IoBuffer { - return IoBuffer{Size}; - }); -bool IsPackageMessage(IoBuffer Payload); - -bool ParsePackageMessageWithLegacyFallback(const IoBuffer& Response, CbPackage& OutPackage); - -std::vector<IoBuffer> FormatPackageMessage(const CbPackage& Data, int TargetProcessPid = 0); -CompositeBuffer FormatPackageMessageBuffer(const CbPackage& Data, int TargetProcessPid = 0); - -/** Streaming reader for compact binary packages - - The goal is to ultimately support zero-copy I/O, but for now there'll be some - copying involved on some platforms at least. - - This approach to deserializing CbPackage data is more efficient than - `ParsePackageMessage` since it does not require the entire message to - be resident in a memory buffer - - */ -class CbPackageReader -{ -public: - CbPackageReader(); - ~CbPackageReader(); - - void SetPayloadBufferCreator(std::function<IoBuffer(const IoHash& Cid, uint64_t Size)> CreateBuffer); - - /** Process compact binary package data stream - - The data stream must be in the serialization format produced by FormatPackageMessage - - \return How many bytes must be fed to this function in the next call - */ - uint64_t ProcessPackageHeaderData(const void* Data, uint64_t DataBytes); - - void Finalize(); - const std::vector<CbAttachment>& GetAttachments() { return m_Attachments; } - CbObject GetRootObject() { return m_RootObject; } - std::span<IoBuffer> GetPayloadBuffers() { return m_PayloadBuffers; } - -private: - enum class State - { - kInitialState, - kReadingHeader, - kReadingAttachmentEntries, - kReadingBuffers - } m_CurrentState = State::kInitialState; - - std::function<IoBuffer(const IoHash& Cid, uint64_t Size)> m_CreateBuffer; - std::vector<IoBuffer> m_PayloadBuffers; - std::vector<CbAttachmentEntry> m_AttachmentEntries; - std::vector<CbAttachment> m_Attachments; - CbObject m_RootObject; - CbPackageHeader m_PackageHeader; - - IoBuffer MarshalLocalChunkReference(IoBuffer AttachmentBuffer); -}; - -void forcelink_httpshared(); - -} // namespace zen diff --git a/zenhttp/include/zenhttp/websocket.h b/zenhttp/include/zenhttp/websocket.h deleted file mode 100644 index adca7e988..000000000 --- a/zenhttp/include/zenhttp/websocket.h +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include <zencore/compactbinarypackage.h> -#include <zencore/memory.h> - -#include <compare> -#include <functional> -#include <future> -#include <memory> -#include <optional> - -#pragma once - -namespace asio { -class io_context; -} - -namespace zen { - -class BinaryWriter; - -/** - * A unique socket ID. - */ -class WebSocketId -{ - static std::atomic_uint32_t NextId; - -public: - WebSocketId() = default; - - uint32_t Value() const { return m_Value; } - - auto operator<=>(const WebSocketId&) const = default; - - static WebSocketId New() { return WebSocketId(NextId.fetch_add(1)); } - -private: - WebSocketId(uint32_t Value) : m_Value(Value) {} - - uint32_t m_Value{}; -}; - -/** - * Type of web socket message. - */ -enum class WebSocketMessageType : uint8_t -{ - kInvalid, - kNotification, - kRequest, - kStreamRequest, - kResponse, - kStreamResponse, - kStreamCompleteResponse, - kCount -}; - -inline std::string_view -ToString(WebSocketMessageType Type) -{ - switch (Type) - { - case WebSocketMessageType::kInvalid: - return std::string_view("Invalid"); - case WebSocketMessageType::kNotification: - return std::string_view("Notification"); - case WebSocketMessageType::kRequest: - return std::string_view("Request"); - case WebSocketMessageType::kStreamRequest: - return std::string_view("StreamRequest"); - case WebSocketMessageType::kResponse: - return std::string_view("Response"); - case WebSocketMessageType::kStreamResponse: - return std::string_view("StreamResponse"); - case WebSocketMessageType::kStreamCompleteResponse: - return std::string_view("StreamCompleteResponse"); - default: - return std::string_view("Unknown"); - }; -} - -/** - * Web socket message. - */ -class WebSocketMessage -{ - struct Header - { - static constexpr uint32_t ExpectedMagic = 0x7a776d68; // zwmh - - uint64_t MessageSize{}; - uint32_t Magic{ExpectedMagic}; - uint32_t CorrelationId{}; - uint32_t StatusCode{200u}; - WebSocketMessageType MessageType{}; - uint8_t Reserved[3] = {0}; - - bool IsValid() const; - }; - - static_assert(sizeof(Header) == 24); - - static std::atomic_uint32_t NextCorrelationId; - -public: - static constexpr size_t HeaderSize = sizeof(Header); - - WebSocketMessage() = default; - - WebSocketId SocketId() const { return m_SocketId; } - void SetSocketId(WebSocketId Id) { m_SocketId = Id; } - uint64_t MessageSize() const { return m_Header.MessageSize; } - void SetMessageType(WebSocketMessageType MessageType); - void SetCorrelationId(uint32_t Id) { m_Header.CorrelationId = Id; } - uint32_t CorrelationId() const { return m_Header.CorrelationId; } - uint32_t StatusCode() const { return m_Header.StatusCode; } - void SetStatusCode(uint32_t StatusCode) { m_Header.StatusCode = StatusCode; } - WebSocketMessageType MessageType() const { return m_Header.MessageType; } - - const CbPackage& Body() const { return m_Body.value(); } - void SetBody(CbPackage&& Body); - void SetBody(CbObject&& Body); - bool HasBody() const { return m_Body.has_value(); } - - void Save(BinaryWriter& Writer); - bool TryLoadHeader(MemoryView Memory); - - bool IsValid() const { return m_Header.MessageType != WebSocketMessageType::kInvalid; } - -private: - Header m_Header{}; - WebSocketId m_SocketId{}; - std::optional<CbPackage> m_Body; -}; - -class WebSocketServer; - -/** - * Base class for handling web socket requests and notifications from connected client(s). - */ -class WebSocketService -{ -public: - virtual ~WebSocketService() = default; - - void Configure(WebSocketServer& Server); - - virtual bool HandleRequest(const WebSocketMessage&) { ZEN_ASSERT(false); } - virtual void HandleNotification(const WebSocketMessage&) { ZEN_ASSERT(false); } - -protected: - WebSocketService() = default; - - virtual void RegisterHandlers(WebSocketServer& Server) = 0; - void SendStreamResponse(WebSocketId SocketId, uint32_t CorrelationId, CbPackage&& StreamResponse, bool IsStreamComplete); - void SendStreamResponse(WebSocketId SocketId, uint32_t CorrelationId, CbObject&& StreamResponse, bool IsStreamComplete); - - WebSocketServer& SocketServer() - { - ZEN_ASSERT(m_SocketServer); - return *m_SocketServer; - } - -private: - WebSocketServer* m_SocketServer{}; -}; - -/** - * Server options. - */ -struct WebSocketServerOptions -{ - uint16_t Port = 2337; - uint32_t ThreadCount = 1; -}; - -/** - * The web socket server manages client connections and routing of requests and notifications. - */ -class WebSocketServer -{ -public: - virtual ~WebSocketServer() = default; - - virtual bool Run() = 0; - virtual void Shutdown() = 0; - - virtual void RegisterService(WebSocketService& Service) = 0; - virtual void RegisterNotificationHandler(std::string_view Key, WebSocketService& Service) = 0; - virtual void RegisterRequestHandler(std::string_view Key, WebSocketService& Service) = 0; - - virtual void SendNotification(WebSocketMessage&& Notification) = 0; - virtual void SendResponse(WebSocketMessage&& Response) = 0; - - static std::unique_ptr<WebSocketServer> Create(const WebSocketServerOptions& Options); -}; - -/** - * The state of the web socket. - */ -enum class WebSocketState : uint32_t -{ - kNone, - kHandshaking, - kConnected, - kDisconnected, - kError -}; - -/** - * Type of web socket client event. - */ -enum class WebSocketEvent : uint32_t -{ - kConnected, - kDisconnected, - kError -}; - -/** - * Web socket client connection info. - */ -struct WebSocketConnectInfo -{ - std::string Host; - int16_t Port{8848}; - std::string Endpoint; - std::vector<std::string> Protocols; - uint16_t Version{13}; -}; - -/** - * A connection to a web socket server for sending requests and listening for notifications. - */ -class WebSocketClient -{ -public: - using EventCallback = std::function<void()>; - using NotificationCallback = std::function<void(WebSocketMessage&&)>; - - virtual ~WebSocketClient() = default; - - virtual std::future<bool> Connect(const WebSocketConnectInfo& Info) = 0; - virtual void Disconnect() = 0; - virtual bool IsConnected() const = 0; - virtual WebSocketState State() const = 0; - - virtual std::future<WebSocketMessage> SendRequest(WebSocketMessage&& Request) = 0; - virtual void OnNotification(NotificationCallback&& Cb) = 0; - virtual void OnEvent(WebSocketEvent Evt, EventCallback&& Cb) = 0; - - static std::shared_ptr<WebSocketClient> Create(asio::io_context& IoCtx); -}; - -} // namespace zen diff --git a/zenhttp/include/zenhttp/zenhttp.h b/zenhttp/include/zenhttp/zenhttp.h deleted file mode 100644 index 59c64b31f..000000000 --- a/zenhttp/include/zenhttp/zenhttp.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include <zencore/zencore.h> - -#ifndef ZEN_WITH_HTTPSYS -# if ZEN_PLATFORM_WINDOWS -# define ZEN_WITH_HTTPSYS 1 -# else -# define ZEN_WITH_HTTPSYS 0 -# endif -#endif - -#define ZENHTTP_API // Placeholder to allow DLL configs in the future - -namespace zen { - -ZENHTTP_API void zenhttp_forcelinktests(); - -} |