diff options
| author | Dan Engelbrecht <[email protected]> | 2023-09-08 08:51:54 -0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-09-08 14:51:54 +0200 |
| commit | 2f6a49f8d850806da94a92d0bd23e22735bf19a2 (patch) | |
| tree | ca11d34ade5cdded3d59c37294c1c2a299738800 /src/zenhttp/include | |
| parent | 0.2.20 (diff) | |
| download | zen-2f6a49f8d850806da94a92d0bd23e22735bf19a2.tar.xz zen-2f6a49f8d850806da94a92d0bd23e22735bf19a2.zip | |
Extend http client (#387)
* extend http client with configuration, headers, parameters and disk streaming upload/download
Diffstat (limited to 'src/zenhttp/include')
| -rw-r--r-- | src/zenhttp/include/zenhttp/formatters.h | 40 | ||||
| -rw-r--r-- | src/zenhttp/include/zenhttp/httpclient.h | 101 |
2 files changed, 122 insertions, 19 deletions
diff --git a/src/zenhttp/include/zenhttp/formatters.h b/src/zenhttp/include/zenhttp/formatters.h index 759df58d3..d45f5fbb2 100644 --- a/src/zenhttp/include/zenhttp/formatters.h +++ b/src/zenhttp/include/zenhttp/formatters.h @@ -6,6 +6,7 @@ #include <zencore/compactbinaryvalidation.h> #include <zencore/iobuffer.h> #include <zencore/string.h> +#include <zenhttp/httpclient.h> ZEN_THIRD_PARTY_INCLUDES_START #include <cpr/cpr.h> @@ -69,3 +70,42 @@ struct fmt::formatter<cpr::Response> } } }; + +template<> +struct fmt::formatter<zen::HttpClient::Response> +{ + constexpr auto parse(format_parse_context& Ctx) -> decltype(Ctx.begin()) { return Ctx.end(); } + + template<typename FormatContext> + auto format(const zen::HttpClient::Response& Response, FormatContext& Ctx) -> decltype(Ctx.out()) + { + using namespace std::literals; + + if (Response.IsSuccess()) + { + return fmt::format_to(Ctx.out(), + "OK: Status: {}, Bytes: {}/{} (Up/Down), Elapsed: {}s", + ToString(Response.StatusCode), + Response.UploadedBytes, + Response.DownloadedBytes, + Response.ElapsedSeconds); + } + else if (Response.Error) + { + return fmt::format_to(Ctx.out(), + "Failed: Elapsed: {}s, Error: ({}) '{}", + Response.ElapsedSeconds, + Response.Error.value().ErrorCode, + Response.Error.value().ErrorMessage); + } + else + { + return fmt::format_to(Ctx.out(), + "Failed: Bytes: {}/{} (Up/Down), Elapsed: {}s, Reason: '{}", + Response.UploadedBytes, + Response.DownloadedBytes, + Response.ElapsedSeconds, + Response.ErrorMessage(""sv)); + } + } +}; diff --git a/src/zenhttp/include/zenhttp/httpclient.h b/src/zenhttp/include/zenhttp/httpclient.h index 9ff4910bf..18031b280 100644 --- a/src/zenhttp/include/zenhttp/httpclient.h +++ b/src/zenhttp/include/zenhttp/httpclient.h @@ -9,10 +9,12 @@ #include <zenhttp/httpcommon.h> #include <optional> +#include <unordered_map> namespace zen { class CbPackage; +class CompositeBuffer; /** HTTP client implementation for Zen use cases @@ -24,22 +26,68 @@ class CbPackage; */ +struct HttpClientSettings +{ + std::chrono::milliseconds ConnectTimeout{3000}; + std::chrono::milliseconds Timeout{}; + bool AssumeHttp2 = false; +}; + class HttpClient { public: - HttpClient(std::string_view BaseUri); + struct Settings + { + }; + HttpClient(std::string_view BaseUri, const HttpClientSettings& Connectionsettings = {}); ~HttpClient(); struct ErrorContext { + int ErrorCode; std::string ErrorMessage; }; + struct KeyValueMap + { + KeyValueMap() = default; + std::unordered_map<std::string, std::string> Entries; + + constexpr inline const std::unordered_map<std::string, std::string>* operator->() const { return &Entries; } + constexpr inline std::unordered_map<std::string, std::string>* operator->() { return &Entries; } + constexpr inline const std::unordered_map<std::string, std::string>& operator*() const { return Entries; } + constexpr inline std::unordered_map<std::string, std::string>& operator*() { return Entries; } + + template<typename T> + KeyValueMap(T Begin, T End) : Entries(Begin, End) + { + } + KeyValueMap(std::pair<std::string, std::string>&& Entry) : Entries({{Entry}}) {} + KeyValueMap(std::pair<std::string_view, std::string_view>&& Entry) + : Entries({{{std::string(Entry.first), std::string(Entry.second)}}}) + { + } + KeyValueMap(std::span<std::pair<std::string_view, std::string_view>>&& List) : Entries(List.begin(), List.end()) {} + KeyValueMap(std::initializer_list<std::pair<std::string_view, std::string_view>>&& List) : Entries(List.begin(), List.end()) {} + }; + struct Response { HttpResponseCode StatusCode = HttpResponseCode::ImATeapot; IoBuffer ResponsePayload; // Note: this also includes the content type + // Contains the reponse headers + KeyValueMap Header; + + // The number of bytes sent as part of the request + int64_t UploadedBytes; + + // The number of bytes recevied as part of the response + int64_t DownloadedBytes; + + // The elapsed time in seconds for the request to execute + double ElapsedSeconds; + // This contains any errors from the HTTP stack. It won't contain information on // why the server responded with a non-success HTTP status, that may be gleaned // from the response payload itself depending on what the server provides. @@ -47,19 +95,19 @@ public: // Return the response payload as a CbObject. Note that this does not attempt to // validate that the content type or content itself makes sense as a CbObject - CbObject AsObject(); + CbObject AsObject() const; // Return the response payload as a CbPackage. Note that this does not attempt to // validate that the content type or content itself makes sense as a CbPackage - CbPackage AsPackage(); + CbPackage AsPackage() const; // Return the response payload as a string. Note that this does not attempt to // validate that the content type or content itself makes sense as a string. - std::string_view AsText(); + std::string_view AsText() const; // Return text representation of the payload. Formats into JSON for structured // objects, returns text as-is for text types like Text, JSON, HTML etc - std::string ToText(); + std::string ToText() const; // Returns whether the HTTP status code is considered successful (i.e in the // 2xx range) @@ -67,26 +115,41 @@ public: inline explicit operator bool() const noexcept { return IsSuccess(); } void ThrowError(std::string_view ErrorPrefix = "error"); - }; - - [[nodiscard]] Response Put(std::string_view Url, const IoBuffer& Payload); - [[nodiscard]] Response Get(std::string_view Url); - [[nodiscard]] Response Delete(std::string_view Url); - [[nodiscard]] Response Post(std::string_view Url); - [[nodiscard]] Response Post(std::string_view Url, const IoBuffer& Payload); - [[nodiscard]] Response Post(std::string_view Url, CbObject Payload); - [[nodiscard]] Response Post(std::string_view Url, CbPackage Payload); - [[nodiscard]] Response TransactPackage(std::string_view Url, CbPackage Package); + std::string ErrorMessage(std::string_view Prefix) const; + }; - inline std::string GetBaseUri() const { return m_BaseUri; } + [[nodiscard]] Response Put(std::string_view Url, const IoBuffer& Payload, const KeyValueMap& AdditionalHeader = {}); + [[nodiscard]] Response Get(std::string_view Url, const KeyValueMap& AdditionalHeader = {}, const KeyValueMap& Parameters = {}); + [[nodiscard]] Response Head(std::string_view Url, const KeyValueMap& AdditionalHeader = {}); + [[nodiscard]] Response Delete(std::string_view Url, const KeyValueMap& AdditionalHeader = {}); + [[nodiscard]] Response Post(std::string_view Url, const KeyValueMap& AdditionalHeader = {}); + [[nodiscard]] Response Post(std::string_view Url, const IoBuffer& Payload, const KeyValueMap& AdditionalHeader = {}); + [[nodiscard]] Response Post(std::string_view Url, CbObject Payload, const KeyValueMap& AdditionalHeader = {}); + [[nodiscard]] Response Post(std::string_view Url, CbPackage Payload, const KeyValueMap& AdditionalHeader = {}); + [[nodiscard]] Response Upload(std::string_view Url, const IoBuffer& Payload, const KeyValueMap& AdditionalHeader = {}); + [[nodiscard]] Response Upload(std::string_view Url, + const CompositeBuffer& Payload, + ZenContentType ContentType, + const KeyValueMap& AdditionalHeader = {}); + [[nodiscard]] Response Download(std::string_view Url, + const std::filesystem::path& TempFolderPath, + const KeyValueMap& AdditionalHeader = {}); + + [[nodiscard]] Response TransactPackage(std::string_view Url, CbPackage Package, const KeyValueMap& AdditionalHeader = {}); + + static std::pair<std::string_view, std::string_view> Accept(ZenContentType ContentType) + { + return std::make_pair("Accept", MapContentTypeToString(ContentType)); + } private: struct Impl; - std::string m_BaseUri; - std::string m_SessionId; - Ref<Impl> m_Impl; + std::string m_BaseUri; + std::string m_SessionId; + const HttpClientSettings m_ConnectionSettings; + Ref<Impl> m_Impl; }; void httpclient_forcelink(); // internal |