// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include #include #include #include namespace zen { using namespace std::literals; class HttpClientBase { public: HttpClientBase(std::string_view BaseUri, const HttpClientSettings& Connectionsettings, std::function&& CheckIfAbortFunction); virtual ~HttpClientBase() = 0; using Response = HttpClient::Response; using KeyValueMap = HttpClient::KeyValueMap; [[nodiscard]] virtual Response Put(std::string_view Url, const IoBuffer& Payload, const KeyValueMap& AdditionalHeader = {}) = 0; [[nodiscard]] virtual Response Put(std::string_view Url, const KeyValueMap& Parameters = {}) = 0; [[nodiscard]] virtual Response Get(std::string_view Url, const KeyValueMap& AdditionalHeader = {}, const KeyValueMap& Parameters = {}) = 0; [[nodiscard]] virtual Response Head(std::string_view Url, const KeyValueMap& AdditionalHeader = {}) = 0; [[nodiscard]] virtual Response Delete(std::string_view Url, const KeyValueMap& AdditionalHeader = {}) = 0; [[nodiscard]] virtual Response Post(std::string_view Url, const KeyValueMap& AdditionalHeader = {}, const KeyValueMap& Parameters = {}) = 0; [[nodiscard]] virtual Response Post(std::string_view Url, const IoBuffer& Payload, const KeyValueMap& AdditionalHeader = {}) = 0; [[nodiscard]] virtual Response Post(std::string_view Url, const IoBuffer& Payload, ZenContentType ContentType, const KeyValueMap& AdditionalHeader = {}) = 0; [[nodiscard]] virtual Response Post(std::string_view Url, CbObject Payload, const KeyValueMap& AdditionalHeader = {}) = 0; [[nodiscard]] virtual Response Post(std::string_view Url, CbPackage Payload, const KeyValueMap& AdditionalHeader = {}) = 0; [[nodiscard]] virtual Response Post(std::string_view Url, const CompositeBuffer& Payload, ZenContentType ContentType, const KeyValueMap& AdditionalHeader = {}) = 0; [[nodiscard]] virtual Response Upload(std::string_view Url, const IoBuffer& Payload, const KeyValueMap& AdditionalHeader = {}) = 0; [[nodiscard]] virtual Response Upload(std::string_view Url, const CompositeBuffer& Payload, ZenContentType ContentType, const KeyValueMap& AdditionalHeader = {}) = 0; [[nodiscard]] virtual Response Download(std::string_view Url, const std::filesystem::path& TempFolderPath, const KeyValueMap& AdditionalHeader = {}) = 0; [[nodiscard]] virtual Response TransactPackage(std::string_view Url, CbPackage Package, const KeyValueMap& AdditionalHeader = {}) = 0; LoggerRef Log() { return m_Log; } std::string_view GetBaseUri() const { return m_BaseUri; } std::string_view GetSessionId() const { return m_SessionId; } bool Authenticate(); protected: LoggerRef m_Log; std::string m_BaseUri; std::string m_SessionId; const HttpClientSettings m_ConnectionSettings; std::function m_CheckIfAbortFunction; const std::optional GetAccessToken(); RwLock m_AccessTokenLock; HttpClientAccessToken m_CachedAccessToken; }; namespace detail { class TempPayloadFile { public: TempPayloadFile(const TempPayloadFile&) = delete; TempPayloadFile& operator=(const TempPayloadFile&) = delete; TempPayloadFile(); ~TempPayloadFile(); std::error_code Open(const std::filesystem::path& TempFolderPath, uint64_t FinalSize); std::error_code Write(std::string_view DataString); IoBuffer DetachToIoBuffer(); IoBuffer BorrowIoBuffer(); inline uint64_t GetSize() const { return m_WriteOffset + m_CacheBufferOffset; } void ResetWritePos(uint64_t WriteOffset); private: std::error_code Flush(); std::error_code AppendData(const void* Data, uint64_t Size); void* m_FileHandle; std::uint64_t m_WriteOffset; static constexpr uint64_t CacheBufferSize = 512u * 1024u; uint8_t m_CacheBuffer[CacheBufferSize]; std::uint64_t m_CacheBufferOffset = 0; }; class BufferedReadFileStream { public: BufferedReadFileStream(const BufferedReadFileStream&) = delete; BufferedReadFileStream& operator=(const BufferedReadFileStream&) = delete; BufferedReadFileStream(void* FileHandle, uint64_t FileOffset, uint64_t FileSize, uint64_t BufferSize); ~BufferedReadFileStream(); void Read(void* Data, uint64_t Size); private: void Read(void* Data, uint64_t BytesToRead, uint64_t FileOffset); void* m_FileHandle = nullptr; const uint64_t m_FileSize = 0; const uint64_t m_FileEnd = 0; uint64_t m_BufferSize = 0; uint8_t* m_Buffer = nullptr; uint64_t m_BufferStart = 0; uint64_t m_BufferEnd = 0; uint64_t m_FileOffset = 0; }; class CompositeBufferReadStream { public: CompositeBufferReadStream(const CompositeBufferReadStream&) = delete; CompositeBufferReadStream& operator=(const CompositeBufferReadStream&) = delete; CompositeBufferReadStream(const CompositeBuffer& Data, uint64_t BufferSize); uint64_t Read(void* Data, uint64_t Size); private: const CompositeBuffer& m_Data; const uint64_t m_BufferSize; size_t m_SegmentIndex; std::unique_ptr m_SegmentDiskBuffer; MemoryView m_SegmentMemoryBuffer; uint64_t m_BytesLeftInSegment; }; class IncrementalStringMatcher { public: enum class EMatchState { None, Partial, Complete }; EMatchState MatchState = EMatchState::None; IncrementalStringMatcher() {} IncrementalStringMatcher(std::string&& InMatchString) : MatchString(std::move(InMatchString)) { RawMatchString = MatchString.data(); } void Init(std::string&& InMatchString) { MatchString = std::move(InMatchString); RawMatchString = MatchString.data(); } inline void Reset() { MatchLength = 0; MatchStartOffset = 0; MatchState = EMatchState::None; } inline uint64_t GetMatchEndOffset() const { if (MatchState == EMatchState::Complete) { return MatchStartOffset + MatchString.length(); } return 0; } inline uint64_t GetMatchStartOffset() const { ZEN_ASSERT(MatchState == EMatchState::Complete); return MatchStartOffset; } void Match(uint64_t Offset, char C) { ZEN_ASSERT_SLOW(RawMatchString != nullptr); if (MatchState == EMatchState::Complete) { Reset(); } if (C == RawMatchString[MatchLength]) { if (MatchLength == 0) { MatchStartOffset = Offset; } MatchLength++; if (MatchLength == MatchString.length()) { MatchState = EMatchState::Complete; } else { MatchState = EMatchState::Partial; } } else if (MatchLength != 0) { Reset(); Match(Offset, C); } else { Reset(); } } inline const std::string& GetMatchString() const { return MatchString; } private: std::string MatchString; const char* RawMatchString = nullptr; uint64_t MatchLength = 0; uint64_t MatchStartOffset = 0; }; class MultipartBoundaryParser { public: std::vector Boundaries; MultipartBoundaryParser(); bool Init(const std::string_view ContentTypeHeaderValue); void ParseInput(std::string_view data); private: IncrementalStringMatcher BoundaryBeginMatcher; IncrementalStringMatcher BoundaryEndMatcher; IncrementalStringMatcher HeaderEndMatcher; ExtendableStringBuilder<64> BoundaryHeader; uint64_t PayloadOffset = 0; }; std::pair GetHeaderKeyAndValue(std::string_view HeaderString); std::pair ParseContentRange(std::string_view Value); } // namespace detail } // namespace zen