Why
Closes #119. /r/<name>/raw/<ref>/<path> streamed the whole blob and ignored
Range, so a client resuming a download or asking for a few KiB got the whole
file. Measured with the load harness in #100: a 64 KiB range of an 8 MiB blob
returned 200 and 8 MiB, while the artifact route answered 206 because it uses
http.ServeContent.
What changed
- A single
Rangeis answered with206,Content-Range: bytes a-b/size, and only the requested bytes. Prefix (0-9), open-ended (15-) and suffix (-5) forms work, and an end past the file is clamped. - A range starting past the end is
416withContent-Range: bytes */<size>. - Multiple ranges, an unknown unit, and a malformed header get the whole file
with
200, which RFC 9110 allows. A client that asks for something the server does not implement gets a correct full response, not a broken one. - The no-range response keeps its bytes and gains
Accept-Ranges: bytes. HEADnow stops after the headers instead of reading the blob.
A blob is streamed from git cat-file, which cannot seek: a range is served by
reading and discarding the bytes before it and copying only the requested part.
The win is what does not cross the network (64 KiB instead of 8 MiB); the server
still reads the skipped bytes. That is stated in the code.
Measurement
The harness’s raw-range scenario (64 KiB of an 8 MiB file, concurrency 4, 5 s):
| status | req/s | p50 | p90 | p99 | |
|---|---|---|---|---|---|
| before (#100) | 200 (8 MiB) | 265.6 | 15 ms | 17 ms | 21 ms |
| after | 206 (64 KiB) | 365.6 | 11 ms | 13 ms | 17 ms |
Checks
TestParseByteRange: every form, clamping,416, and the headers that mean “whole file”.TestRawAnswersByteRanges: the route end to end over a pushed file — no range, prefix, middle, open-ended, clamped end, suffix, suffix larger than the file, past-the-end416, and the ignored headers.gofmt,go vet ./...,python3 scripts/check-links.py, and the full-racesuite withGOFLAGS=-tags=sqlite_fts5.- The load harness
raw-rangescenario reports206, as #119 asked.