// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "httpclientcommon.h" #include #include ZEN_THIRD_PARTY_INCLUDES_START #include ZEN_THIRD_PARTY_INCLUDES_END namespace zen { class CurlHttpClient : public HttpClientBase { public: CurlHttpClient(std::string_view BaseUri, const HttpClientSettings& ConnectionSettings, std::function&& 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> 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 m_Sessions; void ReleaseSession(CURL* Handle); CurlResult DoWithRetry(std::string_view SessionId, std::function&& Func, std::unique_ptr& PayloadFile); CurlResult DoWithRetry( std::string_view SessionId, std::function&& Func, std::function&& Validate = [](CurlResult&) { return true; }); bool ValidatePayload(CurlResult& Result, std::unique_ptr& 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&& BoundaryPositions = {}); HttpClient::Response ResponseWithPayload(std::string_view SessionId, CurlResult&& Result, const HttpResponseCode WorkResponseCode, IoBuffer&& Payload, std::vector&& BoundaryPositions); }; } // namespace zen