close
Skip to content

fix(http2): fix internals of HTTP/2 CONNECT upgrades#3967

Merged
seanmonstar merged 1 commit intomasterfrom
push-nvrlowqkoptx
Nov 10, 2025
Merged

fix(http2): fix internals of HTTP/2 CONNECT upgrades#3967
seanmonstar merged 1 commit intomasterfrom
push-nvrlowqkoptx

Conversation

@seanmonstar
Copy link
Copy Markdown
Member

@seanmonstar seanmonstar commented Oct 24, 2025

This refactors the way hyper handles HTTP/2 CONNECT / Extended CONNECT. Before, an uninhabited enum was used to try to prevent sending of the Buf type once the STREAM had been upgraded. However, the way it was originally written was incorrect, and will eventually have compilation issues.

The change here is to spawn an extra task and use a channel to bridge the IO operations of the Upgraded object to be Cursor buffers in the new task.

ref: rust-lang/rust#147588
Closes #3966

BREAKING CHANGE: The HTTP/2 client connection no longer allows an executor that can not spawn itself.

This was an oversight originally. The client connection will now include spawning a future that keeps a copy of the executor to spawn other futures. Thus, if it is !Send, it needs to spawn !Send futures. The likelihood of executors that match the previously allowed behavior should be very remote.

There is also technically a semver break in here, which is that the Http2ClientConnExec trait no longer dyn-compatible, because it now expects to be Clone. This should not break usage of the conn builder, because it already separately had E: Clone bounds. If someone were using dyn Http2ClientConnExec, that will break. However, there is no purpose for doing so, and it is not usable otherwise, since the trait only exists to propagate bounds into hyper. Thus, the breakage should not affect anyone.

@seanmonstar seanmonstar requested a review from nox October 24, 2025 20:03
This refactors the way hyper handles HTTP/2 CONNECT / Extended CONNECT. Before,
an uninhabited enum was used to try to prevent sending of the `Buf` type once
the STREAM had been upgraded. However, the way it was originally written was
incorrect, and will eventually have compilation issues.

The change here is to spawn an extra task and use a channel to bridge the IO
operations of the `Upgraded` object to be `Cursor` buffers in the new task.

ref: rust-lang/rust#147588
@seanmonstar
Copy link
Copy Markdown
Member Author

@nox you originally implemented this, I presume because you needed it in an application. Would you be able to take a look, or even try the branch in your app?

@seanmonstar seanmonstar merged commit 58e0e7d into master Nov 10, 2025
21 of 22 checks passed
@seanmonstar seanmonstar deleted the push-nvrlowqkoptx branch November 10, 2025 16:20
abbshr pushed a commit to abbshr/hyper that referenced this pull request Apr 9, 2026
…adedSendStreamTask

Fix a backpressure bypass bug in UpgradedSendStreamTask::tick() where
poll_capacity() returning Poll::Pending caused a 'break 'capacity' that
fell through to rx.poll_next() -> send_data(), pushing data into the h2
send buffer without available capacity. This broke the HTTP/2 flow
control chain, causing unbounded memory growth (OOM) when downstream
consumers were slower than upstream producers.

The fix changes 'break 'capacity' to 'return Poll::Pending', which
correctly suspends the task until a WINDOW_UPDATE frame restores send
capacity. The now-unused 'capacity label is also removed.

This bug was introduced in hyper v1.8.0 (PR hyperium#3967) and affects
v1.8.0, v1.8.1, and v1.9.0. A single HTTP/2 CONNECT tunnel with
asymmetric upstream/downstream speeds could trigger OOM within seconds.

Add four integration tests covering H2 CONNECT backpressure scenarios:
- h2_connect_backpressure_respected: small window + large data transfer
- h2_connect_zero_window_then_release: normal path regression guard
- h2_connect_reset_during_backpressure: RST_STREAM error propagation
- h2_connect_backpressure_bidirectional: bidirectional data + backpressure
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.

HTTP/2 Upgraded wrongly uses a transparent uninhabited type

1 participant