Repository navigation
fix(http2): Http2Client applies response-body backpressure to the HTTP/2 stream and uses 4 MiB / 16 MiB receive windows - #2015
Merged
mosuem merged 0 commit intoOct 22, 2026
Conversation
mosuem
force-pushed
the
mosum/http2-6-http2-client-pool-and-flow-control
branch
from
October 8, 2026 12:04
3b80204 to
faab4e8
Compare
mosuem
force-pushed
the
mosum/http2-6b-http2-client-backpressure-and-windows
branch
from
October 8, 2026 12:04
3cd4fe8 to
3695462
Compare
mosuem
force-pushed
the
mosum/http2-6-http2-client-pool-and-flow-control
branch
from
October 9, 2026 07:54
faab4e8 to
b4bd763
Compare
mosuem
force-pushed
the
mosum/http2-6b-http2-client-backpressure-and-windows
branch
from
October 9, 2026 07:54
3695462 to
37e1b65
Compare
mosuem
force-pushed
the
mosum/http2-6-http2-client-pool-and-flow-control
branch
from
October 9, 2026 08:18
b4bd763 to
c157584
Compare
mosuem
force-pushed
the
mosum/http2-6b-http2-client-backpressure-and-windows
branch
from
October 9, 2026 08:18
37e1b65 to
24dacab
Compare
PR Health
|
mosuem
force-pushed
the
mosum/http2-6b-http2-client-backpressure-and-windows
branch
from
October 9, 2026 08:48
24dacab to
ed41d40
Compare
mosuem
force-pushed
the
mosum/http2-6-http2-client-pool-and-flow-control
branch
from
October 9, 2026 08:48
c157584 to
9174e50
Compare
Contributor
|
@mosuem thanks for this PR. The 4 MiB / 16 MiB window change in Since this PR sits on top of the stack, could the window change land separately against |
mosuem
force-pushed
the
mosum/http2-6b-http2-client-backpressure-and-windows
branch
from
October 22, 2026 06:40
ed41d40 to
1d29611
Compare
mosuem
force-pushed
the
mosum/http2-6-http2-client-pool-and-flow-control
branch
from
October 22, 2026 06:44
9174e50 to
c038a93
Compare
mosuem
deleted the
mosum/http2-6b-http2-client-backpressure-and-windows
branch
October 22, 2026 06:44
mosuem
changed the base branch from
mosum/http2-6-http2-client-pool-and-flow-control
to
master
October 22, 2026 06:44
mosuem
restored the
mosum/http2-6b-http2-client-backpressure-and-windows
branch
October 22, 2026 06:45
mosuem
deleted the
mosum/http2-6b-http2-client-backpressure-and-windows
branch
October 23, 2026 05:31
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
This PR was generated by an AI coding agent (Jetski) on behalf of @mosuem.
Summary
Http2Clientforwarded everyDATAframe of a response into theStreamControllerbehindStreamedResponse.streamregardless of whether anyone was reading it. The HTTP/2 stream's subscription was never paused, so the stream kept acknowledging data withWINDOW_UPDATEframes (RFC 9113 Section 5.2) and the server kept sending: a consumer that paused the body — or simply read it slower than the network delivered it — accumulated the entire response in memory. HTTP/2 flow control exists precisely so that a receiver can stop granting credit when it is not consuming.Separately, the client used the protocol's default 65535-byte windows, which cap a connection at 64 KiB per round trip no matter how many streams it carries.
Changes
lib/src/http2_client.dartbodyControllernow forwardsonPause/onResumeto the HTTP/2 stream's subscription. The stream's incoming queue then stops dispatching (StreamMessageQueueIn._tryDispatchonly dispatches to a non-paused listener, and it is dispatching that callswindowHandler.dataProcessed, i.e. sendsWINDOW_UPDATE), so once the data the server was already allowed to send has arrived it must wait. Memory held by a paused body is bounded by the stream window; across all of a connection's paused streams by the connection window._dialconfiguresClientSettings(streamWindowSize: 4 MiB, connectionWindowSize: 16 MiB). The two changes belong together: with backpressure and the 64 KiB connection window, one paused body would exhaust the connection's credit and stall every other stream on it (the connection queue stops handing data to a buffering stream and only replenishes connection credit for data it handed over).Not changed, as candidates for follow-ups: a body that has not been listened to yet is still buffered without bound (pausing the HTTP/2 subscription until the first listener would delay
onDone, and with it the pool slot's release, for callers that never read the body — a behavior change worth its own discussion); and whether the window sizes should beHttp2Clientconstructor parameters rather than constants.Test Verification (Fails Before$\rightarrow$ Passes After)
a-paused-response-body-stops-granting-flow-control-creditintest/http2_client_test.dart, against a frame-level TLS server (FrameReader/FrameWriteron the accepted socket), with no sleeps:WINDOW_UPDATEraises the connection window to 16 MiB.DATAfollowed by a PING. The client answers the PING only after processing every frame before it, so any streamWINDOW_UPDATEarriving before the PING ACK was granted while paused.DATAwithEND_STREAMand checks the body is complete (4 MiB + 3 bytes).Before fix (test on the previous library code — fails at step 1):
With only the window sizes applied (no
onPause/onResumeforwarding) it fails at step 2, at the first half-window acknowledgement:After fix:
Full
package:http2suite at this point of the stack (onmaster):+268 ~7: All tests passed!