aboutsummaryrefslogtreecommitdiff
path: root/src/zenhttp/include
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2023-09-08 08:51:54 -0400
committerGitHub <[email protected]>2023-09-08 14:51:54 +0200
commit2f6a49f8d850806da94a92d0bd23e22735bf19a2 (patch)
treeca11d34ade5cdded3d59c37294c1c2a299738800 /src/zenhttp/include
parent0.2.20 (diff)
downloadzen-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.h40
-rw-r--r--src/zenhttp/include/zenhttp/httpclient.h101
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