aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/zen/zen.cpp2
-rw-r--r--src/zen/zen.h2
-rw-r--r--src/zenhttp/clients/httpclientcpr.cpp1285
-rw-r--r--src/zenhttp/clients/httpclientcpr.h191
-rw-r--r--src/zenhttp/httpclient.cpp32
-rw-r--r--src/zenhttp/include/zenhttp/cprutils.h98
-rw-r--r--src/zenhttp/include/zenhttp/httpclient.h3
-rw-r--r--src/zenhttp/xmake.lua7
-rw-r--r--src/zenserver-test/cache-tests.cpp8
-rw-r--r--src/zenserver/config/config.cpp2
-rw-r--r--src/zenserver/config/config.h2
-rw-r--r--src/zenutil/xmake.lua4
12 files changed, 13 insertions, 1623 deletions
diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp
index cbaf64e31..5f92a7ca3 100644
--- a/src/zen/zen.cpp
+++ b/src/zen/zen.cpp
@@ -799,7 +799,7 @@ main(int argc, char** argv)
Options.add_options()("help", "Show command line help");
Options.add_options()("c, command", "Sub command", cxxopts::value<std::string>(SubCommand));
Options.add_options()("httpclient",
- "Select HTTP client implementation (e.g. 'curl', 'cpr')",
+ "Select HTTP client implementation",
cxxopts::value<std::string>(GlobalOptions.HttpClientBackend)->default_value("curl"));
int CoreLimit = 0;
diff --git a/src/zen/zen.h b/src/zen/zen.h
index 97cc9af6f..64d9390a3 100644
--- a/src/zen/zen.h
+++ b/src/zen/zen.h
@@ -18,7 +18,7 @@ struct ZenCliOptions
ZenLoggingConfig LoggingConfig;
- std::string HttpClientBackend; // Choice of HTTP client implementation (e.g. "curl", "cpr")
+ std::string HttpClientBackend; // Choice of HTTP client implementation
// Arguments after " -- " on command line are passed through and not parsed
std::string PassthroughCommandLine;
diff --git a/src/zenhttp/clients/httpclientcpr.cpp b/src/zenhttp/clients/httpclientcpr.cpp
deleted file mode 100644
index 08e3a8c49..000000000
--- a/src/zenhttp/clients/httpclientcpr.cpp
+++ /dev/null
@@ -1,1285 +0,0 @@
-// Copyright Epic Games, Inc. All Rights Reserved.
-
-#include "httpclientcpr.h"
-
-#include <zencore/compactbinary.h>
-#include <zencore/compactbinarybuilder.h>
-#include <zencore/compactbinarypackage.h>
-#include <zencore/compactbinaryutil.h>
-#include <zencore/compress.h>
-#include <zencore/filesystem.h>
-#include <zencore/iobuffer.h>
-#include <zencore/iohash.h>
-#include <zencore/session.h>
-#include <zencore/stream.h>
-#include <zenhttp/packageformat.h>
-#include <algorithm>
-
-ZEN_THIRD_PARTY_INCLUDES_START
-#include <cpr/ssl_options.h>
-#include <cpr/unix_socket.h>
-ZEN_THIRD_PARTY_INCLUDES_END
-
-namespace zen {
-
-HttpClientBase*
-CreateCprHttpClient(std::string_view BaseUri, const HttpClientSettings& ConnectionSettings, std::function<bool()>&& CheckIfAbortFunction)
-{
- return new CprHttpClient(BaseUri, ConnectionSettings, std::move(CheckIfAbortFunction));
-}
-
-static std::atomic<uint32_t> HttpClientRequestIdCounter{0};
-
-//////////////////////////////////////////////////////////////////////////
-
-static HttpClientErrorCode
-MapCprError(cpr::ErrorCode Code)
-{
- switch (Code)
- {
- case cpr::ErrorCode::OK:
- return HttpClientErrorCode::kOK;
- case cpr::ErrorCode::CONNECTION_FAILURE:
- return HttpClientErrorCode::kConnectionFailure;
- case cpr::ErrorCode::HOST_RESOLUTION_FAILURE:
- return HttpClientErrorCode::kHostResolutionFailure;
- case cpr::ErrorCode::PROXY_RESOLUTION_FAILURE:
- return HttpClientErrorCode::kProxyResolutionFailure;
- case cpr::ErrorCode::INTERNAL_ERROR:
- return HttpClientErrorCode::kInternalError;
- case cpr::ErrorCode::NETWORK_RECEIVE_ERROR:
- return HttpClientErrorCode::kNetworkReceiveError;
- case cpr::ErrorCode::NETWORK_SEND_FAILURE:
- return HttpClientErrorCode::kNetworkSendFailure;
- case cpr::ErrorCode::OPERATION_TIMEDOUT:
- return HttpClientErrorCode::kOperationTimedOut;
- case cpr::ErrorCode::SSL_CONNECT_ERROR:
- return HttpClientErrorCode::kSSLConnectError;
- case cpr::ErrorCode::SSL_LOCAL_CERTIFICATE_ERROR:
- case cpr::ErrorCode::SSL_REMOTE_CERTIFICATE_ERROR:
- return HttpClientErrorCode::kSSLCertificateError;
- case cpr::ErrorCode::SSL_CACERT_ERROR:
- return HttpClientErrorCode::kSSLCACertError;
- case cpr::ErrorCode::GENERIC_SSL_ERROR:
- return HttpClientErrorCode::kGenericSSLError;
- case cpr::ErrorCode::REQUEST_CANCELLED:
- return HttpClientErrorCode::kRequestCancelled;
- default:
- return HttpClientErrorCode::kOtherError;
- }
-}
-
-//////////////////////////////////////////////////////////////////////////
-//
-// CPR helpers
-
-static cpr::Body
-AsCprBody(const CbObject& Obj)
-{
- return cpr::Body((const char*)Obj.GetBuffer().GetData(), Obj.GetBuffer().GetSize());
-}
-
-static cpr::Body
-AsCprBody(const IoBuffer& Obj)
-{
- return cpr::Body((const char*)Obj.GetData(), Obj.GetSize());
-}
-
-static bool
-ShouldRetry(const cpr::Response& Response)
-{
- switch (Response.error.code)
- {
- case cpr::ErrorCode::OK:
- break;
- case cpr::ErrorCode::INTERNAL_ERROR:
- case cpr::ErrorCode::NETWORK_RECEIVE_ERROR:
- case cpr::ErrorCode::NETWORK_SEND_FAILURE:
- case cpr::ErrorCode::OPERATION_TIMEDOUT:
- return true;
- default:
- return false;
- }
- switch ((HttpResponseCode)Response.status_code)
- {
- case HttpResponseCode::RequestTimeout:
- case HttpResponseCode::TooManyRequests:
- case HttpResponseCode::InternalServerError:
- case HttpResponseCode::BadGateway:
- case HttpResponseCode::ServiceUnavailable:
- case HttpResponseCode::GatewayTimeout:
- return true;
- default:
- return false;
- }
-};
-
-static std::pair<std::string, std::string>
-HeaderContentType(ZenContentType ContentType)
-{
- return std::make_pair("Content-Type", std::string(MapContentTypeToString(ContentType)));
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-CprHttpClient::CprHttpClient(std::string_view BaseUri,
- const HttpClientSettings& Connectionsettings,
- std::function<bool()>&& CheckIfAbortFunction)
-: HttpClientBase(BaseUri, Connectionsettings, std::move(CheckIfAbortFunction))
-{
-}
-
-bool
-CprHttpClient::ShouldLogErrorCode(HttpResponseCode ResponseCode) const
-{
- if (m_CheckIfAbortFunction && m_CheckIfAbortFunction())
- {
- // Quiet
- return false;
- }
- const auto& Expected = m_ConnectionSettings.ExpectedErrorCodes;
- return std::find(Expected.begin(), Expected.end(), ResponseCode) == Expected.end();
-}
-
-CprHttpClient::~CprHttpClient()
-{
- ZEN_TRACE_CPU("CprHttpClient::~CprHttpClient");
- m_SessionLock.WithExclusiveLock([&] {
- for (auto CprSession : m_Sessions)
- {
- delete CprSession;
- }
- m_Sessions.clear();
- });
-}
-
-HttpClient::Response
-CprHttpClient::ResponseWithPayload(std::string_view SessionId,
- cpr::Response&& HttpResponse,
- const HttpResponseCode WorkResponseCode,
- IoBuffer&& Payload,
- std::vector<HttpClient::Response::MultipartBoundary>&& BoundaryPositions)
-{
- // This ends up doing a memcpy, would be good to get rid of it by streaming results
- // into buffer directly
- IoBuffer ResponseBuffer = Payload ? std::move(Payload) : IoBuffer(IoBuffer::Clone, HttpResponse.text.data(), HttpResponse.text.size());
-
- if (auto It = HttpResponse.header.find("Content-Type"); It != HttpResponse.header.end())
- {
- const HttpContentType ContentType = ParseContentType(It->second);
- ResponseBuffer.SetContentType(ContentType);
- }
-
- if (!IsHttpSuccessCode(WorkResponseCode) && WorkResponseCode != HttpResponseCode::NotFound)
- {
- if (ShouldLogErrorCode(WorkResponseCode))
- {
- ZEN_WARN("HttpClient request failed (session: {}): {}", SessionId, HttpResponse);
- }
- }
-
- std::sort(BoundaryPositions.begin(),
- BoundaryPositions.end(),
- [](const HttpClient::Response::MultipartBoundary& Lhs, const HttpClient::Response::MultipartBoundary& Rhs) {
- return Lhs.RangeOffset < Rhs.RangeOffset;
- });
-
- return HttpClient::Response{.StatusCode = WorkResponseCode,
- .ResponsePayload = std::move(ResponseBuffer),
- .Header = HttpClient::KeyValueMap(HttpResponse.header.begin(), HttpResponse.header.end()),
- .UploadedBytes = gsl::narrow<int64_t>(HttpResponse.uploaded_bytes),
- .DownloadedBytes = gsl::narrow<int64_t>(HttpResponse.downloaded_bytes),
- .ElapsedSeconds = HttpResponse.elapsed,
- .Ranges = std::move(BoundaryPositions)};
-}
-
-HttpClient::Response
-CprHttpClient::CommonResponse(std::string_view SessionId,
- cpr::Response&& HttpResponse,
- IoBuffer&& Payload,
- std::vector<HttpClient::Response::MultipartBoundary>&& BoundaryPositions)
-{
- const HttpResponseCode WorkResponseCode = HttpResponseCode(HttpResponse.status_code);
- if (HttpResponse.error)
- {
- const bool Quiet = m_CheckIfAbortFunction && m_CheckIfAbortFunction();
- if (!Quiet)
- {
- if (HttpResponse.error.code != cpr::ErrorCode::OPERATION_TIMEDOUT &&
- HttpResponse.error.code != cpr::ErrorCode::CONNECTION_FAILURE &&
- HttpResponse.error.code != cpr::ErrorCode::REQUEST_CANCELLED)
- {
- ZEN_WARN("HttpClient client failure (session: {}): {}", SessionId, HttpResponse);
- }
- }
-
- // Client side failure code
- return HttpClient::Response{
- .StatusCode = WorkResponseCode,
- .ResponsePayload = IoBufferBuilder::MakeCloneFromMemory(HttpResponse.text.data(), HttpResponse.text.size()),
- .Header = HttpClient::KeyValueMap(HttpResponse.header.begin(), HttpResponse.header.end()),
- .UploadedBytes = gsl::narrow<int64_t>(HttpResponse.uploaded_bytes),
- .DownloadedBytes = gsl::narrow<int64_t>(HttpResponse.downloaded_bytes),
- .ElapsedSeconds = HttpResponse.elapsed,
- .Error =
- HttpClient::ErrorContext{.ErrorCode = MapCprError(HttpResponse.error.code), .ErrorMessage = HttpResponse.error.message}};
- }
-
- if (WorkResponseCode == HttpResponseCode::NoContent || (HttpResponse.text.empty() && !Payload))
- {
- return HttpClient::Response{.StatusCode = WorkResponseCode,
- .Header = HttpClient::KeyValueMap(HttpResponse.header.begin(), HttpResponse.header.end()),
- .UploadedBytes = gsl::narrow<int64_t>(HttpResponse.uploaded_bytes),
- .DownloadedBytes = gsl::narrow<int64_t>(HttpResponse.downloaded_bytes),
- .ElapsedSeconds = HttpResponse.elapsed};
- }
- else
- {
- return ResponseWithPayload(SessionId, std::move(HttpResponse), WorkResponseCode, std::move(Payload), std::move(BoundaryPositions));
- }
-}
-
-bool
-CprHttpClient::ValidatePayload(cpr::Response& Response, std::unique_ptr<detail::TempPayloadFile>& PayloadFile)
-{
- ZEN_TRACE_CPU("ValidatePayload");
- IoBuffer ResponseBuffer = (Response.text.empty() && PayloadFile) ? PayloadFile->BorrowIoBuffer()
- : IoBuffer(IoBuffer::Wrap, Response.text.data(), Response.text.size());
-
- if (auto ContentLength = Response.header.find("Content-Length"); ContentLength != Response.header.end())
- {
- std::optional<uint64_t> ExpectedContentSize = ParseInt<uint64_t>(ContentLength->second);
- if (!ExpectedContentSize.has_value())
- {
- Response.error =
- cpr::Error(/*CURLE_READ_ERROR*/ 26, fmt::format("Can not parse Content-Length header. Value: '{}'", ContentLength->second));
- return false;
- }
- if (ExpectedContentSize.value() != ResponseBuffer.GetSize())
- {
- Response.error = cpr::Error(
- /*CURLE_READ_ERROR*/ 26,
- fmt::format("Payload size {} does not match Content-Length {}", ResponseBuffer.GetSize(), ContentLength->second));
- return false;
- }
- }
-
- if (Response.status_code == (long)HttpResponseCode::PartialContent)
- {
- return true;
- }
-
- if (auto JupiterHash = Response.header.find("X-Jupiter-IoHash"); JupiterHash != Response.header.end())
- {
- IoHash ExpectedPayloadHash;
- if (IoHash::TryParse(JupiterHash->second, ExpectedPayloadHash))
- {
- IoHash PayloadHash = IoHash::HashBuffer(ResponseBuffer);
- if (PayloadHash != ExpectedPayloadHash)
- {
- Response.error = cpr::Error(/*CURLE_READ_ERROR*/ 26,
- fmt::format("Payload hash {} does not match X-Jupiter-IoHash {}",
- PayloadHash.ToHexString(),
- ExpectedPayloadHash.ToHexString()));
- return false;
- }
- }
- }
-
- if (auto ContentType = Response.header.find("Content-Type"); ContentType != Response.header.end())
- {
- if (ContentType->second == "application/x-ue-comp")
- {
- IoHash RawHash;
- uint64_t RawSize;
- if (CompressedBuffer::ValidateCompressedHeader(ResponseBuffer, RawHash, RawSize, /*OutOptionalTotalCompressedSize*/ nullptr))
- {
- return true;
- }
- else
- {
- Response.error = cpr::Error(/*CURLE_READ_ERROR*/ 26, "Compressed binary failed validation");
- return false;
- }
- }
- if (ContentType->second == "application/x-ue-cb")
- {
- if (CbValidateError Error = ValidateCompactBinary(ResponseBuffer.GetView(), CbValidateMode::Default);
- Error == CbValidateError::None)
- {
- return true;
- }
- else
- {
- Response.error = cpr::Error(/*CURLE_READ_ERROR*/ 26, fmt::format("Compact binary failed validation: {}", ToString(Error)));
- return false;
- }
- }
- }
-
- return true;
-}
-
-cpr::Response
-CprHttpClient::DoWithRetry(std::string_view SessionId,
- std::function<cpr::Response()>&& Func,
- std::function<bool(cpr::Response& Result)>&& Validate)
-{
- uint8_t Attempt = 0;
- cpr::Response Result = Func();
- while (Attempt < m_ConnectionSettings.RetryCount)
- {
- if (m_CheckIfAbortFunction && m_CheckIfAbortFunction())
- {
- return Result;
- }
- if (!ShouldRetry(Result))
- {
- if (Result.error || !IsHttpSuccessCode(Result.status_code))
- {
- break;
- }
- if (Validate(Result))
- {
- break;
- }
- }
- Sleep(100 * (Attempt + 1));
- Attempt++;
- if (ShouldLogErrorCode(HttpResponseCode(Result.status_code)))
- {
- ZEN_INFO("{} Attempt {}/{}",
- CommonResponse(SessionId, std::move(Result), {}).ErrorMessage("Retry"),
- Attempt,
- m_ConnectionSettings.RetryCount + 1);
- }
- Result = Func();
- }
- return Result;
-}
-
-cpr::Response
-CprHttpClient::DoWithRetry(std::string_view SessionId,
- std::function<cpr::Response()>&& Func,
- std::unique_ptr<detail::TempPayloadFile>& PayloadFile)
-{
- uint8_t Attempt = 0;
- cpr::Response Result = Func();
- while (Attempt < m_ConnectionSettings.RetryCount)
- {
- if (m_CheckIfAbortFunction && m_CheckIfAbortFunction())
- {
- return Result;
- }
- if (!ShouldRetry(Result))
- {
- if (Result.error || !IsHttpSuccessCode(Result.status_code))
- {
- break;
- }
- if (ValidatePayload(Result, PayloadFile))
- {
- break;
- }
- }
- Sleep(100 * (Attempt + 1));
- Attempt++;
- if (ShouldLogErrorCode(HttpResponseCode(Result.status_code)))
- {
- ZEN_INFO("{} Attempt {}/{}",
- CommonResponse(SessionId, std::move(Result), {}).ErrorMessage("Retry"),
- Attempt,
- m_ConnectionSettings.RetryCount + 1);
- }
- Result = Func();
- }
- return Result;
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-CprHttpClient::Session
-CprHttpClient::AllocSession(const std::string_view BaseUrl,
- const std::string_view ResourcePath,
- const HttpClientSettings& ConnectionSettings,
- const KeyValueMap& AdditionalHeader,
- const KeyValueMap& Parameters,
- const std::string_view SessionId,
- std::optional<std::string> AccessToken)
-{
- ZEN_TRACE_CPU("CprHttpClient::AllocSession");
- cpr::Session* CprSession = nullptr;
- m_SessionLock.WithExclusiveLock([&] {
- if (!m_Sessions.empty())
- {
- CprSession = m_Sessions.back();
- m_Sessions.pop_back();
- }
- });
-
- if (CprSession == nullptr)
- {
- CprSession = new cpr::Session();
- CprSession->SetConnectTimeout(ConnectionSettings.ConnectTimeout);
- CprSession->SetTimeout(ConnectionSettings.Timeout);
- if (ConnectionSettings.AssumeHttp2)
- {
- CprSession->SetHttpVersion(cpr::HttpVersion{cpr::HttpVersionCode::VERSION_2_0_PRIOR_KNOWLEDGE});
- }
- if (ConnectionSettings.Verbose)
- {
- // CprSession->SetVerbose(cpr::Verbose{ true });
- CprSession->SetDebugCallback(cpr::DebugCallback{
- [this](cpr::DebugCallback::InfoType type, std::string data, intptr_t userdata) {
- cpr::Session* CprSession = (cpr::Session*)userdata;
- ZEN_UNUSED(CprSession);
- switch (type)
- {
- case cpr::DebugCallback::InfoType::TEXT:
- if (data.find("need more data"sv) == std::string::npos)
- {
- ZEN_INFO("TEXT: {}", data);
- }
- break;
- case cpr::DebugCallback::InfoType::HEADER_IN:
- ZEN_INFO("HIN : {}", data);
- break;
- case cpr::DebugCallback::InfoType::HEADER_OUT:
- if (std::string::size_type TokenPos = data.find("Authorization: Bearer "sv); TokenPos != std::string::npos)
- {
- TokenPos += 22;
- std::string::size_type TokenEndPos = data.find_first_of("\r\n", TokenPos);
- if (TokenEndPos == std::string::npos)
- {
- TokenEndPos = data.length();
- }
- std::string Copy = data;
- Copy.replace(Copy.begin() + TokenPos,
- Copy.begin() + TokenEndPos,
- fmt::format("[{} char token]", TokenEndPos - TokenPos));
- ZEN_INFO("HOUT: {}", Copy);
- }
- else
- {
- ZEN_INFO("HOUT: {}", data);
- }
- break;
- case cpr::DebugCallback::InfoType::DATA_IN:
- // ZEN_INFO("DATA_IN: {}", data);
- break;
- case cpr::DebugCallback::InfoType::DATA_OUT:
- // ZEN_INFO("DATA_OUT: {}", data);
- break;
- case cpr::DebugCallback::InfoType::SSL_DATA_IN:
- // ZEN_INFO("SSL_DATA_IN: {}", data);
- break;
- case cpr::DebugCallback::InfoType::SSL_DATA_OUT:
- // ZEN_INFO("SSL_DATA_OUT: {}", data);
- break;
- }
- },
- (intptr_t)CprSession});
- }
- }
-
- if (!AdditionalHeader->empty())
- {
- CprSession->SetHeader(cpr::Header(AdditionalHeader->begin(), AdditionalHeader->end()));
- }
- if (!SessionId.empty())
- {
- CprSession->UpdateHeader({{"UE-Session", std::string(SessionId)}});
- }
- if (ConnectionSettings.ForbidReuseConnection)
- {
- CprSession->UpdateHeader({{"Connection", "close"}});
- }
-
- if (AccessToken.has_value())
- {
- CprSession->UpdateHeader({{"Authorization", AccessToken.value()}});
- }
- if (!Parameters->empty())
- {
- cpr::Parameters Tmp;
- for (auto It = Parameters->begin(); It != Parameters->end(); It++)
- {
- Tmp.Add({It->first, It->second});
- }
- CprSession->SetParameters(Tmp);
- }
- else
- {
- CprSession->SetParameters({});
- }
-
- if (!ConnectionSettings.UnixSocketPath.empty())
- {
- CprSession->SetUnixSocket(cpr::UnixSocket(PathToUtf8(ConnectionSettings.UnixSocketPath)));
- }
-
- if (ConnectionSettings.InsecureSsl || !ConnectionSettings.CaBundlePath.empty())
- {
- cpr::SslOptions SslOpts;
- if (ConnectionSettings.InsecureSsl)
- {
- SslOpts.SetOption(cpr::ssl::VerifyHost{false});
- SslOpts.SetOption(cpr::ssl::VerifyPeer{false});
- }
- if (!ConnectionSettings.CaBundlePath.empty())
- {
- SslOpts.SetOption(cpr::ssl::CaInfo{ConnectionSettings.CaBundlePath});
- }
- CprSession->SetSslOptions(SslOpts);
- }
-
- ExtendableStringBuilder<128> UrlBuffer;
- UrlBuffer << BaseUrl << ResourcePath;
- CprSession->SetUrl(UrlBuffer.c_str());
-
- return Session(this, CprSession);
-}
-
-void
-CprHttpClient::ReleaseSession(cpr::Session* CprSession)
-{
- ZEN_TRACE_CPU("CprHttpClient::ReleaseSession");
- CprSession->SetUrl({});
- CprSession->SetHeader({});
- CprSession->SetBody({});
- m_SessionLock.WithExclusiveLock([&] { m_Sessions.push_back(CprSession); });
-}
-
-CprHttpClient::Response
-CprHttpClient::TransactPackage(std::string_view Url, CbPackage Package, const KeyValueMap& AdditionalHeader)
-{
- ZEN_TRACE_CPU("CprHttpClient::TransactPackage");
-
- Session Sess = AllocSession(m_BaseUri, Url, m_ConnectionSettings, AdditionalHeader, {}, m_SessionId, GetAccessToken());
-
- // First, list of offered chunks for filtering on the server end
-
- std::vector<IoHash> AttachmentsToSend;
- std::span<const CbAttachment> Attachments = Package.GetAttachments();
-
- const uint32_t RequestId = ++HttpClientRequestIdCounter;
- auto RequestIdString = fmt::to_string(RequestId);
-
- if (Attachments.empty() == false)
- {
- CbObjectWriter Writer;
- Writer.BeginArray("offer");
-
- for (const CbAttachment& Attachment : Attachments)
- {
- Writer.AddHash(Attachment.GetHash());
- }
-
- Writer.EndArray();
-
- BinaryWriter MemWriter;
- Writer.Save(MemWriter);
-
- Sess->UpdateHeader({HeaderContentType(HttpContentType::kCbPackageOffer), {"UE-Request", RequestIdString}});
- Sess->SetBody(cpr::Body{(const char*)MemWriter.Data(), MemWriter.Size()});
-
- cpr::Response FilterResponse = Sess.Post();
-
- if (FilterResponse.status_code == 200)
- {
- IoBuffer ResponseBuffer(IoBuffer::Wrap, FilterResponse.text.data(), FilterResponse.text.size());
- CbValidateError ValidationError = CbValidateError::None;
- if (CbObject ResponseObject = ValidateAndReadCompactBinaryObject(std::move(ResponseBuffer), ValidationError);
- ValidationError == CbValidateError::None)
- {
- for (CbFieldView& Entry : ResponseObject["need"])
- {
- ZEN_ASSERT(Entry.IsHash());
- AttachmentsToSend.push_back(Entry.AsHash());
- }
- }
- }
- }
-
- // Prepare package for send
-
- CbPackage SendPackage;
- SendPackage.SetObject(Package.GetObject(), Package.GetObjectHash());
-
- for (const IoHash& AttachmentCid : AttachmentsToSend)
- {
- const CbAttachment* Attachment = Package.FindAttachment(AttachmentCid);
-
- if (Attachment)
- {
- SendPackage.AddAttachment(*Attachment);
- }
- else
- {
- // This should be an error -- server asked to have something we can't find
- }
- }
-
- // Transmit package payload
-
- CompositeBuffer Message = FormatPackageMessageBuffer(SendPackage);
- SharedBuffer FlatMessage = Message.Flatten();
-
- Sess->UpdateHeader({HeaderContentType(HttpContentType::kCbPackage), {"UE-Request", RequestIdString}});
- Sess->SetBody(cpr::Body{(const char*)FlatMessage.GetData(), FlatMessage.GetSize()});
-
- cpr::Response FilterResponse = Sess.Post();
-
- if (!IsHttpSuccessCode(FilterResponse.status_code))
- {
- return {.StatusCode = HttpResponseCode(FilterResponse.status_code)};
- }
-
- IoBuffer ResponseBuffer(IoBuffer::Clone, FilterResponse.text.data(), FilterResponse.text.size());
-
- if (auto It = FilterResponse.header.find("Content-Type"); It != FilterResponse.header.end())
- {
- HttpContentType ContentType = ParseContentType(It->second);
-
- ResponseBuffer.SetContentType(ContentType);
- }
-
- return {.StatusCode = HttpResponseCode(FilterResponse.status_code), .ResponsePayload = std::move(ResponseBuffer)};
-}
-
-//////////////////////////////////////////////////////////////////////////
-//
-// Standard HTTP verbs
-//
-
-CprHttpClient::Response
-CprHttpClient::Put(std::string_view Url, const IoBuffer& Payload, const KeyValueMap& AdditionalHeader, const KeyValueMap& Parameters)
-{
- ZEN_TRACE_CPU("CprHttpClient::Put");
-
- return CommonResponse(
- m_SessionId,
- DoWithRetry(m_SessionId,
- [&]() {
- Session Sess =
- AllocSession(m_BaseUri, Url, m_ConnectionSettings, AdditionalHeader, Parameters, m_SessionId, GetAccessToken());
- Sess->SetBody(AsCprBody(Payload));
- Sess->UpdateHeader({HeaderContentType(Payload.GetContentType())});
- return Sess.Put();
- }),
- {});
-}
-
-CprHttpClient::Response
-CprHttpClient::Put(std::string_view Url, const KeyValueMap& Parameters)
-{
- ZEN_TRACE_CPU("CprHttpClient::Put");
-
- return CommonResponse(m_SessionId,
- DoWithRetry(m_SessionId,
- [&]() {
- Session Sess = AllocSession(m_BaseUri,
- Url,
- m_ConnectionSettings,
- {{"Content-Length", "0"}},
- Parameters,
- m_SessionId,
- GetAccessToken());
- return Sess.Put();
- }),
- {});
-}
-
-CprHttpClient::Response
-CprHttpClient::Get(std::string_view Url, const KeyValueMap& AdditionalHeader, const KeyValueMap& Parameters)
-{
- ZEN_TRACE_CPU("CprHttpClient::Get");
- return CommonResponse(
- m_SessionId,
- DoWithRetry(
- m_SessionId,
- [&]() {
- Session Sess =
- AllocSession(m_BaseUri, Url, m_ConnectionSettings, AdditionalHeader, Parameters, m_SessionId, GetAccessToken());
- return Sess.Get();
- },
- [this](cpr::Response& Result) {
- std::unique_ptr<detail::TempPayloadFile> NoTempFile;
- return ValidatePayload(Result, NoTempFile);
- }),
- {});
-}
-
-CprHttpClient::Response
-CprHttpClient::Head(std::string_view Url, const KeyValueMap& AdditionalHeader)
-{
- ZEN_TRACE_CPU("CprHttpClient::Head");
-
- return CommonResponse(
- m_SessionId,
- DoWithRetry(m_SessionId,
- [&]() {
- Session Sess =
- AllocSession(m_BaseUri, Url, m_ConnectionSettings, AdditionalHeader, {}, m_SessionId, GetAccessToken());
- return Sess.Head();
- }),
- {});
-}
-
-CprHttpClient::Response
-CprHttpClient::Delete(std::string_view Url, const KeyValueMap& AdditionalHeader)
-{
- ZEN_TRACE_CPU("CprHttpClient::Delete");
-
- return CommonResponse(
- m_SessionId,
- DoWithRetry(m_SessionId,
- [&]() {
- Session Sess =
- AllocSession(m_BaseUri, Url, m_ConnectionSettings, AdditionalHeader, {}, m_SessionId, GetAccessToken());
- return Sess.Delete();
- }),
- {});
-}
-
-CprHttpClient::Response
-CprHttpClient::Post(std::string_view Url, const KeyValueMap& AdditionalHeader, const KeyValueMap& Parameters)
-{
- ZEN_TRACE_CPU("CprHttpClient::PostNoPayload");
-
- return CommonResponse(
- m_SessionId,
- DoWithRetry(m_SessionId,
- [&]() {
- Session Sess =
- AllocSession(m_BaseUri, Url, m_ConnectionSettings, AdditionalHeader, Parameters, m_SessionId, GetAccessToken());
- return Sess.Post();
- }),
- {});
-}
-
-CprHttpClient::Response
-CprHttpClient::Post(std::string_view Url, const IoBuffer& Payload, const KeyValueMap& AdditionalHeader)
-{
- return Post(Url, Payload, Payload.GetContentType(), AdditionalHeader);
-}
-
-CprHttpClient::Response
-CprHttpClient::Post(std::string_view Url, const IoBuffer& Payload, ZenContentType ContentType, const KeyValueMap& AdditionalHeader)
-{
- ZEN_TRACE_CPU("CprHttpClient::PostWithPayload");
-
- return CommonResponse(
- m_SessionId,
- DoWithRetry(
- m_SessionId,
- [&]() {
- Session Sess = AllocSession(m_BaseUri, Url, m_ConnectionSettings, AdditionalHeader, {}, m_SessionId, GetAccessToken());
- Sess->UpdateHeader({HeaderContentType(ContentType)});
-
- IoBufferFileReference FileRef = {nullptr, 0, 0};
- if (Payload.GetFileReference(FileRef))
- {
- uint64_t Offset = 0;
- detail::BufferedReadFileStream Buffer(FileRef.FileHandle, FileRef.FileChunkOffset, FileRef.FileChunkSize, 512u * 1024u);
- auto ReadCallback = [&Payload, &Offset, &Buffer](char* buffer, size_t& size, intptr_t) {
- size = Min<size_t>(size, Payload.GetSize() - Offset);
- Buffer.Read(buffer, size);
- Offset += size;
- return true;
- };
- return Sess.Post(cpr::ReadCallback(gsl::narrow<cpr::cpr_off_t>(Payload.GetSize()), ReadCallback));
- }
- Sess->SetBody(AsCprBody(Payload));
- return Sess.Post();
- }),
- {});
-}
-
-CprHttpClient::Response
-CprHttpClient::Post(std::string_view Url,
- CbObject Payload,
- const KeyValueMap& AdditionalHeader,
- const std::filesystem::path& TempFolderPath)
-{
- ZEN_TRACE_CPU("CprHttpClient::PostObjectPayload");
-
- std::string PayloadString;
- std::unique_ptr<detail::TempPayloadFile> PayloadFile;
-
- cpr::Response Response = DoWithRetry(
- m_SessionId,
- [&]() {
- PayloadString.clear();
- PayloadFile.reset();
-
- Session Sess = AllocSession(m_BaseUri, Url, m_ConnectionSettings, AdditionalHeader, {}, m_SessionId, GetAccessToken());
-
- Sess->SetBody(AsCprBody(Payload));
- Sess->UpdateHeader({HeaderContentType(ZenContentType::kCbObject)});
-
- std::vector<std::pair<std::string, std::string>> ReceivedHeaders;
- auto HeaderCallback = [&](std::string header, intptr_t) {
- const std::pair<std::string_view, std::string_view> Header = detail::GetHeaderKeyAndValue(header);
- if (StrCaseCompare(std::string(Header.first).c_str(), "Content-Length") == 0)
- {
- std::optional<size_t> ContentLength = ParseInt<size_t>(Header.second);
- if (ContentLength.has_value())
- {
- if (!TempFolderPath.empty() && ContentLength.value() > m_ConnectionSettings.MaximumInMemoryDownloadSize)
- {
- PayloadFile = std::make_unique<detail::TempPayloadFile>();
- std::error_code Ec = PayloadFile->Open(TempFolderPath, ContentLength.value());
- if (Ec)
- {
- ZEN_WARN("Failed to create temp file in '{}' for HttpClient::Post. Reason: {}",
- TempFolderPath.string(),
- Ec.message());
- PayloadFile.reset();
- }
- }
- else
- {
- PayloadString.reserve(ContentLength.value());
- }
- }
- }
- if (!Header.first.empty())
- {
- ReceivedHeaders.emplace_back(std::move(Header));
- }
- return 1;
- };
-
- auto DownloadCallback = [&](std::string data, intptr_t) {
- if (m_CheckIfAbortFunction && m_CheckIfAbortFunction())
- {
- return false;
- }
-
- if (PayloadFile)
- {
- ZEN_ASSERT(PayloadString.empty());
- std::error_code Ec = PayloadFile->Write(data);
- if (Ec)
- {
- ZEN_WARN("Failed to write to temp file in '{}' for HttpClient::Post. Reason: {}",
- TempFolderPath.string(),
- Ec.message());
- return false;
- }
- }
- else
- {
- PayloadString.append(data);
- }
- return true;
- };
- cpr::Response Response = Sess.Post({}, cpr::WriteCallback{DownloadCallback}, cpr::HeaderCallback{HeaderCallback});
- for (const std::pair<std::string, std::string>& H : ReceivedHeaders)
- {
- Response.header.insert_or_assign(H.first, H.second);
- }
- if (!PayloadString.empty())
- {
- Response.text = std::move(PayloadString);
- }
- return Response;
- },
- PayloadFile);
- return CommonResponse(m_SessionId, std::move(Response), PayloadFile ? PayloadFile->DetachToIoBuffer() : IoBuffer{});
-}
-
-CprHttpClient::Response
-CprHttpClient::Post(std::string_view Url, CbPackage Pkg, const KeyValueMap& AdditionalHeader)
-{
- return Post(Url, zen::FormatPackageMessageBuffer(Pkg), ZenContentType::kCbPackage, AdditionalHeader);
-}
-
-CprHttpClient::Response
-CprHttpClient::Post(std::string_view Url, const CompositeBuffer& Payload, ZenContentType ContentType, const KeyValueMap& AdditionalHeader)
-{
- ZEN_TRACE_CPU("CprHttpClient::Post");
-
- return CommonResponse(
- m_SessionId,
- DoWithRetry(m_SessionId,
- [&]() {
- Session Sess =
- AllocSession(m_BaseUri, Url, m_ConnectionSettings, AdditionalHeader, {}, m_SessionId, GetAccessToken());
- Sess->UpdateHeader({HeaderContentType(ContentType)});
-
- detail::CompositeBufferReadStream Reader(Payload, 512u * 1024u);
- auto ReadCallback = [this, &Reader](char* buffer, size_t& size, intptr_t) {
- if (m_CheckIfAbortFunction && m_CheckIfAbortFunction())
- {
- return false;
- }
- size = Reader.Read(buffer, size);
- return true;
- };
- return Sess.Post(cpr::ReadCallback(gsl::narrow<cpr::cpr_off_t>(Payload.GetSize()), ReadCallback));
- }),
- {});
-}
-
-CprHttpClient::Response
-CprHttpClient::Upload(std::string_view Url, const IoBuffer& Payload, const KeyValueMap& AdditionalHeader)
-{
- ZEN_TRACE_CPU("CprHttpClient::Upload");
-
- return CommonResponse(
- m_SessionId,
- DoWithRetry(
- m_SessionId,
- [&]() {
- Session Sess = AllocSession(m_BaseUri, Url, m_ConnectionSettings, AdditionalHeader, {}, m_SessionId, GetAccessToken());
- Sess->UpdateHeader({HeaderContentType(Payload.GetContentType())});
-
- IoBufferFileReference FileRef = {nullptr, 0, 0};
- if (Payload.GetFileReference(FileRef))
- {
- uint64_t Offset = 0;
- detail::BufferedReadFileStream Buffer(FileRef.FileHandle, FileRef.FileChunkOffset, FileRef.FileChunkSize, 512u * 1024u);
- auto ReadCallback = [this, &Payload, &Offset, &Buffer](char* buffer, size_t& size, intptr_t) {
- if (m_CheckIfAbortFunction && m_CheckIfAbortFunction())
- {
- return false;
- }
-
- size = Min<size_t>(size, Payload.GetSize() - Offset);
- Buffer.Read(buffer, size);
- Offset += size;
- return true;
- };
- return Sess.Put(cpr::ReadCallback(gsl::narrow<cpr::cpr_off_t>(Payload.GetSize()), ReadCallback));
- }
- Sess->SetBody(AsCprBody(Payload));
- return Sess.Put();
- }),
- {});
-}
-
-CprHttpClient::Response
-CprHttpClient::Upload(std::string_view Url, const CompositeBuffer& Payload, ZenContentType ContentType, const KeyValueMap& AdditionalHeader)
-{
- ZEN_TRACE_CPU("CprHttpClient::Upload");
-
- return CommonResponse(
- m_SessionId,
- DoWithRetry(m_SessionId,
- [&]() {
- Session Sess =
- AllocSession(m_BaseUri, Url, m_ConnectionSettings, AdditionalHeader, {}, m_SessionId, GetAccessToken());
- Sess->UpdateHeader({HeaderContentType(ContentType)});
-
- detail::CompositeBufferReadStream Reader(Payload, 512u * 1024u);
- auto ReadCallback = [this, &Reader](char* buffer, size_t& size, intptr_t) {
- if (m_CheckIfAbortFunction && m_CheckIfAbortFunction())
- {
- return false;
- }
- size = Reader.Read(buffer, size);
- return true;
- };
- return Sess.Put(cpr::ReadCallback(gsl::narrow<cpr::cpr_off_t>(Payload.GetSize()), ReadCallback));
- }),
- {});
-}
-
-CprHttpClient::Response
-CprHttpClient::Download(std::string_view Url, const std::filesystem::path& TempFolderPath, const KeyValueMap& AdditionalHeader)
-{
- ZEN_TRACE_CPU("CprHttpClient::Download");
-
- std::string PayloadString;
- std::unique_ptr<detail::TempPayloadFile> PayloadFile;
-
- HttpContentType ContentType = HttpContentType::kUnknownContentType;
- detail::MultipartBoundaryParser BoundaryParser;
- bool IsMultiRangeResponse = false;
-
- cpr::Response Response = DoWithRetry(
- m_SessionId,
- [&]() {
- // Reset state from any previous attempt
- PayloadString.clear();
- PayloadFile.reset();
- BoundaryParser.Boundaries.clear();
- ContentType = HttpContentType::kUnknownContentType;
- IsMultiRangeResponse = false;
-
- auto DownloadCallback = [&](std::string data, intptr_t) {
- if (m_CheckIfAbortFunction && m_CheckIfAbortFunction())
- {
- return false;
- }
-
- if (IsMultiRangeResponse)
- {
- BoundaryParser.ParseInput(data);
- }
-
- if (PayloadFile)
- {
- ZEN_ASSERT(PayloadString.empty());
- std::error_code Ec = PayloadFile->Write(data);
- if (Ec)
- {
- ZEN_WARN("Failed to write to temp file in '{}' for HttpClient::Download. Reason: {}",
- TempFolderPath.string(),
- Ec.message());
- return false;
- }
- }
- else
- {
- PayloadString.append(data);
- }
- return true;
- };
-
- uint64_t RequestedContentLength = (uint64_t)-1;
- if (auto RangeIt = AdditionalHeader.Entries.find("Range"); RangeIt != AdditionalHeader.Entries.end())
- {
- if (RangeIt->second.starts_with("bytes"))
- {
- std::string_view RangeValue(RangeIt->second);
- size_t RangeStartPos = RangeValue.find('=', 5);
- if (RangeStartPos != std::string::npos)
- {
- RangeStartPos++;
- while (RangeStartPos < RangeValue.length() && RangeValue[RangeStartPos] == ' ')
- {
- RangeStartPos++;
- }
- RequestedContentLength = 0;
-
- while (RangeStartPos < RangeValue.length())
- {
- size_t RangeEnd = RangeValue.find_first_of(", \r\n", RangeStartPos);
- if (RangeEnd == std::string::npos)
- {
- RangeEnd = RangeValue.length();
- }
-
- std::string_view RangeString = RangeValue.substr(RangeStartPos, RangeEnd - RangeStartPos);
- size_t RangeSplitPos = RangeString.find('-');
- if (RangeSplitPos != std::string::npos)
- {
- std::optional<size_t> RequestedRangeStart = ParseInt<size_t>(RangeString.substr(0, RangeSplitPos));
- std::optional<size_t> RequestedRangeEnd = ParseInt<size_t>(RangeString.substr(RangeSplitPos + 1));
- if (RequestedRangeStart.has_value() && RequestedRangeEnd.has_value())
- {
- RequestedContentLength += RequestedRangeEnd.value() - RequestedRangeStart.value() + 1;
- }
- }
- RangeStartPos = RangeEnd;
- while (RangeStartPos != RangeValue.length() &&
- (RangeValue[RangeStartPos] == ',' || RangeValue[RangeStartPos] == ' '))
- {
- RangeStartPos++;
- }
- }
- }
- }
- }
-
- cpr::Response Response;
- {
- std::vector<std::pair<std::string, std::string>> ReceivedHeaders;
- auto HeaderCallback = [&](std::string header, intptr_t) {
- if (RequestedContentLength != (uint64_t)-1 && RequestedContentLength > m_ConnectionSettings.MaximumInMemoryDownloadSize)
- {
- ZEN_DEBUG("Multirange request");
- }
- const std::pair<std::string_view, std::string_view> Header = detail::GetHeaderKeyAndValue(header);
- const std::string Key(Header.first);
- if (StrCaseCompare(Key.c_str(), "Content-Length") == 0)
- {
- std::optional<size_t> ContentLength = ParseInt<size_t>(Header.second);
- if (ContentLength.has_value())
- {
- if (!TempFolderPath.empty() && ContentLength.value() > m_ConnectionSettings.MaximumInMemoryDownloadSize)
- {
- PayloadFile = std::make_unique<detail::TempPayloadFile>();
- std::error_code Ec = PayloadFile->Open(TempFolderPath, ContentLength.value());
- if (Ec)
- {
- ZEN_WARN("Failed to create temp file in '{}' for HttpClient::Download. Reason: {}",
- TempFolderPath.string(),
- Ec.message());
- PayloadFile.reset();
- }
- }
- else
- {
- PayloadString.reserve(ContentLength.value());
- }
- }
- }
- else if (StrCaseCompare(Key.c_str(), "Content-Type") == 0)
- {
- IsMultiRangeResponse = BoundaryParser.Init(Header.second);
- if (!IsMultiRangeResponse)
- {
- ContentType = ParseContentType(Header.second);
- }
- }
- else if (StrCaseCompare(Key.c_str(), "Content-Range") == 0)
- {
- if (!IsMultiRangeResponse)
- {
- std::pair<uint64_t, uint64_t> Range = detail::ParseContentRange(Header.second);
- if (Range.second != 0)
- {
- BoundaryParser.Boundaries.push_back(HttpClient::Response::MultipartBoundary{.OffsetInPayload = 0,
- .RangeOffset = Range.first,
- .RangeLength = Range.second,
- .ContentType = ContentType});
- }
- }
- }
- if (!Header.first.empty())
- {
- ReceivedHeaders.emplace_back(std::move(Header));
- }
- return 1;
- };
-
- Session Sess = AllocSession(m_BaseUri, Url, m_ConnectionSettings, AdditionalHeader, {}, m_SessionId, GetAccessToken());
- Response = Sess.Download(cpr::WriteCallback{DownloadCallback}, cpr::HeaderCallback{HeaderCallback});
- for (const std::pair<std::string, std::string>& H : ReceivedHeaders)
- {
- Response.header.insert_or_assign(H.first, H.second);
- }
- }
- if (m_ConnectionSettings.AllowResume)
- {
- auto SupportsRanges = [](const cpr::Response& Response) -> bool {
- if (Response.header.find("Content-Range") != Response.header.end())
- {
- return true;
- }
- if (auto It = Response.header.find("Accept-Ranges"); It != Response.header.end())
- {
- return It->second == "bytes"sv;
- }
- return false;
- };
-
- auto ShouldResume = [&SupportsRanges, &IsMultiRangeResponse](const cpr::Response& Response) -> bool {
- if (IsMultiRangeResponse)
- {
- return false;
- }
- if (ShouldRetry(Response))
- {
- return SupportsRanges(Response);
- }
- return false;
- };
-
- if (ShouldResume(Response))
- {
- auto It = Response.header.find("Content-Length");
- if (It != Response.header.end())
- {
- uint64_t ContentLength = RequestedContentLength;
- if (ContentLength == uint64_t(-1))
- {
- if (auto ParsedContentLength = ParseInt<int64_t>(It->second); ParsedContentLength.has_value())
- {
- ContentLength = ParsedContentLength.value();
- }
- }
-
- std::vector<std::pair<std::string, std::string>> ReceivedHeaders;
-
- auto HeaderCallback = [&](std::string header, intptr_t) {
- const std::pair<std::string_view, std::string_view> Header = detail::GetHeaderKeyAndValue(header);
- if (!Header.first.empty())
- {
- ReceivedHeaders.emplace_back(std::move(Header));
- }
-
- if (StrCaseCompare(std::string(Header.first).c_str(), "Content-Range") == 0)
- {
- if (Header.second.starts_with("bytes "sv))
- {
- size_t RangeStartEnd = Header.second.find('-', 6);
- if (RangeStartEnd != std::string::npos)
- {
- const auto Start = ParseInt<uint64_t>(Header.second.substr(6, RangeStartEnd - 6));
- if (Start)
- {
- uint64_t DownloadedSize = PayloadFile ? PayloadFile->GetSize() : PayloadString.length();
- if (Start.value() == DownloadedSize)
- {
- return 1;
- }
- else if (Start.value() > DownloadedSize)
- {
- return 0;
- }
- if (PayloadFile)
- {
- PayloadFile->ResetWritePos(Start.value());
- }
- else
- {
- PayloadString = PayloadString.substr(0, Start.value());
- }
- return 1;
- }
- }
- }
- return 0;
- }
- return 1;
- };
-
- KeyValueMap HeadersWithRange(AdditionalHeader);
- do
- {
- uint64_t DownloadedSize = PayloadFile ? PayloadFile->GetSize() : PayloadString.length();
-
- std::string Range = fmt::format("bytes={}-{}", DownloadedSize, DownloadedSize + ContentLength - 1);
- if (auto RangeIt = HeadersWithRange.Entries.find("Range"); RangeIt != HeadersWithRange.Entries.end())
- {
- if (RangeIt->second == Range)
- {
- // If we didn't make any progress, abort
- break;
- }
- }
- HeadersWithRange.Entries.insert_or_assign("Range", Range);
-
- Session Sess =
- AllocSession(m_BaseUri, Url, m_ConnectionSettings, HeadersWithRange, {}, m_SessionId, GetAccessToken());
- Response = Sess.Download(cpr::WriteCallback{DownloadCallback}, cpr::HeaderCallback{HeaderCallback});
- for (const std::pair<std::string, std::string>& H : ReceivedHeaders)
- {
- Response.header.insert_or_assign(H.first, H.second);
- }
- ReceivedHeaders.clear();
- } while (ShouldResume(Response));
- }
- }
- }
-
- if (!PayloadString.empty())
- {
- Response.text = std::move(PayloadString);
- }
- return Response;
- },
- PayloadFile);
-
- return CommonResponse(m_SessionId,
- std::move(Response),
- PayloadFile ? PayloadFile->DetachToIoBuffer() : IoBuffer{},
- std::move(BoundaryParser.Boundaries));
-}
-
-} // namespace zen
diff --git a/src/zenhttp/clients/httpclientcpr.h b/src/zenhttp/clients/httpclientcpr.h
deleted file mode 100644
index 451f22eae..000000000
--- a/src/zenhttp/clients/httpclientcpr.h
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright Epic Games, Inc. All Rights Reserved.
-
-#pragma once
-
-#include "httpclientcommon.h"
-
-#include <zencore/logging.h>
-#include <zenhttp/cprutils.h>
-#include <zenhttp/httpclient.h>
-
-ZEN_THIRD_PARTY_INCLUDES_START
-#include <cpr/body.h>
-#include <cpr/session.h>
-ZEN_THIRD_PARTY_INCLUDES_END
-
-namespace zen {
-
-class CprHttpClient : public HttpClientBase
-{
-public:
- CprHttpClient(std::string_view BaseUri, const HttpClientSettings& Connectionsettings, std::function<bool()>&& CheckIfAbortFunction);
- ~CprHttpClient();
-
- // HttpClientBase
-
- [[nodiscard]] virtual Response Put(std::string_view Url,
- const IoBuffer& Payload,
- const KeyValueMap& AdditionalHeader = {},
- const KeyValueMap& Parameters = {}) 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 Session
- {
- Session(CprHttpClient* InOuter, cpr::Session* InSession) : Outer(InOuter), CprSession(InSession) {}
- ~Session() { Outer->ReleaseSession(CprSession); }
-
- inline cpr::Session* operator->() const { return CprSession; }
- inline cpr::Response Get()
- {
- ZEN_TRACE_CPU("HttpClient::Impl::Get");
- cpr::Response Result = CprSession->Get();
- ZEN_TRACE("GET {}", Result);
- return Result;
- }
- inline cpr::Response Download(cpr::WriteCallback&& Write, std::optional<cpr::HeaderCallback>&& Header = {})
- {
- ZEN_TRACE_CPU("HttpClient::Impl::Download");
- if (Header)
- {
- CprSession->SetHeaderCallback(std::move(Header.value()));
- }
- cpr::Response Result = CprSession->Download(Write);
- ZEN_TRACE("GET {}", Result);
- CprSession->SetHeaderCallback({});
- CprSession->SetWriteCallback({});
- return Result;
- }
- inline cpr::Response Head()
- {
- ZEN_TRACE_CPU("HttpClient::Impl::Head");
- cpr::Response Result = CprSession->Head();
- ZEN_TRACE("HEAD {}", Result);
- return Result;
- }
- inline cpr::Response Put(std::optional<cpr::ReadCallback>&& Read = {})
- {
- ZEN_TRACE_CPU("HttpClient::Impl::Put");
- if (Read)
- {
- CprSession->SetReadCallback(std::move(Read.value()));
- }
- cpr::Response Result = CprSession->Put();
- ZEN_TRACE("PUT {}", Result);
- CprSession->SetReadCallback({});
- return Result;
- }
- inline cpr::Response Post(std::optional<cpr::ReadCallback>&& Read = {},
- std::optional<cpr::WriteCallback>&& Write = {},
- std::optional<cpr::HeaderCallback>&& Header = {})
- {
- ZEN_TRACE_CPU("HttpClient::Impl::Post");
- if (Read)
- {
- CprSession->SetReadCallback(std::move(Read.value()));
- }
- if (Write)
- {
- CprSession->SetWriteCallback(std::move(Write.value()));
- }
- if (Header)
- {
- CprSession->SetHeaderCallback(std::move(Header.value()));
- }
- cpr::Response Result = CprSession->Post();
- ZEN_TRACE("POST {}", Result);
- CprSession->SetHeaderCallback({});
- CprSession->SetWriteCallback({});
- CprSession->SetReadCallback({});
- return Result;
- }
- inline cpr::Response Delete()
- {
- ZEN_TRACE_CPU("HttpClient::Impl::Delete");
- cpr::Response Result = CprSession->Delete();
- ZEN_TRACE("DELETE {}", Result);
- return Result;
- }
-
- LoggerRef Log() { return Outer->Log(); }
-
- private:
- CprHttpClient* Outer;
- cpr::Session* CprSession;
-
- Session(Session&&) = delete;
- Session& operator=(Session&&) = delete;
- };
-
- Session AllocSession(const std::string_view BaseUrl,
- const std::string_view Url,
- const HttpClientSettings& ConnectionSettings,
- const KeyValueMap& AdditionalHeader,
- const KeyValueMap& Parameters,
- const std::string_view SessionId,
- std::optional<std::string> AccessToken);
-
- RwLock m_SessionLock;
- std::vector<cpr::Session*> m_Sessions;
-
- void ReleaseSession(cpr::Session*);
-
- cpr::Response DoWithRetry(std::string_view SessionId,
- std::function<cpr::Response()>&& Func,
- std::unique_ptr<detail::TempPayloadFile>& PayloadFile);
- cpr::Response DoWithRetry(
- std::string_view SessionId,
- std::function<cpr::Response()>&& Func,
- std::function<bool(cpr::Response& Result)>&& Validate = [](cpr::Response&) { return true; });
-
- bool ShouldLogErrorCode(HttpResponseCode ResponseCode) const;
- bool ValidatePayload(cpr::Response& Response, std::unique_ptr<detail::TempPayloadFile>& PayloadFile);
-
- HttpClient::Response CommonResponse(std::string_view SessionId,
- cpr::Response&& HttpResponse,
- IoBuffer&& Payload,
- std::vector<HttpClient::Response::MultipartBoundary>&& BoundaryPositions = {});
-
- HttpClient::Response ResponseWithPayload(std::string_view SessionId,
- cpr::Response&& HttpResponse,
- const HttpResponseCode WorkResponseCode,
- IoBuffer&& Payload,
- std::vector<HttpClient::Response::MultipartBoundary>&& BoundaryPositions);
-};
-
-} // namespace zen
diff --git a/src/zenhttp/httpclient.cpp b/src/zenhttp/httpclient.cpp
index 96107883e..ace7a3c7f 100644
--- a/src/zenhttp/httpclient.cpp
+++ b/src/zenhttp/httpclient.cpp
@@ -36,12 +36,6 @@
namespace zen {
-#if ZEN_WITH_CPR
-extern HttpClientBase* CreateCprHttpClient(std::string_view BaseUri,
- const HttpClientSettings& ConnectionSettings,
- std::function<bool()>&& CheckIfAbortFunction);
-#endif
-
extern HttpClientBase* CreateCurlHttpClient(std::string_view BaseUri,
const HttpClientSettings& ConnectionSettings,
std::function<bool()>&& CheckIfAbortFunction);
@@ -57,14 +51,7 @@ SetDefaultHttpClientBackend(HttpClientBackend Backend)
void
SetDefaultHttpClientBackend(std::string_view Backend)
{
-#if ZEN_WITH_CPR
- if (Backend == "cpr")
- {
- g_DefaultHttpClientBackend = HttpClientBackend::kCpr;
- }
- else
-#endif
- if (Backend == "curl")
+ if (Backend == "curl")
{
g_DefaultHttpClientBackend = HttpClientBackend::kCurl;
}
@@ -378,22 +365,7 @@ HttpClient::HttpClient(std::string_view BaseUri, const HttpClientSettings& Conne
, m_ConnectionSettings(ConnectionSettings)
{
m_SessionId = GetSessionIdString();
-
- HttpClientBackend EffectiveBackend =
- ConnectionSettings.Backend != HttpClientBackend::kDefault ? ConnectionSettings.Backend : g_DefaultHttpClientBackend;
-
- switch (EffectiveBackend)
- {
-#if ZEN_WITH_CPR
- case HttpClientBackend::kCpr:
- m_Inner = CreateCprHttpClient(BaseUri, ConnectionSettings, std::move(CheckIfAbortFunction));
- break;
-#endif
- case HttpClientBackend::kCurl:
- default:
- m_Inner = CreateCurlHttpClient(BaseUri, ConnectionSettings, std::move(CheckIfAbortFunction));
- break;
- }
+ m_Inner = CreateCurlHttpClient(BaseUri, ConnectionSettings, std::move(CheckIfAbortFunction));
}
HttpClient::~HttpClient()
diff --git a/src/zenhttp/include/zenhttp/cprutils.h b/src/zenhttp/include/zenhttp/cprutils.h
deleted file mode 100644
index 3cfe652c5..000000000
--- a/src/zenhttp/include/zenhttp/cprutils.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright Epic Games, Inc. All Rights Reserved.
-
-#pragma once
-
-#if ZEN_WITH_CPR
-
-# include <zencore/compactbinary.h>
-# include <zencore/compactbinaryvalidation.h>
-# include <zencore/iobuffer.h>
-# include <zencore/string.h>
-# include <zenhttp/formatters.h>
-# include <zenhttp/httpclient.h>
-# include <zenhttp/httpcommon.h>
-
-ZEN_THIRD_PARTY_INCLUDES_START
-# include <cpr/response.h>
-# include <fmt/format.h>
-ZEN_THIRD_PARTY_INCLUDES_END
-
-template<>
-struct fmt::formatter<cpr::Response>
-{
- constexpr auto parse(format_parse_context& Ctx) -> decltype(Ctx.begin()) { return Ctx.end(); }
-
- template<typename FormatContext>
- auto format(const cpr::Response& Response, FormatContext& Ctx) const -> decltype(Ctx.out())
- {
- using namespace std::literals;
-
- if (Response.error)
- {
- return fmt::format_to(Ctx.out(),
- "Failed: Url: {}, Reason: ({}) '{}'",
- Response.url.str(),
- int(Response.error.code),
- Response.error.message);
- }
- else
- {
- const zen::NiceTimeSpanMs NiceResponseTime(uint64_t(Response.elapsed * 1000));
-
- if (zen::IsHttpSuccessCode(Response.status_code))
- {
- return fmt::format_to(Ctx.out(),
- "OK: Url: {}, Status: ({}) '{}', Bytes: {}/{} (Up/Down), Elapsed: {}",
- Response.url.str(),
- Response.status_code,
- zen::ToString(zen::HttpResponseCode(Response.status_code)),
- Response.uploaded_bytes,
- Response.downloaded_bytes,
- NiceResponseTime.c_str());
- }
- else
- {
- const auto It = Response.header.find("Content-Type");
- const std::string_view ContentType = It != Response.header.end() ? It->second : "<None>"sv;
-
- if (ContentType == "application/x-ue-cb"sv)
- {
- zen::IoBuffer Body(zen::IoBuffer::Wrap, Response.text.data(), Response.text.size());
- zen::CbObjectView Obj(Body.Data());
- zen::ExtendableStringBuilder<256> Sb;
- std::string_view Json = Obj.ToJson(Sb).ToView();
-
- return fmt::format_to(
- Ctx.out(),
- "Failed: Url: {}, Status: ({}) '{}', Reason: '{}'. Bytes: {}/{} (Up/Down), Elapsed: {}, Response: '{}'",
- Response.url.str(),
- Response.status_code,
- zen::ToString(zen::HttpResponseCode(Response.status_code)),
- Response.reason,
- Response.uploaded_bytes,
- Response.downloaded_bytes,
- NiceResponseTime.c_str(),
- Json);
- }
- else
- {
- zen::BodyLogFormatter Body(Response.text);
-
- return fmt::format_to(
- Ctx.out(),
- "Failed: Url: {}, Status: ({}) '{}', Reason: '{}'. Bytes: {}/{} (Up/Down), Elapsed: {}, Response: '{}'",
- Response.url.str(),
- Response.status_code,
- zen::ToString(zen::HttpResponseCode(Response.status_code)),
- Response.reason,
- Response.uploaded_bytes,
- Response.downloaded_bytes,
- NiceResponseTime.c_str(),
- Body.GetText());
- }
- }
- }
- }
-};
-
-#endif // ZEN_WITH_CPR
diff --git a/src/zenhttp/include/zenhttp/httpclient.h b/src/zenhttp/include/zenhttp/httpclient.h
index 26d60b9ae..e199b700f 100644
--- a/src/zenhttp/include/zenhttp/httpclient.h
+++ b/src/zenhttp/include/zenhttp/httpclient.h
@@ -52,9 +52,6 @@ enum class HttpClientErrorCode : int
enum class HttpClientBackend : uint8_t
{
kDefault,
-#if ZEN_WITH_CPR
- kCpr,
-#endif
kCurl,
};
diff --git a/src/zenhttp/xmake.lua b/src/zenhttp/xmake.lua
index b4c65ea96..7b050ae35 100644
--- a/src/zenhttp/xmake.lua
+++ b/src/zenhttp/xmake.lua
@@ -9,12 +9,7 @@ target('zenhttp')
add_files("servers/wshttpsys.cpp", {unity_ignored=true})
add_includedirs("include", {public=true})
add_deps("zencore", "zentelemetry", "transport-sdk", "asio")
- if has_config("zencpr") then
- add_deps("cpr")
- else
- remove_files("clients/httpclientcpr.cpp")
- end
- add_packages("http_parser", "json11")
+ add_packages("http_parser", "json11", "libcurl")
add_options("httpsys")
if is_plat("linux", "macosx") then
diff --git a/src/zenserver-test/cache-tests.cpp b/src/zenserver-test/cache-tests.cpp
index 334dd04ab..14748e214 100644
--- a/src/zenserver-test/cache-tests.cpp
+++ b/src/zenserver-test/cache-tests.cpp
@@ -1193,14 +1193,10 @@ TEST_CASE("zcache.rpc")
// CbPackage Package;
// CHECK(Request.Format(Package));
- // IoBuffer Body = FormatPackageMessageBuffer(Package).Flatten().AsIoBuffer();
- // cpr::Response Result = cpr::Post(cpr::Url{fmt::format("{}/$rpc", LocalCfg.BaseUri)},
- // cpr::Header{{"Content-Type", "application/x-ue-cbpkg"}, {"Accept", "application/x-ue-cbpkg"}},
- // cpr::Body{(const char*)Body.GetData(), Body.GetSize()});
+ // IoBuffer Body = FormatPackageMessageBuffer(Package).Flatten().AsIoBuffer();
+ // // TODO: rewrite using HttpClient instead of removed CPR dependency
- // CHECK(Result.status_code == 200);
// cacherequests::PutCacheRecordsResult ParsedResult;
- // CbPackage Response = ParsePackageMessage(zen::IoBuffer(zen::IoBuffer::Wrap, Result.text.data(), Result.text.size()));
// CHECK(!Response.IsNull());
// CHECK(ParsedResult.Parse(Response));
// for (bool ResponseSuccess : ParsedResult.Success)
diff --git a/src/zenserver/config/config.cpp b/src/zenserver/config/config.cpp
index 15f6f79f3..daad154bc 100644
--- a/src/zenserver/config/config.cpp
+++ b/src/zenserver/config/config.cpp
@@ -417,7 +417,7 @@ ZenServerCmdLineOptions::AddCliOptions(cxxopts::Options& options, ZenServerConfi
options.add_option("network",
"",
"httpclient",
- "Select HTTP client implementation (e.g. 'curl', 'cpr')",
+ "Select HTTP client implementation",
cxxopts::value<std::string>(ServerOptions.HttpClient.Backend)->default_value("curl"),
"<http client>");
diff --git a/src/zenserver/config/config.h b/src/zenserver/config/config.h
index 5078fe71a..d35a1a8c7 100644
--- a/src/zenserver/config/config.h
+++ b/src/zenserver/config/config.h
@@ -40,7 +40,7 @@ struct ZenSentryConfig
struct HttpClientConfig
{
- std::string Backend = "cpr"; // Choice of HTTP client implementation (e.g. "curl", "cpr")
+ std::string Backend = "curl"; // Choice of HTTP client implementation
};
struct ZenServerConfig
diff --git a/src/zenutil/xmake.lua b/src/zenutil/xmake.lua
index 1e19f7b2f..83a6b7f93 100644
--- a/src/zenutil/xmake.lua
+++ b/src/zenutil/xmake.lua
@@ -11,6 +11,10 @@ target('zenutil')
add_deps("robin-map")
add_packages("json11")
+ if is_plat("linux", "macosx") then
+ add_packages("openssl3")
+ end
+
if is_plat("linux") then
add_includedirs("$(projectdir)/thirdparty/systemd/include")
add_linkdirs("$(projectdir)/thirdparty/systemd/lib")