// Copyright Epic Games, Inc. All Rights Reserved. #include #include #include #include #include #include #include #include #include #include #include static std::atomic HttpClientRequestIdCounter{0}; namespace zen { using namespace std::literals; HttpClient::Response FromCprResponse(cpr::Response& InResponse) { return {.StatusCode = int(InResponse.status_code)}; } ////////////////////////////////////////////////////////////////////////// HttpClient::HttpClient(std::string_view BaseUri) : m_BaseUri(BaseUri) { StringBuilder<32> SessionId; GetSessionId().ToString(SessionId); m_SessionId = SessionId; } 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(); BinaryWriter MemWriter; Writer.Save(MemWriter); Sess.SetHeader({{"Content-Type", "application/x-ue-offer"}, {"UE-Session", m_SessionId}, {"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()); 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", m_SessionId}, {"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 = int(FilterResponse.status_code), .ResponsePayload = ResponseBuffer}; } HttpClient::Response HttpClient::Put(std::string_view Url, IoBuffer Payload) { ZEN_UNUSED(Url); ZEN_UNUSED(Payload); return {}; } HttpClient::Response HttpClient::Get(std::string_view Url) { ZEN_UNUSED(Url); return {}; } HttpClient::Response HttpClient::Delete(std::string_view Url) { ZEN_UNUSED(Url); return {}; } ////////////////////////////////////////////////////////////////////////// #if ZEN_WITH_TESTS TEST_CASE("httpclient") { using namespace std::literals; SUBCASE("client") {} } void httpclient_forcelink() { } #endif } // namespace zen