// Copyright Epic Games, Inc. All Rights Reserved. #include #include #include #include #include #include #include #include #include "httpshared.h" #include static std::atomic HttpClientRequestIdCounter{0}; namespace zen { using namespace std::literals; HttpClient::Response FromCprResponse(cpr::Response& InResponse) { return {.StatusCode = InResponse.status_code}; } ////////////////////////////////////////////////////////////////////////// HttpClient::HttpClient(std::string_view BaseUri) : m_BaseUri(BaseUri) { } HttpClient::~HttpClient() { } HttpClient::Response HttpClient::TransactPackage(std::string_view Url, CbPackage Package) { cpr::Session Sess; Sess.SetUrl(m_BaseUri + std::string(Url)); // First, list of offered chunks for filtering on the server end std::vector AttachmentsToSend; std::span 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) { IoHash Hash = Attachment.GetHash(); Writer.AddHash(Hash); } Writer.EndArray(); MemoryOutStream MemOut; BinaryWriter MemWriter(MemOut); Writer.Save(MemWriter); Sess.SetHeader( {{"Content-Type", "application/x-ue-offer"}, {"UE-Session", "123456789012345678901234"}, {"UE-Request", RequestIdString}}); Sess.SetBody(cpr::Body{(const char*)MemOut.Data(), MemOut.Size()}); cpr::Response FilterResponse = Sess.Post(); if (FilterResponse.status_code == 200) { IoBuffer ResponseBuffer(IoBuffer::Wrap, FilterResponse.text.data(), FilterResponse.text.size()); CbObject ResponseObject = LoadCompactBinaryObject(ResponseBuffer); for (auto& 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.SetHeader( {{"Content-Type", "application/x-ue-cbpkg"}, {"UE-Session", "123456789012345678901234"}, {"UE-Request", RequestIdString}}); Sess.SetBody(cpr::Body{(const char*)FlatMessage.GetData(), FlatMessage.GetSize()}); cpr::Response FilterResponse = Sess.Post(); if (!IsHttpSuccessCode(FilterResponse.status_code)) { return FromCprResponse(FilterResponse); } 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 = FilterResponse.status_code, .ResponsePayload = ResponseBuffer}; } HttpClient::Response HttpClient::Delete(std::string_view Url) { ZEN_UNUSED(Url); return {}; } ////////////////////////////////////////////////////////////////////////// TEST_CASE("httpclient") { using namespace std::literals; SUBCASE("client") {} } void httpclient_forcelink() { } } // namespace zen