Repository navigation
Bun.serve: answer 206 for a Bun.file() slice as long as the file that starts past byte 0 - #44827
Conversation
|
Updated 12:20 AM PT - Oct 9th, 2026
✅ @robobun, your commit 4bdf0f8160ebe7a5686f72125b7b1ef5e3fa3642 passed in 🧪 To try this PR locally: bunx bun-pr 44827That installs a local version of the PR into your bun-44827 --bun |
|
Status: stacked on #44826 and reproduced on that branch. |
…e clamp `do_sendfile` sets the `needs_content_range` bit from the slice length as asked. A path slice as long as the file that starts past byte 0 counts as the whole file. It answers 200 with no Content-Range, and its neighbours one byte shorter or longer answer 206. The bit is now set after the clamp, from the window that is sent. `render_metadata` reads the bit alone. The copy of the file size in the blob and the second check against it have no reader left, so they are deleted.
5a3a3a6 to
4bdf0f8
Compare
8fb5808 to
42da3fb
Compare
|
Related: #46059 proposes one rule for the three arms that Background describes here. A slice becomes
If the answer to question 2 is no, #46059 does not apply as it is. |
Stacked on #44826. Draft until the two questions in Downsides have an answer.
Problem
Bun.file(p).slice(5, 21)of a 16-byte file sends 11 bytes and answers200with noContent-Range.slice(5, 20)andslice(5, 22)send the same 11 bytes and answer206withContent-Range: bytes 5-15/*.do_sendfile(src/runtime/server/RequestContext.rs:1906) sets theneeds_content_rangebit from the slice length as asked, before the clamp. A slice as long as the file counts as the whole file.206withContent-Rangehere. serve: consolidate file streaming into FileResponseStream; Range support; fix aborted-stream hang #29202 (v1.3.13) changed that.Fix
do_sendfilesets the bit after the clamp, from the window that it sends.render_metadatareads the bit alone. The blob-size store and the second check against it have no reader left and are deleted.serve.test.ts. On Bun.serve: write the Content-Length of a file response from the window it streams #44826 they fail on status lines only.Background
206and an automaticContent-Range. Only a path-backed file gets it.render_metadatahas three arms. No init headers: any status becomes206. Headers withoutContent-Range: the status stays. Headers withContent-Range: any status becomes206.Downsides
206, as over TLS through v1.3.12?{ status: 404 }and{ status: 416, "Content-Range": "bytes */16" }on these slices become206. Should an explicit status win?Notes
History (handler of
docs/runtime/http/routing.mdx, "To send part of a file",Range: bytes=100-307300on 307200 bytes, official release binaries and debug builds of 620b50f).200, length 307200, 307100 bytes206, length 307100,bytes 100-307199/*200, length 307200, 307100 bytes200, length 307200, 307100 bytes200, length 307100200, length 307100206, length 307100,bytes 100-307199/*206, length 307100,bytes 100-307199/*Status by arm, path
slice(5, 15)of 10 bytes (as long as the file, from byte 5).Responseinitslice(5, 14)on mainbytes 5-9/*bytes 5-9/*{ status: 404 }(also 201, 500, 416)bytes 5-9/*bytes 5-9/*{ headers: { "x-a": "1" } }{ status: 404, headers: { "x-a": "1" } }{ headers: { "Content-Range": "bytes 5-9/10" } }{ status: 416, headers: { "Content-Range": "bytes */10" } }bytes */10bytes */10Each cell of this PR equals what the neighbour
slice(5, 14)answers on main with the same init. On main every row of the first three columns is also mis-framed (length 10 for 5 bytes). #44826 fixes the length.Counts.
What also follows.
slice(10, 20)of 10 bytes) answers206, length 0,Content-Range: bytes 10-10/*, asslice(10, 19)does on main. The test does not pin that text.206where HEAD answers the status of the handler, as for every other path slice on main (Bun.serve: resolve a file body for HEAD the same way as for GET #41585).Bun.file(fd)slices keep the status of the handler. An incomingRangeheader on an unslicedBun.file(fd)still answers206, as on main.response.headers, or a print of the Response, attaches a header list and so moves the Response from the first arm to the second.new Response(Bun.file(p).slice(0, 100))answers206, and200with noContent-Rangeafterres.headers.get("content-type")orBun.inspect(res). This is the same on main and with this PR. It is a separate defect and not changed here.docs/runtime/http/routing.mdxis right again for such aRange. It can also return the file unsliced:do_sendfileanswersRangefor a whole file.Tests.
bun-serve-file.test.ts: the status of each row of the 18-window test, the shrink case, the TLS window, the documented handler and the sendfile row.serve.test.ts,should support Content-Range with Bun.file(): rows[10, size + 10](two tests) and[size, 2 * size]. All 42 cases of that block pass. Not run: Windows, macOS, HTTP/2, HTTP/3.Self-review. A comment on the
auto_closegate said that a descriptor of the caller gets no automaticContent-Range. An incomingRangeon an unslicedBun.file(fd)gets one, so the comment is gone. The rows with an explicit status were not pinned by any test. They are now, one per arm.Code. +5/-14 in
RequestContext.rson top of #44826. Release builds:do_sendfile4571 to 4546 bytes (8 bodies),render_metadata2321 and 2334 to 2251 and 2294 (2 bodies).no test proof · iteration 0 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/bun/http/serve.test.ts, test/js/bun/http/bun-serve-file.test.ts