Repository navigation
Stream archives when listing and reading browsed files - #408
abhinavgautam01 wants to merge 1 commit into
Conversation
andrew
left a comment
There was a problem hiding this comment.
Please handle errors while reading the selected file so truncated archive contents are not returned as successful downloads.
| return | ||
| } | ||
| // Find the file and stream it straight from the archive | ||
| fileReader, err := archive.Extract(filePath) |
There was a problem hiding this comment.
Switching to a streaming reader moves archive errors into the response copy, but browseFile still discards the error from io.Copy(w, content). A test through the HTTP browse endpoint with an npm TAR entry declaring 4,096 bytes but containing only 3 returned HTTP 200, Content-Length: 3, and no client read error. The buffered reader rejected that same archive with unexpected EOF. Setting MaxInputBytes to 1,024 on the complete TAR likewise returned a completed 200 containing only 512 bytes. These failures occur inside the selected file, rather than in entries after it. Please handle and log stream errors, return an error status before the response starts where possible, and abort an already-started response so the client detects the incomplete download. Add endpoint coverage for both truncated entry bodies and input-limit failures during the copy.
Closes #386
Problem
Browsing a cached artifact buffered the whole archive in
openArchive. The TAR readers also kept every expanded file body in memory. For non-npm packages the archive was parsed twice: once to detect a common root directory, then again with that prefix stripped. So listing one directory or viewing one small file could allocate memory for the entire expanded archive.Changes
Streaming reader (
internal/server/browse_archive.go)Directory listings and file reads now use
archives.OpenStream:package/prefix stripping and needs a single pass.The listing logic mirrors
archives.Reader.ListDircombined with the prefix wrapper, so paths, synthesized directory entries, ordering and duplicate handling all stay the same.Limits
The existing 512 MB input-size cap stays. These limits are now explicit:
They match what the buffered reader enforced implicitly, so no archive that browsed before is rejected now. A limit failure returns
500witharchive exceeds browse limitsand is logged at warn level. Other archive errors still returnfailed to open archive.Unchanged
Content-Typesniffing,Content-Security-Policy,X-Content-Type-OptionsandContent-Dispositionheaders.404.openArchive, becausediff.Compareneeds a random-accessarchives.Reader.Results
Fixture: a gzip tarball with 1,025 entries and 32 MiB expanded (92 KB compressed).
From
BenchmarkBrowseLargeArchive(B/opandns/op).Tests
New tests in
internal/server/browse_archive_test.goall go through the public browse endpoints:"",/,src,src/,/src, nested, missing, file path).404for missing files and that the first of two duplicate entries wins.gofmt,go vet,golangci-lint(v2.13.1),go test -race ./...and Swagger regeneration all pass locally.Notes
klauspost/compressa direct dependency ingo.mod. ZIP covers the same buffered path.