Skip to content

fix(node): keep derived content-length when FastResponse materializes - #335

Open
pi0x wants to merge 1 commit into
mainfrom
fix/fastres-len
Open

pi0x wants to merge 1 commit into
mainfrom
fix/fastres-len

Conversation

@pi0x

@pi0x pi0x commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Resolves #332

On Node, FastResponse only worked out content-type / content-length for string and byte bodies inside _toNodeResponse(). When anything read .body (or called .clone(), .text(), …) first, the _response getter built a native Response with no content-length, so srvx sent the response chunked. A HEAD response had no length header at all.

_response now sets these headers for string, ArrayBuffer, Uint8Array, DataView and Blob bodies, using the same rules as _toNodeResponse():

  • content-length is the body's byte length (Buffer.byteLength for strings, .size for Blob).
  • content-type is srvx's text/plain; charset=UTF-8 for strings, or blob.type, and is only set when the header is missing.
  • Headers the user set always win. A shared Headers passed as init is copied, never changed.
  • Stream and Node Readable bodies are unchanged.

Body-wrapping layers like env-runner's RunnerManager (unjs/env-runner#92) can now read .body and keep the framing. They no longer need to call _toNodeResponse() directly.

Tests: added FastResponse keeps derived content-length once materialized in test/node-adapters.test.ts. It covers each body type, user header precedence, headers changed before materializing, not mutating a shared init, and an end-to-end GET and HEAD through serve(). The new tests fail without the fix. The full suite, lint and typecheck pass.

🤖 Generated with AI assistant

Summary by CodeRabbit

  • Bug Fixes
    • Responses now include inferred content type and content length for supported body types when those headers aren’t already set.
    • Materialized responses send the correct body length without unnecessary transfer-encoding, including for HEAD requests.
    • Existing headers remain unchanged, and streamed responses do not receive an inferred content length.

@pi0x
pi0x requested a review from pi0 as a code owner October 23, 2026 06:03
@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
srvx Ready Ready Preview Oct 23, 2026 6:03am UTC

Request Review

@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b1f639ba-9863-4711-9b40-c20352325233
📥 Commits

Reviewing files that changed from the base of the PR and between 008a954 and 02c7c6d.

📒 Files selected for processing: 2
  • src/adapters/_node/response.ts
  • test/node-adapters.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 0 remain after this review.

📝 Walkthrough

Node response materialization now derives content type and content length for supported body types when those headers are absent. Regression tests cover header preservation, streams, and HTTP framing for GET and HEAD responses.

Changes: Node response header preservation

Layer / File(s) Summary
Derive headers during materialization
src/adapters/_node/response.ts, test/node-adapters.test.ts
_response derives content type and byte length for strings, buffers, typed arrays, DataView, and Blob bodies. Explicit headers take precedence, mutations made before materialization remain present, shared Headers objects are not mutated, and streams receive no derived length.
Verify HTTP response framing
test/node-adapters.test.ts
An HTTP test checks that materialized GET and HEAD responses retain content-length without transfer-encoding. The GET response body remains intact.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Possibly related issues

Possibly related PRs

  • h3js/srvx#330 — It changes how NodeResponse resolves NativeResponse, which this change uses during materialization.
  • h3js/srvx#331 — It changes native Response constructor resolution used by this change’s materialization path.
Merge Risk: ⚪ Minimal · up to 02c7c

The reviewed header behavior is consistent across materialized and direct Node responses. No issue identified here needs resolution before merge.

Pre-merge checks: Passed 5
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title names the change: keeping derived content-length when FastResponse materializes, scoped to the node adapter. The changeset adds header derivation in NodeResponse._response (content-length, a…
Linked Issues check Passed Issue #332 requires materialized Node FastResponse objects to retain derived framing headers for string, ArrayBuffer, Uint8Array, DataView, and Blob bodies. The change derives byte length and content …
Out of Scope Changes check Passed The source change is limited to materialized Node response headers. The added tests directly verify issue #332 behavior. No unrelated production change or unrelated test objective is evidenced.
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
Full details: Title check

Explanation

The title names the change: keeping derived content-length when FastResponse materializes, scoped to the node adapter. The changeset adds header derivation in NodeResponse._response (content-length, and content-type when absent) and regression tests for materialized responses. The title is specific and matches the main change.

Full details: Linked Issues check

Explanation

Issue #332 requires materialized Node FastResponse objects to retain derived framing headers for string, ArrayBuffer, Uint8Array, DataView, and Blob bodies. The change derives byte length and content type in _response, preserves existing headers, and leaves stream bodies without derived length. The added tests cover body types, header precedence, shared-header isolation, and GET and HEAD framing through serve().

Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2 files.

✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit checked the headers in a line,
Then watched the byte count fit just fine.
Strings and blobs now carry their size,
While streams stay free of guessed replies.
GET and HEAD keep framing clear,
The rabbit hops away with cheer.

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Oct 23, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/srvx@335

commit: 02c7c6d

@codecov

codecov Bot commented Oct 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

This branch was successfully deployed

1 active deployment
Preview — 02c7c6df Deployed Oct 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Node: FastResponse drops derived content-length once it becomes a native Response

2 participants