1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "httpclientcommon.h"
#include <zencore/logging.h>
#include <zenhttp/httpclient.h>
ZEN_THIRD_PARTY_INCLUDES_START
#include <curl/curl.h>
ZEN_THIRD_PARTY_INCLUDES_END
namespace zen {
class CurlHttpClient : public HttpClientBase
{
public:
CurlHttpClient(std::string_view BaseUri, const HttpClientSettings& ConnectionSettings, std::function<bool()>&& CheckIfAbortFunction);
~CurlHttpClient();
// HttpClientBase
[[nodiscard]] virtual Response Put(std::string_view Url, const IoBuffer& Payload, const KeyValueMap& AdditionalHeader = {}) override;
[[nodiscard]] virtual Response Put(std::string_view Url, const KeyValueMap& Parameters = {}) override;
[[nodiscard]] virtual Response Get(std::string_view Url,
const KeyValueMap& AdditionalHeader = {},
const KeyValueMap& Parameters = {}) override;
[[nodiscard]] virtual Response Head(std::string_view Url, const KeyValueMap& AdditionalHeader = {}) override;
[[nodiscard]] virtual Response Delete(std::string_view Url, const KeyValueMap& AdditionalHeader = {}) override;
[[nodiscard]] virtual Response Post(std::string_view Url,
const KeyValueMap& AdditionalHeader = {},
const KeyValueMap& Parameters = {}) override;
[[nodiscard]] virtual Response Post(std::string_view Url, const IoBuffer& Payload, const KeyValueMap& AdditionalHeader = {}) override;
[[nodiscard]] virtual Response Post(std::string_view Url,
const IoBuffer& Payload,
ZenContentType ContentType,
const KeyValueMap& AdditionalHeader = {}) override;
[[nodiscard]] virtual Response Post(std::string_view Url,
CbObject Payload,
const KeyValueMap& AdditionalHeader = {},
const std::filesystem::path& TempFolderPath = {}) override;
[[nodiscard]] virtual Response Post(std::string_view Url, CbPackage Payload, const KeyValueMap& AdditionalHeader = {}) override;
[[nodiscard]] virtual Response Post(std::string_view Url,
const CompositeBuffer& Payload,
ZenContentType ContentType,
const KeyValueMap& AdditionalHeader = {}) override;
[[nodiscard]] virtual Response Upload(std::string_view Url, const IoBuffer& Payload, const KeyValueMap& AdditionalHeader = {}) override;
[[nodiscard]] virtual Response Upload(std::string_view Url,
const CompositeBuffer& Payload,
ZenContentType ContentType,
const KeyValueMap& AdditionalHeader = {}) override;
[[nodiscard]] virtual Response Download(std::string_view Url,
const std::filesystem::path& TempFolderPath,
const KeyValueMap& AdditionalHeader = {}) override;
[[nodiscard]] virtual Response TransactPackage(std::string_view Url,
CbPackage Package,
const KeyValueMap& AdditionalHeader = {}) override;
private:
struct CurlResult
{
long StatusCode = 0;
std::string Body;
std::vector<std::pair<std::string, std::string>> Headers;
double ElapsedSeconds = 0;
int64_t UploadedBytes = 0;
int64_t DownloadedBytes = 0;
CURLcode ErrorCode = CURLE_OK;
std::string ErrorMessage;
};
struct Session
{
Session(CurlHttpClient* InOuter, CURL* InHandle) : Outer(InOuter), Handle(InHandle) {}
~Session();
CURL* Get() const { return Handle; }
// Takes ownership of the curl_slist and sets it on the handle.
// The list is freed automatically when the Session is destroyed.
void SetHeaders(curl_slist* Headers);
// Low-level perform: executes the request and collects status/timing.
CurlResult Perform();
// Sets up standard write+header callbacks, performs the request, and
// moves the collected body and headers into the returned CurlResult.
CurlResult PerformWithResponseCallbacks();
LoggerRef Log() { return Outer->Log(); }
private:
CurlHttpClient* Outer;
CURL* Handle;
curl_slist* HeaderList = nullptr;
Session(Session&&) = delete;
Session& operator=(Session&&) = delete;
};
Session AllocSession(std::string_view ResourcePath, const KeyValueMap& Parameters);
RwLock m_SessionLock;
std::vector<CURL*> m_Sessions;
void ReleaseSession(CURL* Handle);
CurlResult DoWithRetry(std::string_view SessionId,
std::function<CurlResult()>&& Func,
std::unique_ptr<detail::TempPayloadFile>& PayloadFile);
CurlResult DoWithRetry(
std::string_view SessionId,
std::function<CurlResult()>&& Func,
std::function<bool(CurlResult&)>&& Validate = [](CurlResult&) { return true; });
bool ValidatePayload(CurlResult& Result, std::unique_ptr<detail::TempPayloadFile>& PayloadFile);
static bool ShouldRetry(const CurlResult& Result);
bool ShouldLogErrorCode(HttpResponseCode ResponseCode) const;
HttpClient::Response CommonResponse(std::string_view SessionId,
CurlResult&& Result,
IoBuffer&& Payload,
std::vector<HttpClient::Response::MultipartBoundary>&& BoundaryPositions = {});
HttpClient::Response ResponseWithPayload(std::string_view SessionId,
CurlResult&& Result,
const HttpResponseCode WorkResponseCode,
IoBuffer&& Payload,
std::vector<HttpClient::Response::MultipartBoundary>&& BoundaryPositions);
};
} // namespace zen
|