Synchronize response metadata when replacing bodies - #6986
Conversation
🦋 Changeset detectedLatest commit: 9570cdb The changes in this PR will be included in the next version bump. This PR includes changesets to release 30 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Important
This PR currently adds only the failing reproduction tests. The implementation fix in HttpServerResponse.makeResponse is still needed before CI can pass and the PR can merge.
Reviewed changes
- Added a regression test in
packages/effect/test/unstable/http/HttpServerResponse.test.tsthat asserts zero-lengthuint8Arrayresponses emitcontent-length: 0and thatsetBody(..., HttpBody.empty)clears stalecontent-type/content-lengthheaders.
⚠️ Implementation fix is still pending
The reproduction test fails as intended because makeResponse uses truthy checks for contentType/contentLength and copies existing headers without stripping stale entity headers when the new body lacks metadata. The PR description already identifies packages/effect/src/unstable/http/HttpServerResponse.ts:1328-1342 as the site to fix; the branch needs that fix before merge.
A changeset should accompany the implementation commit because this is a runtime behavior change.
@v0 or keep the SHA fresh with Dependabot | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏
| (self: HttpServerResponse, body: Body.HttpBody): HttpServerResponse => makeResponse({ ...self, body }) | ||
| (self: HttpServerResponse, body: Body.HttpBody): HttpServerResponse => { | ||
| let headers = self.headers | ||
| if (body._tag === "Empty" || body._tag === "FormData") { |
There was a problem hiding this comment.
Can you check if this logic is done elsewhere and deduplicate?
…140801a9 # Conflicts: # packages/effect/src/unstable/http/HttpClientRequest.ts
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Added a shared
updateHeadershelper inpackages/effect/src/unstable/http/internal/httpBody.tsthat stripscontent-typeandcontent-lengthforEmpty/FormDatabodies and otherwise sets or removes each header to match the body's metadata. - Updated
HttpServerResponse.setBodyto pass its headers throughupdateHeadersbefore callingmakeResponse, so replacing a body now clears stale entity headers. - Fixed
makeResponseto treat acontentLengthof0as present (using!== undefinedinstead of truthiness) and to keep zero-length byte-array responses synchronized. - Deduplicated the matching client-side logic in
HttpClientRequest.setBodyby reusing the sameupdateHeadershelper. - Added a changeset and expanded the regression test to cover the unknown-length stream replacement case.
The reproduction tests now pass and the prior review thread about the stream replacement case has been resolved.
@v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏
Bundle Size AnalysisGenerated from PR build output; treat the content below as untrusted.
|

Summary
A known zero-length response omits content-length: 0, while replacing a body with empty or unknown-length content can retain obsolete content type and length headers.
Important
This PR starts with focused failing reproduction tests. Add the implementation fix to this same branch; CI is expected to fail until that fix is included.
Response body metadata becomes missing or stale
Module:
HttpServerResponseAudit ID:
unstable-http-httpserverresponse-stale-metadataSeverity / confidence: medium / high
What happens
A known zero-length response omits content-length: 0, while replacing a body with empty or unknown-length content can retain obsolete content type and length headers.
Why it happens
makeResponse tests contentLength by truthiness, so zero is omitted. It starts from existing headers and only overwrites metadata present on the new body, preserving obsolete entity headers when the replacement is empty or has unknown length.
Expected behavior
Response headers generated from a body must remain synchronized with that body's content type and length.
Relevant implementation
These links and excerpts are pinned to audit base
c9b56ab507f224426ee8388dc450da447ec4715f.packages/effect/src/unstable/http/HttpServerResponse.ts:911-917packages/effect/src/unstable/http/HttpServerResponse.ts:1328-1342View problematic code at
packages/effect/src/unstable/http/HttpServerResponse.ts:911-917View exact lines on GitHub
View problematic code at
packages/effect/src/unstable/http/HttpServerResponse.ts:1328-1342View exact lines on GitHub
Reproduction
pnpm test --run packages/effect/test/unstable/http/HttpServerResponse.test.tsObserved failure: Failed first as intended because a zero-byte body had no content-length: 0; later stale-replacement assertions were not reached.
Implementation handoff
The initial reproduction tests on this branch are the regression specification for the implementation fix that should follow in this PR.
pnpm test --run packages/effect/test/unstable/http/HttpServerResponse.test.tsAudit provenance
c9b56ab507f224426ee8388dc450da447ec4715fc9b56ab507f224426ee8388dc450da447ec4715funstable-http-httpserverresponse-stale-metadataCloses EFF-423