Skip to content

Use HTTP range requests for uncompressed layer fetches in libindex#1948

Open
ushaket wants to merge 2 commits into
quay:mainfrom
ushaket:http-range-reader
Open

Use HTTP range requests for uncompressed layer fetches in libindex#1948
ushaket wants to merge 2 commits into
quay:mainfrom
ushaket:http-range-reader

Conversation

@ushaket

@ushaket ushaket commented Jul 14, 2026

Copy link
Copy Markdown

Summary

Use HTTP range requests for uncompressed layer fetches in libindex instead of downloading the full blob up front. This lets claircore access large uncompressed layers lazily through io.ReaderAt, while keeping the existing full-download behavior for compressed layers.


Related Issues


Changes

HTTP range-backed layer access

  • Added internal/httputil.RangeReaderAt, an io.ReaderAt implementation backed by HTTP Range requests
  • Added read-ahead buffering to reduce the number of small HTTP requests generated while tarfs walks tar headers
  • Reused existing compression detection logic by exporting zreader.DetectCompression

Fetcher behavior

  • Updated libindex/fetcher.go to probe layers with Range: bytes=0-15
  • If the layer is uncompressed and the registry responds cleanly to the probe, libindex now initializes the layer directly from RangeReaderAt
  • If the layer is compressed or the registry does not cleanly support the range path, the fetcher falls back to the existing full-download path

Test coverage

  • Added unit tests for RangeReaderAt
  • Added integration tests for:
    • uncompressed range-backed fetches
    • compressed fallback
    • non-range fallback
    • malformed probe fallback
    • transport failure fallback
    • follow-up reads where a server ignores a later Range request
    • same digest accessed through different source/auth contexts

Context

Claircore already builds tarfs from io.ReaderAt, which means scanners do not require a fully downloaded layer stream up front. By switching uncompressed layers to an HTTP range-backed reader, the existing indexing and scanning pipeline can stay unchanged while avoiding unnecessary transfer of large blob contents.

This is especially useful for large uncompressed model/data layers, where scanners typically only need tar metadata or a small number of files.


Testing

In-repo automated tests

  • internal/httputil/rangereaderat_test.go
  • libindex/fetcher_range_test.go

Real-image validation

Validated the behavior against live registries and real images:

  • registry.redhat.io modelcar images:
    • uncompressed layers used the range-backed path
    • compressed layers used the existing fallback path
    • large .safetensors layers were accessed through small ranged reads without full blob downloads
  • Common public images:
    • Python scanner validated on python:3.12-slim
    • Java scanner validated on tomcat:10.1-jdk17-temurin
    • RPM scanner validated on centos:stream9

A/B equivalence validation

Ran a fixture-based comparison of old vs new fetch behavior and confirmed equivalent scanner results for:

  • Python
  • Java
  • RPM

The same fixture runs also showed that irrelevant uncompressed blob content was fully downloaded with the old path and avoided with the new range-backed path.


Examples

Uncompressed layer

Before:

  • blob is fully downloaded before scanning

After:

  • fetcher probes the blob with a small range request
  • uncompressed layers are read lazily through RangeReaderAt
  • tarfs and scanners request only the byte ranges they need

Compressed layer

Before:

  • full download + decompress + spool

After:

  • probe detects compression
  • fetcher follows the same full-download path as before

Assisted-by: Claude Opus 4.6

@ushaket
ushaket requested review from a team as code owners July 14, 2026 14:39
@ushaket
ushaket requested a review from hdonnay July 14, 2026 14:39
@ushaket
ushaket force-pushed the http-range-reader branch from fe408b1 to babd4ee Compare July 14, 2026 14:43
Use a range-backed ReaderAt for uncompressed layers so libindex can avoid downloading and spooling large blobs when scanners only need tar metadata or a small subset of files.

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Uri Shaket <ushaket@redhat.com>
@ushaket
ushaket force-pushed the http-range-reader branch from babd4ee to c12b7d7 Compare July 14, 2026 17:13
Use compressed fixtures in TestFetchConcurrent so the test continues to exercise the cached full-download path instead of assuming uncompressed layers only require a single fetch.

Signed-off-by: Uri Shaket <ushaket@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant