Skip to content

wire-api: strip framing headers in generic streaming response renderers - #5389

Draft
jschaul wants to merge 1 commit into
developfrom
defensive-stream-filter
Draft

wire-api: strip framing headers in generic streaming response renderers#5389
jschaul wants to merge 1 commit into
developfrom
defensive-stream-filter

Conversation

@jschaul

@jschaul jschaul commented Jul 29, 2026

Copy link
Copy Markdown
Member

A follow-up to #5381 in other places streaming is done.

== WARNING: AI written. Needs a bit more human review first, keeping this in draft for later... ===

Both generic streaming renderers re-frame the body via Warp (chunked):

  • MultiVerb's IsWaiBody (SourceIO ByteString) instance
  • LowLevelStream's servant HasServer route

Each spliced the response's own headers into the Wai.responseStream
verbatim. If a Content-Length (or Transfer-Encoding) rode along, Warp
would honour it and serve the streamed body under that declared length
instead of chunking; any mismatch between the declared length and the bytes
actually streamed then desynchronises the caller's keep-alive HTTP/1.1
connection (the client reads past the response boundary into the next
response). This is the same defect fixed in
Federator.Response.streamingResponseToWai.

No endpoint currently attaches such a header to a streaming response (the
federator's inward path already strips to Content-Type only, and cargohold's
RespondStreaming carries no length), so this is defense-in-depth: strip
Content-Length/Transfer-Encoding in both renderers via a stripFramingHeaders
helper so Warp always frames exactly what is streamed.

Adds Test.Wire.API.Routes.Streaming covering both renderers (verified
red without the strip, green with it).

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Checklist

  • Add a new entry in an appropriate subdirectory of changelog.d
  • Read and follow the PR guidelines

Both generic streaming renderers re-frame the body via Warp (chunked):

  * MultiVerb's `IsWaiBody (SourceIO ByteString)` instance
  * `LowLevelStream`'s servant `HasServer` route

Each spliced the response's own headers into the `Wai.responseStream`
verbatim. If a `Content-Length` (or `Transfer-Encoding`) rode along, Warp
would honour it and serve the streamed body under that declared length
instead of chunking; any mismatch between the declared length and the bytes
actually streamed then desynchronises the caller's keep-alive HTTP/1.1
connection (the client reads past the response boundary into the next
response). This is the same defect fixed in
`Federator.Response.streamingResponseToWai`.

No endpoint currently attaches such a header to a streaming response (the
federator's inward path already strips to `Content-Type` only, and cargohold's
`RespondStreaming` carries no length), so this is defense-in-depth: strip
`Content-Length`/`Transfer-Encoding` in both renderers via a `stripFramingHeaders`
helper so Warp always frames exactly what is streamed.

Adds `Test.Wire.API.Routes.Streaming` covering both renderers (verified
red without the strip, green with it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@zebot zebot added the ok-to-test Approved for running tests in CI, overrides not-ok-to-test if both labels exist label Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ok-to-test Approved for running tests in CI, overrides not-ok-to-test if both labels exist

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants