Skip to content

test(http2): cover small flow-control windows - #4212

Closed
jeremyjpj0916 wants to merge 3 commits into
hyperium:masterfrom
jeremyjpj0916:fix/h2-avoid-sliver-data-frames
Closed

jeremyjpj0916 wants to merge 3 commits into
hyperium:masterfrom
jeremyjpj0916:fix/h2-avoid-sliver-data-frames

Conversation

@jeremyjpj0916

@jeremyjpj0916 jeremyjpj0916 commented Sep 30, 2026 •

Copy link
Copy Markdown

The original version of this PR waited for at least 1 KiB of HTTP/2 send capacity before handing a body chunk to h2. Review correctly identified that this can hang when a peer advertises a smaller stream window.

I reproduced that failure through Ferrum Edge 0.9.10 with a standards-compliant h2 backend using a 512-byte stream window: request headers reached the backend, but the backend received zero body bytes until the request timed out. A fixed positive threshold cannot distinguish a transient connection-window sliver from all of the capacity a peer has made available, so Hyper must allow body progress whenever capacity is nonzero.

This revision removes the 1 KiB gate and adds two deterministic regression tests:

  • a 2 KiB request body makes progress through a peer's 512-byte stream window;
  • a request uses the final available byte of connection capacity without waiting for a WINDOW_UPDATE.

The receiver-side protection against excessive small DATA frames belongs in h2. hyperium/h2#965 updates h2's automatic DATA-frame budget whenever adaptive flow control changes the target connection window, preserving explicit budgets and outstanding charges.

Validation:

  • cargo test --features full --test client (70 passed)
  • cargo fmt --all -- --check

Related: #4211

A body chunk was handed to h2 once the stream held any capacity. h2 cuts
DATA frames from the capacity a stream holds, so on a connection whose
window was nearly spent a chunk left as a 1-byte frame followed by more
slivers. h2 0.4.16+ servers charge small non-final DATA frames to a
per-connection budget and GOAWAY the connection with ENHANCE_YOUR_CALM
when it runs out.

Reserve min(len, 1024) instead of 1 and wait until that much is assigned.
The claim stays small, so hyperium#4003 still holds, but no sub-1 KiB first frame
is cut from a larger chunk.

Closes hyperium#4211
@seanmonstar

Copy link
Copy Markdown
Member

I think this causes other problems, making streams hang if the peer sets a smaller window than what is defined here as "useful".

Remove the fixed 1 KiB body-capacity gate because peers may legally advertise a smaller stream window. Add regressions showing that a 512-byte peer window and the final byte of connection capacity both make request-body progress.
@jeremyjpj0916 jeremyjpj0916 changed the title fix(http2): wait for useful send capacity before handing a chunk to h2 test(http2): cover small flow-control windows Oct 4, 2026
@jeremyjpj0916

Copy link
Copy Markdown
Author

Sean, your concern was correct. I reproduced the hang through Ferrum Edge 0.9.10 against a legal 512-byte peer stream window: the backend received the request headers but zero body bytes before timeout.

I removed the fixed 1 KiB gate and replaced the original test with regressions requiring progress through a 512-byte stream window and with only one connection-window byte available. I also opened hyperium/h2#965 to make h2's automatic DATA-frame budget follow runtime target-window changes, as suggested in #4211.

@jeremyjpj0916

Copy link
Copy Markdown
Author

Closing this PR as superseded by hyperium/h2#965.

The review feedback was correct on both points: a fixed minimum send threshold can deadlock a peer's smaller legal stream window, and the receiver-side DATA-frame budget should follow runtime target-window changes. Ferrum Edge 0.9.10 reproduced the former with a 512-byte backend stream window: headers arrived, but zero body bytes did. The corrected Ferrum build sends the full 2,048-byte wire body, beginning with a 512-byte DATA frame.

Hyper therefore needs to keep making progress whenever capacity is positive; h2#965 contains the actual budget fix. The test-only commits here and Ferrum's permanent regressions preserve the evidence, but this PR no longer fixes #4211 by itself.

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.

2 participants