Repository navigation
Conversation
Adds Endpoint::http2_max_concurrent_reset_streams and Endpoint::http2_reset_stream_duration, which set hyper's client options of the same name. A long-lived channel that drops many response streams with DATA still in flight can then make h2 remember those resets long enough to ignore the late DATA, rather than count it toward the local error reset limit and close the connection with GOAWAY(ENHANCE_YOUR_CALM). Also adds Endpoint::http2_max_local_error_reset_streams, the client-side counterpart of the Server option, so a caller can raise the limit or disable it with None. Unset keeps hyper's default of 1024. This follows grpc#2708. Requires hyper 1.10, which added the client reset_stream_duration option.
On Windows, a write after the client closed the connection resets it, and the reset discards a GOAWAY that the test server did not read yet. The server now writes the late DATA from a separate task and keeps reading.
A client that closes the socket with unread late DATA resets the connection, and Windows then discards the GOAWAY that the test server did not read yet. On Windows, the test that expects the connection to close also accepts that reset.
adriangb
commented
Oct 28, 2026
| # transport | ||
| h2 = {version = "0.4", optional = true} | ||
| hyper = {version = "1", features = ["http1", "http2"], optional = true} | ||
| hyper = {version = "1.10", features = ["http1", "http2"], optional = true} |
Contributor
Author
There was a problem hiding this comment.
This probably needs some discussion if we really need it
Contributor
Author
There was a problem hiding this comment.
Moved the bump out of this PR. max_concurrent_reset_streams is in every hyper 1.x release, so this PR keeps hyper = "1". The two options that need hyper 1.9 and 1.10 are now in #2972, which also offers an optional-dependency feature so that only users who enable it need hyper 1.10.
…arate PR The client reset_stream_duration option needs hyper 1.10, and the client max_local_error_reset_streams option needs hyper 1.9. max_concurrent_reset_streams is in every hyper 1.x release, so this PR keeps only that option and the hyper requirement stays at 1.
adriangb
added a commit
to pydantic/grpc-rust
that referenced
this pull request
Oct 28, 2026
Adds Endpoint::http2_max_concurrent_reset_streams, which sets hyper's client option of the same name. A long-lived channel that drops many response streams with DATA still in flight can then make h2 remember those resets and ignore the late DATA, rather than count it toward the local error reset limit and close the connection with GOAWAY(ENHANCE_YOUR_CALM). Upstream PR: grpc#2969
Contributor
Author
|
Combined into #2972. |
LucioFranco
pushed a commit
that referenced
this pull request
Oct 29, 2026
Replaces #2969, combined here as suggested. ## Motivation When a client resets many streams, a server can still send DATA for them. h2 remembers only 50 reset streams for 1 s by default. DATA for a forgotten stream counts toward the local error reset limit (1024), and reaching it closes the connection with `GOAWAY(ENHANCE_YOUR_CALM, "too_many_internal_resets")`. `Endpoint` exposes none of these h2 client options today, so a caller must build the connection with hyper directly to change them. ## Solution - Add `Endpoint::http2_max_concurrent_reset_streams(usize)`: how many reset streams h2 remembers. - Add `Endpoint::http2_reset_stream_duration(Duration)`: how long h2 remembers a reset stream. - Add `Endpoint::http2_max_local_error_reset_streams(impl Into<Option<usize>>)`, the client counterpart of the `Server` option, as in #2708 (closed). Unset keeps hyper's default of 1024, and `None` disables the limit. - Raise tonic's `hyper` requirement from `1` to `1.10`, the first release with the client `reset_stream_duration` option.
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.
Motivation
A long-lived client
Channelthat drops many streaming responses before their end (a cancelled query, a client that went away) resets each stream. The server usually still has DATA in flight for those streams, up to the stream window. h2 ignores DATA for a reset stream that it still remembers. By default it remembers at most 50 reset streams. DATA for a reset stream that h2 has forgotten makes it reset the stream again and count that towardmax_local_error_reset_streams(1024). At the limit, h2 closes the connection withGOAWAY(ENHANCE_YOUR_CALM, "too_many_internal_resets"), and every in-flight RPC on it fails.The fix is to have h2 remember more reset streams, so that late DATA is ignored and not counted. Go's
x/net/http2, grpc-go and Netty all ignore late DATA this way. hyper's client builder hasmax_concurrent_reset_streamsin every 1.x release, butEndpointdoes not expose it. So today a client has to drop down to hyper's HTTP/2 builder and give upEndpoint/Channelto set it.Solution
Endpoint::http2_max_concurrent_reset_streams(usize). When set, it is passed to hyper's client HTTP/2 builder. When unset, h2's default is unchanged, so this is non-breaking. Thehyperrequirement does not change.tests/integration_tests/tests/http2_reset_streams.rs. A raw HTTP/2 server answers 100 requests with headers only. The client drops each response, and the server then sends 40 late 1 KiB DATA frames on every reset stream.GOAWAY("too_many_internal_resets"). This shows the test reproduces the failure. On Windows, the client's close can reset the connection before the server reads theGOAWAY, so there the test also accepts a reset.http2_max_concurrent_reset_streams(10_000), the connection stays open.A follow-up PR, #2972, adds
http2_reset_stream_durationandhttp2_max_local_error_reset_streams(the client counterpart of theServeroption, from #2708). They need hyper 1.10 and 1.9, so that PR also raises thehyperrequirement.The branch is based on
master. I'm happy to retarget it tov0.14.xif you'd prefer that.