Skip to content

Commit 1a1f84d

Browse files
authored
Multipart download enhancements (#4021)
* fix(api): set blob response Content-Type from OCI descriptor Blob HEAD responses had no Content-Type and GET responses echoed the request's Accept header verbatim, which produced missing or malformed media types and left multipart/byteranges parts without a per-part Content-Type. This breaks OCI distribution-spec conformance and consumers like stargz-snapshotter that need a well-formed layer media type. Add a blobResponseMediaType helper that resolves the descriptor's MediaType via GetBlobDescriptorFromRepo and falls back to application/octet-stream. Use it in CheckBlob (HEAD), GetBlob full (200), GetBlob single-range (206), and per-part in writeMultipartRanges (206 multipart). Lookup is deferred until after the blob is known to exist. Cover the new behaviour with mock-based unit tests in routes_test.go and end-to-end assertions in TestPullRange. Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com> * perf(api): stream multipart blob ranges lazily with precomputed Content-Length writeMultipartRanges previously opened every range reader up front and emitted no Content-Length, so an N-range request held N concurrent storage readers (and their fds / read buffers) per response window and forced chunked encoding on HTTP/1.1 — neither friendly to proxies nor to fan-out scenarios like stargz lazy pulls. Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com> --------- Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
1 parent bb5b77a commit 1a1f84d

3 files changed

Lines changed: 1318 additions & 59 deletions

File tree

pkg/api/controller_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11247,6 +11247,10 @@ func TestPullRange(t *testing.T) {
1124711247
So(err, ShouldBeNil)
1124811248
So(resp.Header().Get("Accept-Ranges"), ShouldEqual, "bytes")
1124911249
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
11250+
// HEAD on an unreferenced blob (no manifest/index) must still
11251+
// advertise a valid Content-Type. The descriptor lookup fails
11252+
// here, so we expect the application/octet-stream fallback.
11253+
So(resp.Header().Get("Content-Type"), ShouldEqual, "application/octet-stream")
1125011254
})
1125111255

1125211256
Convey("Get a range of bytes", func() {
@@ -11255,6 +11259,11 @@ func TestPullRange(t *testing.T) {
1125511259
So(resp.StatusCode(), ShouldEqual, http.StatusPartialContent)
1125611260
So(resp.Header().Get("Content-Length"), ShouldEqual, strconv.Itoa(len(content)))
1125711261
So(resp.Body(), ShouldResemble, content)
11262+
// Single-range 206: descriptor lookup fails (blob is not
11263+
// referenced by any manifest), so Content-Type falls back to
11264+
// application/octet-stream rather than echoing the request
11265+
// Accept header.
11266+
So(resp.Header().Get("Content-Type"), ShouldEqual, "application/octet-stream")
1125811267

1125911268
resp, err = resty.R().SetHeader("Range", "bytes=0-100").Get(blobLoc)
1126011269
So(err, ShouldBeNil)
@@ -11318,6 +11327,11 @@ func TestPullRange(t *testing.T) {
1131811327
part, err := multipartReader.NextPart()
1131911328
So(err, ShouldBeNil)
1132011329
So(part.Header.Get("Content-Range"), ShouldEqual, "bytes 0-1/10")
11330+
// Per-part Content-Type reflects the descriptor-aware
11331+
// resolution. For an unreferenced blob this is the
11332+
// application/octet-stream fallback; for a real OCI layer it
11333+
// would carry the manifest's layer media type.
11334+
So(part.Header.Get("Content-Type"), ShouldEqual, "application/octet-stream")
1132111335

1132211336
partBody, err := io.ReadAll(part)
1132311337
So(err, ShouldBeNil)
@@ -11326,6 +11340,7 @@ func TestPullRange(t *testing.T) {
1132611340
part, err = multipartReader.NextPart()
1132711341
So(err, ShouldBeNil)
1132811342
So(part.Header.Get("Content-Range"), ShouldEqual, "bytes 4-6/10")
11343+
So(part.Header.Get("Content-Type"), ShouldEqual, "application/octet-stream")
1132911344

1133011345
partBody, err = io.ReadAll(part)
1133111346
So(err, ShouldBeNil)

0 commit comments

Comments
 (0)