aboutsummaryrefslogtreecommitdiff
path: root/src/zenhttp
diff options
context:
space:
mode:
Diffstat (limited to 'src/zenhttp')
-rw-r--r--src/zenhttp/httpclient.cpp237
-rw-r--r--src/zenhttp/httpserver.cpp6
-rw-r--r--src/zenhttp/include/zenhttp/formatters.h65
-rw-r--r--src/zenhttp/include/zenhttp/httpserver.h8
-rw-r--r--src/zenhttp/packageformat.cpp24
-rw-r--r--src/zenhttp/servers/httpsys.cpp18
6 files changed, 263 insertions, 95 deletions
diff --git a/src/zenhttp/httpclient.cpp b/src/zenhttp/httpclient.cpp
index 211a5d05c..30711a432 100644
--- a/src/zenhttp/httpclient.cpp
+++ b/src/zenhttp/httpclient.cpp
@@ -414,6 +414,11 @@ ValidatePayload(cpr::Response& Response, std::unique_ptr<detail::TempPayloadFile
}
}
+ if (Response.status_code == (long)HttpResponseCode::PartialContent)
+ {
+ return true;
+ }
+
if (auto ContentType = Response.header.find("Content-Type"); ContentType != Response.header.end())
{
if (ContentType->second == "application/x-ue-comp")
@@ -449,12 +454,27 @@ ValidatePayload(cpr::Response& Response, std::unique_ptr<detail::TempPayloadFile
}
static cpr::Response
-DoWithRetry(std::string_view SessionId, std::function<cpr::Response()>&& Func, uint8_t RetryCount)
+DoWithRetry(
+ std::string_view SessionId,
+ std::function<cpr::Response()>&& Func,
+ uint8_t RetryCount,
+ std::function<bool(cpr::Response& Result)>&& Validate = [](cpr::Response&) { return true; })
{
uint8_t Attempt = 0;
cpr::Response Result = Func();
- while (Attempt < RetryCount && ShouldRetry(Result))
+ while (Attempt < RetryCount)
{
+ if (!ShouldRetry(Result))
+ {
+ if (Result.error || !IsHttpSuccessCode(Result.status_code))
+ {
+ break;
+ }
+ if (Validate(Result))
+ {
+ break;
+ }
+ }
Sleep(100 * (Attempt + 1));
Attempt++;
ZEN_INFO("{} Attempt {}/{}", CommonResponse(SessionId, std::move(Result)).ErrorMessage("Retry"), Attempt, RetryCount + 1);
@@ -881,7 +901,11 @@ HttpClient::Get(std::string_view Url, const KeyValueMap& AdditionalHeader, const
m_Impl->AllocSession(m_BaseUri, Url, m_ConnectionSettings, AdditionalHeader, Parameters, m_SessionId, GetAccessToken());
return Sess.Get();
},
- m_ConnectionSettings.RetryCount));
+ m_ConnectionSettings.RetryCount,
+ [](cpr::Response& Result) {
+ std::unique_ptr<detail::TempPayloadFile> NoTempFile;
+ return ValidatePayload(Result, NoTempFile);
+ }));
}
HttpClient::Response
@@ -1124,6 +1148,30 @@ HttpClient::Download(std::string_view Url, const std::filesystem::path& TempFold
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"))
+ {
+ size_t RangeStartPos = RangeIt->second.find('=', 5);
+ if (RangeStartPos != std::string::npos)
+ {
+ RangeStartPos++;
+ size_t RangeSplitPos = RangeIt->second.find('-', RangeStartPos);
+ if (RangeSplitPos != std::string::npos)
+ {
+ std::optional<size_t> RequestedRangeStart =
+ ParseInt<size_t>(RangeIt->second.substr(RangeStartPos, RangeSplitPos - RangeStartPos));
+ std::optional<size_t> RequestedRangeEnd = ParseInt<size_t>(RangeIt->second.substr(RangeStartPos + 1));
+ if (RequestedRangeStart.has_value() && RequestedRangeEnd.has_value())
+ {
+ RequestedContentLength = RequestedRangeEnd.value() - 1;
+ }
+ }
+ }
+ }
+ }
+
cpr::Response Response;
{
std::vector<std::pair<std::string, std::string>> ReceivedHeaders;
@@ -1131,10 +1179,10 @@ HttpClient::Download(std::string_view Url, const std::filesystem::path& TempFold
std::pair<std::string, std::string> Header = GetHeader(header);
if (Header.first == "Content-Length"sv)
{
- std::optional<size_t> ContentSize = ParseInt<size_t>(Header.second);
- if (ContentSize.has_value())
+ std::optional<size_t> ContentLength = ParseInt<size_t>(Header.second);
+ if (ContentLength.has_value())
{
- if (ContentSize.value() > 1024 * 1024)
+ if (ContentLength.value() > 1024 * 1024)
{
PayloadFile = std::make_unique<detail::TempPayloadFile>();
std::error_code Ec = PayloadFile->Open(TempFolderPath);
@@ -1148,7 +1196,7 @@ HttpClient::Download(std::string_view Url, const std::filesystem::path& TempFold
}
else
{
- PayloadString.reserve(ContentSize.value());
+ PayloadString.reserve(ContentLength.value());
}
}
}
@@ -1194,85 +1242,90 @@ HttpClient::Download(std::string_view Url, const std::filesystem::path& TempFold
auto It = Response.header.find("Content-Length");
if (It != Response.header.end())
{
- std::optional<int64_t> ContentLength = ParseInt<int64_t>(It->second);
- if (ContentLength)
- {
- std::vector<std::pair<std::string, std::string>> ReceivedHeaders;
+ std::vector<std::pair<std::string, std::string>> ReceivedHeaders;
- auto HeaderCallback = [&](std::string header, intptr_t) {
- std::pair<std::string, std::string> Header = GetHeader(header);
- if (!Header.first.empty())
- {
- ReceivedHeaders.emplace_back(std::move(Header));
- }
+ auto HeaderCallback = [&](std::string header, intptr_t) {
+ std::pair<std::string, std::string> Header = GetHeader(header);
+ if (!Header.first.empty())
+ {
+ ReceivedHeaders.emplace_back(std::move(Header));
+ }
- if (Header.first == "Content-Range"sv)
+ if (Header.first == "Content-Range"sv)
+ {
+ if (Header.second.starts_with("bytes "sv))
{
- if (Header.second.starts_with("bytes "sv))
+ size_t RangeStartEnd = Header.second.find('-', 6);
+ if (RangeStartEnd != std::string::npos)
{
- 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)
{
- 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)
{
- 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;
}
+ 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;
- };
+ return 0;
+ }
+ return 1;
+ };
- KeyValueMap HeadersWithRange(AdditionalHeader);
- do
- {
- uint64_t DownloadedSize = PayloadFile ? PayloadFile->GetSize() : PayloadString.length();
+ KeyValueMap HeadersWithRange(AdditionalHeader);
+ do
+ {
+ uint64_t DownloadedSize = PayloadFile ? PayloadFile->GetSize() : PayloadString.length();
- std::string Range = fmt::format("bytes={}-{}", DownloadedSize, ContentLength.value());
- if (auto RangeIt = HeadersWithRange.Entries.find("Range"); RangeIt != HeadersWithRange.Entries.end())
+ uint64_t ContentLength = RequestedContentLength;
+ if (ContentLength == uint64_t(-1))
+ {
+ if (auto ParsedContentLength = ParseInt<int64_t>(It->second); ParsedContentLength.has_value())
{
- if (RangeIt->second == Range)
- {
- // If we didn't make any progress, abort
- break;
- }
+ ContentLength = ParsedContentLength.value();
}
- HeadersWithRange.Entries.insert_or_assign("Range", Range);
-
- Impl::Session Sess = m_Impl->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)
+ }
+
+ 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)
{
- Response.header.insert_or_assign(H.first, H.second);
+ // If we didn't make any progress, abort
+ break;
}
- ReceivedHeaders.clear();
- } while (ShouldResume(Response));
- }
+ }
+ HeadersWithRange.Entries.insert_or_assign("Range", Range);
+
+ Impl::Session Sess = m_Impl->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));
}
}
}
@@ -1398,6 +1451,52 @@ HttpClient::Response::ThrowError(std::string_view ErrorPrefix)
#if ZEN_WITH_TESTS
+TEST_CASE("responseformat")
+{
+ using namespace std::literals;
+
+ SUBCASE("identity")
+ {
+ BodyLogFormatter _{"abcd"};
+ CHECK_EQ(_.GetText(), "abcd"sv);
+ }
+
+ SUBCASE("very long")
+ {
+ std::string_view LongView =
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
+
+ BodyLogFormatter _{LongView};
+
+ CHECK(_.GetText().size() < LongView.size());
+ CHECK(_.GetText().starts_with("[truncated"sv));
+ }
+
+ SUBCASE("invalid text")
+ {
+ std::string_view BadText = "totobaba\xff\xfe";
+
+ BodyLogFormatter _{BadText};
+
+ CHECK_EQ(_.GetText(), "totobaba");
+ }
+}
+
TEST_CASE("httpclient")
{
using namespace std::literals;
diff --git a/src/zenhttp/httpserver.cpp b/src/zenhttp/httpserver.cpp
index 1fbe22628..27a09f339 100644
--- a/src/zenhttp/httpserver.cpp
+++ b/src/zenhttp/httpserver.cpp
@@ -31,6 +31,8 @@
#include <span>
#include <string_view>
+#include <EASTL/fixed_vector.h>
+
namespace zen {
using namespace std::literals;
@@ -529,7 +531,7 @@ HttpServerRequest::WriteResponse(HttpResponseCode ResponseCode, HttpContentType
{
std::span<const SharedBuffer> Segments = Payload.GetSegments();
- std::vector<IoBuffer> Buffers;
+ eastl::fixed_vector<IoBuffer, 64> Buffers;
Buffers.reserve(Segments.size());
for (auto& Segment : Segments)
@@ -537,7 +539,7 @@ HttpServerRequest::WriteResponse(HttpResponseCode ResponseCode, HttpContentType
Buffers.push_back(Segment.AsIoBuffer());
}
- WriteResponse(ResponseCode, ContentType, Buffers);
+ WriteResponse(ResponseCode, ContentType, std::span<IoBuffer>(begin(Buffers), end(Buffers)));
}
std::string
diff --git a/src/zenhttp/include/zenhttp/formatters.h b/src/zenhttp/include/zenhttp/formatters.h
index 538136238..74da9ab05 100644
--- a/src/zenhttp/include/zenhttp/formatters.h
+++ b/src/zenhttp/include/zenhttp/formatters.h
@@ -7,12 +7,57 @@
#include <zencore/iobuffer.h>
#include <zencore/string.h>
#include <zenhttp/httpclient.h>
+#include <zenhttp/httpcommon.h>
ZEN_THIRD_PARTY_INCLUDES_START
#include <cpr/cpr.h>
#include <fmt/format.h>
ZEN_THIRD_PARTY_INCLUDES_END
+namespace zen {
+
+struct BodyLogFormatter
+{
+private:
+ std::string_view ResponseText;
+ zen::ExtendableStringBuilder<128> ModifiedResponse;
+
+public:
+ explicit BodyLogFormatter(std::string_view InResponseText) : ResponseText(InResponseText)
+ {
+ using namespace std::literals;
+
+ const int TextSizeLimit = 1024;
+
+ // Trim invalid UTF8
+
+ auto InvalidIt = zen::FindFirstInvalidUtf8Byte(ResponseText);
+
+ if (InvalidIt != end(ResponseText))
+ {
+ ResponseText = ResponseText.substr(0, InvalidIt - begin(ResponseText));
+ }
+
+ if (ResponseText.empty())
+ {
+ ResponseText = "<suppressed non-text response>"sv;
+ }
+
+ if (ResponseText.size() > TextSizeLimit)
+ {
+ const auto TruncatedString = "[truncated response] "sv;
+ ModifiedResponse.Append(TruncatedString);
+ ModifiedResponse.Append(ResponseText.data(), TextSizeLimit - TruncatedString.size());
+
+ ResponseText = ModifiedResponse;
+ }
+ }
+
+ inline std::string_view GetText() const { return ResponseText; }
+};
+
+} // namespace zen
+
template<>
struct fmt::formatter<cpr::Response>
{
@@ -23,15 +68,17 @@ struct fmt::formatter<cpr::Response>
{
using namespace std::literals;
- if (Response.status_code == 200 || Response.status_code == 201)
+ zen::NiceTimeSpanMs NiceResponseTime(uint64_t(Response.elapsed * 1000));
+
+ if (zen::IsHttpSuccessCode(Response.status_code))
{
return fmt::format_to(Ctx.out(),
- "Url: {}, Status: {}, Bytes: {}/{} (Up/Down), Elapsed: {}s",
+ "Url: {}, Status: {}, Bytes: {}/{} (Up/Down), Elapsed: {}",
Response.url.str(),
Response.status_code,
Response.uploaded_bytes,
Response.downloaded_bytes,
- Response.elapsed);
+ NiceResponseTime.c_str());
}
else
{
@@ -46,25 +93,27 @@ struct fmt::formatter<cpr::Response>
std::string_view Json = Obj.ToJson(Sb).ToView();
return fmt::format_to(Ctx.out(),
- "Url: {}, Status: {}, Bytes: {}/{} (Up/Down), Elapsed: {}s, Response: '{}', Reason: '{}'",
+ "Url: {}, Status: {}, Bytes: {}/{} (Up/Down), Elapsed: {}, Response: '{}', Reason: '{}'",
Response.url.str(),
Response.status_code,
Response.uploaded_bytes,
Response.downloaded_bytes,
- Response.elapsed,
+ NiceResponseTime.c_str(),
Json,
Response.reason);
}
else
{
+ zen::BodyLogFormatter Body(Response.text);
+
return fmt::format_to(Ctx.out(),
- "Url: {}, Status: {}, Bytes: {}/{} (Up/Down), Elapsed: {}s, Reponse: '{}', Reason: '{}'",
+ "Url: {}, Status: {}, Bytes: {}/{} (Up/Down), Elapsed: {}, Response: '{}', Reason: '{}'",
Response.url.str(),
Response.status_code,
Response.uploaded_bytes,
Response.downloaded_bytes,
- Response.elapsed,
- Response.text,
+ NiceResponseTime.c_str(),
+ Body.GetText(),
Response.reason);
}
}
diff --git a/src/zenhttp/include/zenhttp/httpserver.h b/src/zenhttp/include/zenhttp/httpserver.h
index 7b87cb84b..217455dba 100644
--- a/src/zenhttp/include/zenhttp/httpserver.h
+++ b/src/zenhttp/include/zenhttp/httpserver.h
@@ -208,7 +208,7 @@ class HttpRouterRequest
public:
HttpRouterRequest(HttpServerRequest& Request) : m_HttpRequest(Request) {}
- ZENCORE_API std::string GetCapture(uint32_t Index) const;
+ std::string_view GetCapture(uint32_t Index) const;
inline HttpServerRequest& ServerRequest() { return m_HttpRequest; }
private:
@@ -220,12 +220,14 @@ private:
friend class HttpRequestRouter;
};
-inline std::string
+inline std::string_view
HttpRouterRequest::GetCapture(uint32_t Index) const
{
ZEN_ASSERT(Index < m_Match.size());
- return m_Match[Index];
+ const auto& Match = m_Match[Index];
+
+ return std::string_view(&*Match.first, Match.second - Match.first);
}
/** HTTP request router helper
diff --git a/src/zenhttp/packageformat.cpp b/src/zenhttp/packageformat.cpp
index 676fc73fd..ae80851e4 100644
--- a/src/zenhttp/packageformat.cpp
+++ b/src/zenhttp/packageformat.cpp
@@ -19,6 +19,8 @@
#include <span>
#include <vector>
+#include <EASTL/fixed_vector.h>
+
#if ZEN_PLATFORM_WINDOWS
# include <zencore/windows.h>
#endif
@@ -31,6 +33,10 @@ namespace zen {
const std::string_view HandlePrefix(":?#:");
+typedef eastl::fixed_vector<IoBuffer, 16> IoBufferVec_t;
+
+IoBufferVec_t FormatPackageMessageInternal(const CbPackage& Data, FormatFlags Flags, void* TargetProcessHandle);
+
std::vector<IoBuffer>
FormatPackageMessage(const CbPackage& Data, void* TargetProcessHandle)
{
@@ -42,10 +48,18 @@ FormatPackageMessageBuffer(const CbPackage& Data, void* TargetProcessHandle)
return FormatPackageMessageBuffer(Data, FormatFlags::kDefault, TargetProcessHandle);
}
+std::vector<IoBuffer>
+FormatPackageMessage(const CbPackage& Data, FormatFlags Flags, void* TargetProcessHandle)
+{
+ auto Vec = FormatPackageMessageInternal(Data, Flags, TargetProcessHandle);
+ return std::vector<IoBuffer>(begin(Vec), end(Vec));
+}
+
CompositeBuffer
FormatPackageMessageBuffer(const CbPackage& Data, FormatFlags Flags, void* TargetProcessHandle)
{
- return CompositeBuffer(FormatPackageMessage(Data, Flags, TargetProcessHandle));
+ auto Vec = FormatPackageMessageInternal(Data, Flags, TargetProcessHandle);
+ return CompositeBuffer(std::span{begin(Vec), end(Vec)});
}
static void
@@ -54,7 +68,7 @@ MarshalLocal(CbAttachmentEntry*& AttachmentInfo,
CbAttachmentReferenceHeader& LocalRef,
const IoHash& AttachmentHash,
bool IsCompressed,
- std::vector<IoBuffer>& ResponseBuffers)
+ IoBufferVec_t& ResponseBuffers)
{
IoBuffer RefBuffer(sizeof(CbAttachmentReferenceHeader) + Path8.size());
@@ -146,8 +160,8 @@ IsLocalRef(tsl::robin_map<void*, std::string>& FileNameMap,
return true;
};
-std::vector<IoBuffer>
-FormatPackageMessage(const CbPackage& Data, FormatFlags Flags, void* TargetProcessHandle)
+IoBufferVec_t
+FormatPackageMessageInternal(const CbPackage& Data, FormatFlags Flags, void* TargetProcessHandle)
{
ZEN_TRACE_CPU("FormatPackageMessage");
@@ -177,7 +191,7 @@ FormatPackageMessage(const CbPackage& Data, FormatFlags Flags, void* TargetProce
#endif // ZEN_PLATFORM_WINDOWS
const std::span<const CbAttachment>& Attachments = Data.GetAttachments();
- std::vector<IoBuffer> ResponseBuffers;
+ IoBufferVec_t ResponseBuffers;
ResponseBuffers.reserve(2 + Attachments.size()); // TODO: may want to use an additional fudge factor here to avoid growing since each
// attachment is likely to consist of several buffers
diff --git a/src/zenhttp/servers/httpsys.cpp b/src/zenhttp/servers/httpsys.cpp
index 87128c0c9..3bdcdf098 100644
--- a/src/zenhttp/servers/httpsys.cpp
+++ b/src/zenhttp/servers/httpsys.cpp
@@ -16,6 +16,8 @@
#include <zencore/trace.h>
#include <zenhttp/packageformat.h>
+#include <EASTL/fixed_vector.h>
+
#if ZEN_WITH_HTTPSYS
# define _WINSOCKAPI_
# include <zencore/windows.h>
@@ -381,14 +383,14 @@ public:
void SuppressResponseBody(); // typically used for HEAD requests
private:
- std::vector<HTTP_DATA_CHUNK> m_HttpDataChunks;
- uint64_t m_TotalDataSize = 0; // Sum of all chunk sizes
- uint16_t m_ResponseCode = 0;
- uint32_t m_NextDataChunkOffset = 0; // Cursor used for very large chunk lists
- uint32_t m_RemainingChunkCount = 0; // Backlog for multi-call sends
- bool m_IsInitialResponse = true;
- HttpContentType m_ContentType = HttpContentType::kBinary;
- std::vector<IoBuffer> m_DataBuffers;
+ eastl::fixed_vector<HTTP_DATA_CHUNK, 16> m_HttpDataChunks;
+ uint64_t m_TotalDataSize = 0; // Sum of all chunk sizes
+ uint16_t m_ResponseCode = 0;
+ uint32_t m_NextDataChunkOffset = 0; // Cursor used for very large chunk lists
+ uint32_t m_RemainingChunkCount = 0; // Backlog for multi-call sends
+ bool m_IsInitialResponse = true;
+ HttpContentType m_ContentType = HttpContentType::kBinary;
+ eastl::fixed_vector<IoBuffer, 16> m_DataBuffers;
void InitializeForPayload(uint16_t ResponseCode, std::span<IoBuffer> Blobs);
};