Skip to content

Fix response streaming dropping unflushed BodyWriter bytes - #2609

Merged
afroz429 merged 1 commit into
masterfrom
normj/streaming-bodywriter-unflushed-bytes
Nov 13, 2026
Merged

afroz429 merged 1 commit into
masterfrom
normj/streaming-bodywriter-unflushed-bytes

Conversation

@normj

@normj normj commented Nov 11, 2026

Copy link
Copy Markdown
Member

Issue #, if available:

Description of changes:

When response streaming is enabled (EnableResponseStreaming), bytes an application writes through HttpResponse.BodyWriter with GetSpan/GetMemory + Advance, without flushing, can be silently dropped. The response then arrives with an empty (or truncated) body and no error.

Cause. StreamingResponseBodyFeature.Writer is a StartOnFlushPipeWriter whose inner PipeWriter is created lazily against the feature's current Stream, which before the response starts is the pre-start MemoryStream. The wrapper only dropped that inner writer on its own unstarted FlushAsync/CompleteAsync/Complete paths. When the response was started any other way (CompleteAsync at the end of the request, an explicit Response.StartAsync(), or SendFileAsync), StartAsync copied the still-empty pre-start buffer to the Lambda stream, and the later flush wrote the pending bytes into the discarded pre-start buffer.

Fix. StartAsync now asks the writer to flush any pending bytes into the pre-start buffer and detach its inner writer before the buffer is copied to the Lambda stream, so pending bytes are sent in order and later writes bind to the live stream regardless of how the response is started.

Tests. Added four tests to StreamingResponseBodyFeatureTests: unflushed Advance followed by CompleteAsync, by an explicit StartAsync + flush, by SendFileAsync, and interleaved writes before and after start. All four fail on master and pass with this change. The full Amazon.Lambda.AspNetCoreServer.Test suite passes on net8.0 and net10.0 (229/229).

Found by an automated concurrency analysis run (Unveil) and reproduced end to end through APIGatewayHttpApiV2ProxyFunction.FunctionHandlerAsync with response streaming enabled.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

When response streaming is enabled, StreamingResponseBodyFeature.Writer binds
its inner PipeWriter to the pre-start buffer on first use. The inner writer was
only rebound to the Lambda response stream when the application itself flushed
or completed the writer. If the response was started any other way
(CompleteAsync at the end of the request, an explicit Response.StartAsync, or
SendFileAsync), bytes the application had advanced but not flushed were later
flushed into the discarded pre-start buffer and never reached the client.

StartAsync now flushes any pending writer bytes into the pre-start buffer and
detaches the inner writer before copying the buffer to the Lambda stream, so
subsequent writes bind to the live stream.
@normj
normj marked this pull request as ready for review November 13, 2026 19:05
@normj
normj requested review from a team as code owners November 13, 2026 19:05
@afroz429
afroz429 merged commit 0c74cae into master Nov 13, 2026
10 of 13 checks passed
@afroz429
afroz429 deleted the normj/streaming-bodywriter-unflushed-bytes branch November 13, 2026 20:04
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.

3 participants