From 9fc1337ba1b9c8198c030a57cb5c5942bbb2b725 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 8 Apr 2026 15:51:42 +0200 Subject: use correct return code for unsupported multirange requests in objectstore (#927) --- src/zenserver/storage/objectstore/objectstore.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/zenserver/storage/objectstore/objectstore.cpp') diff --git a/src/zenserver/storage/objectstore/objectstore.cpp b/src/zenserver/storage/objectstore/objectstore.cpp index d6516fa1a..e34cd0445 100644 --- a/src/zenserver/storage/objectstore/objectstore.cpp +++ b/src/zenserver/storage/objectstore/objectstore.cpp @@ -639,8 +639,8 @@ HttpObjectStoreService::GetObject(HttpRouterRequest& Request, const std::string_ HttpRanges Ranges; if (Request.ServerRequest().TryGetRanges(Ranges); Ranges.size() > 1) { - // Only a single range is supported - return Request.ServerRequest().WriteResponse(HttpResponseCode::BadRequest); + // Multi-range is not supported, fall back to full response per RFC 7233 + Ranges.clear(); } FileContents File; -- cgit v1.2.3 From 1783a5c414c6ae2088bacf6183580432fddbfbe7 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 10 Apr 2026 19:09:10 +0200 Subject: HTTP range responses (RFC 7233) - httpobjectstore (#928) - Improvement: HTTP range responses (RFC 7233) are now fully compliant across the object store and build store - 206 Partial Content responses now include a `Content-Range` header; previously absent for single-range requests, which broke `HttpClient::GetRanges()` - 416 Range Not Satisfiable responses now include `Content-Range: bytes */N` as required by RFC 7233 - Out-of-bounds range requests return 416 Range Not Satisfiable (was 400 Bad Request) - Single-byte ranges (`bytes=N-N`) are now correctly accepted (were previously rejected) - Range byte positions widened from 32-bit to 64-bit; RFC 7233 imposes no size limit on byte range values - Build store binary GET requests with a Range header now return 206 Partial Content with `Content-Range` (previously returned 200 OK without it) --- src/zenserver/storage/objectstore/objectstore.cpp | 50 +++++++++-------------- 1 file changed, 19 insertions(+), 31 deletions(-) (limited to 'src/zenserver/storage/objectstore/objectstore.cpp') diff --git a/src/zenserver/storage/objectstore/objectstore.cpp b/src/zenserver/storage/objectstore/objectstore.cpp index e34cd0445..bab9df06d 100644 --- a/src/zenserver/storage/objectstore/objectstore.cpp +++ b/src/zenserver/storage/objectstore/objectstore.cpp @@ -637,11 +637,7 @@ HttpObjectStoreService::GetObject(HttpRouterRequest& Request, const std::string_ } HttpRanges Ranges; - if (Request.ServerRequest().TryGetRanges(Ranges); Ranges.size() > 1) - { - // Multi-range is not supported, fall back to full response per RFC 7233 - Ranges.clear(); - } + Request.ServerRequest().TryGetRanges(Ranges); FileContents File; { @@ -665,42 +661,34 @@ HttpObjectStoreService::GetObject(HttpRouterRequest& Request, const std::string_ if (Ranges.empty()) { - const uint64_t TotalServed = m_TotalBytesServed.fetch_add(FileBuf.Size()) + FileBuf.Size(); - + const uint64_t TotalServed = m_TotalBytesServed.fetch_add(FileBuf.GetSize()) + FileBuf.GetSize(); ZEN_LOG_DEBUG(LogObj, "GET - '{}/{}' ({}) [OK] (Served: {})", BucketName, RelativeBucketPath, - NiceBytes(FileBuf.Size()), + NiceBytes(FileBuf.GetSize()), NiceBytes(TotalServed)); - - Request.ServerRequest().WriteResponse(HttpResponseCode::OK, HttpContentType::kBinary, FileBuf); } - else + else if (Ranges.size() == 1) { - const auto Range = Ranges[0]; - const uint64_t RangeSize = 1 + (Range.End - Range.Start); - const uint64_t TotalServed = m_TotalBytesServed.fetch_add(RangeSize) + RangeSize; - - ZEN_LOG_DEBUG(LogObj, - "GET - '{}/{}' (Range: {}-{}) ({}/{}) [OK] (Served: {})", - BucketName, - RelativeBucketPath, - Range.Start, - Range.End, - NiceBytes(RangeSize), - NiceBytes(FileBuf.Size()), - NiceBytes(TotalServed)); - - MemoryView RangeView = FileBuf.GetView().Mid(Range.Start, RangeSize); - if (RangeView.GetSize() != RangeSize) + const uint64_t TotalSize = FileBuf.GetSize(); + const uint64_t RangeEnd = (Ranges[0].End != ~uint64_t(0)) ? Ranges[0].End : TotalSize - 1; + if (RangeEnd < TotalSize && Ranges[0].Start <= RangeEnd) { - return Request.ServerRequest().WriteResponse(HttpResponseCode::BadRequest); + const uint64_t RangeSize = 1 + (RangeEnd - Ranges[0].Start); + const uint64_t TotalServed = m_TotalBytesServed.fetch_add(RangeSize) + RangeSize; + ZEN_LOG_DEBUG(LogObj, + "GET - '{}/{}' (Range: {}-{}) ({}/{}) [OK] (Served: {})", + BucketName, + RelativeBucketPath, + Ranges[0].Start, + RangeEnd, + NiceBytes(RangeSize), + NiceBytes(TotalSize), + NiceBytes(TotalServed)); } - - IoBuffer RangeBuf = IoBuffer(IoBuffer::Wrap, RangeView.GetData(), RangeView.GetSize()); - Request.ServerRequest().WriteResponse(HttpResponseCode::PartialContent, HttpContentType::kBinary, RangeBuf); } + Request.ServerRequest().WriteResponse(HttpContentType::kBinary, FileBuf, Ranges); } void -- cgit v1.2.3 From 2d902f99fbfc11b874f4c50fa648f10340e4f0cc Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Mon, 13 Apr 2026 12:42:58 +0200 Subject: minor fixups (#948) * objectstore.cpp - m_TotalBytesServed now tracks all range cases (single, multi, 416) * async http: docstring corrected: curl_multi_socket_action() / ASIO socket async_wait remove non-ascii characters * fix singlethreaded gc option in lua to not use dash * fix changelog order --- src/zenserver/storage/objectstore/objectstore.cpp | 35 ++++++++++++++++------- 1 file changed, 25 insertions(+), 10 deletions(-) (limited to 'src/zenserver/storage/objectstore/objectstore.cpp') diff --git a/src/zenserver/storage/objectstore/objectstore.cpp b/src/zenserver/storage/objectstore/objectstore.cpp index bab9df06d..1115c1cd6 100644 --- a/src/zenserver/storage/objectstore/objectstore.cpp +++ b/src/zenserver/storage/objectstore/objectstore.cpp @@ -669,24 +669,39 @@ HttpObjectStoreService::GetObject(HttpRouterRequest& Request, const std::string_ NiceBytes(FileBuf.GetSize()), NiceBytes(TotalServed)); } - else if (Ranges.size() == 1) + else { - const uint64_t TotalSize = FileBuf.GetSize(); - const uint64_t RangeEnd = (Ranges[0].End != ~uint64_t(0)) ? Ranges[0].End : TotalSize - 1; - if (RangeEnd < TotalSize && Ranges[0].Start <= RangeEnd) + const uint64_t TotalSize = FileBuf.GetSize(); + uint64_t ServedBytes = 0; + for (const HttpRange& Range : Ranges) { - const uint64_t RangeSize = 1 + (RangeEnd - Ranges[0].Start); - const uint64_t TotalServed = m_TotalBytesServed.fetch_add(RangeSize) + RangeSize; + const uint64_t RangeEnd = (Range.End != ~uint64_t(0)) ? Range.End : TotalSize - 1; + if (RangeEnd < TotalSize && Range.Start <= RangeEnd) + { + ServedBytes += 1 + (RangeEnd - Range.Start); + } + } + if (ServedBytes > 0) + { + const uint64_t TotalServed = m_TotalBytesServed.fetch_add(ServedBytes) + ServedBytes; ZEN_LOG_DEBUG(LogObj, - "GET - '{}/{}' (Range: {}-{}) ({}/{}) [OK] (Served: {})", + "GET - '{}/{}' (Ranges: {}) ({}/{}) [OK] (Served: {})", BucketName, RelativeBucketPath, - Ranges[0].Start, - RangeEnd, - NiceBytes(RangeSize), + Ranges.size(), + NiceBytes(ServedBytes), NiceBytes(TotalSize), NiceBytes(TotalServed)); } + else + { + ZEN_LOG_DEBUG(LogObj, + "GET - '{}/{}' (Ranges: {}) [416] ({})", + BucketName, + RelativeBucketPath, + Ranges.size(), + NiceBytes(TotalSize)); + } } Request.ServerRequest().WriteResponse(HttpContentType::kBinary, FileBuf, Ranges); } -- cgit v1.2.3