refactor: split empty data frame count from budget - #945
Merged
Conversation
Sruhvx-jpg
added a commit
to Sruhvx-jpg/h2
that referenced
this pull request
Aug 23, 2026
Problem HTTP/2 flow control limits DATA payload bytes, but not the framing overhead from excessive numbers of small frames. A peer could fragment data into many tiny frames, causing disproportionate memory usage from queued events while remaining within flow-control windows. Solution Backport the framing overhead budget and empty frame handling from master (hyperium#935, hyperium#940, hyperium#942, hyperium#945, hyperium#946) to the 0.3.x maintenance branch. Validation Ran the full test suite and added regression integration tests in stream_states.rs.
lightsofapollo
added a commit
to gpu-cli/h2
that referenced
this pull request
Sep 1, 2026
…etime total MAX_RECV_EMPTY_DATA_FRAMES (introduced in 0.4.19, hyperium#945) counts empty non-final DATA frames over the LIFETIME of a connection and closes the connection with GOAWAY ENHANCE_YOUR_CALM at 100. Some legitimate HTTP/2 stacks emit one empty non-final DATA frame per request while ending the body — Bun's node:http2 does (HEADERS, DATA(payload), DATA(len=0), DATA(len=0, END_STREAM) on every grpc-js call) — so the 100th call on a long-lived connection gets the whole connection killed. Empty non-final DATA frames are legal (RFC 9113 §6.1); the flood this cap defends against is a run of them carrying no useful traffic. Count consecutive empty non-final frames PER STREAM instead: the stream's own non-empty DATA resets its run. Per stream, because concurrent streams flush their empty frames in batches, so legitimate empties from different streams arrive back to back and a connection-wide run counter still trips. A pure empty-frame flood on any one stream still kills the connection at 100, and total no-op frames stay bounded by max_concurrent_streams x the cap. Repro that motivated this (tonic 0.12 server, grpc-js 1.14.1 client under Bun 1.3.5, one connection): stock 0.4.19 dies at exactly the 100th sequential unary call with "too_many_data_frames"; 200-wide concurrent bursts die regardless of window sizing. With this change 500 sequential and 3x200 concurrent calls all complete. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G2Nkt6B96LEFFSQLteSBXp
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.
cc #944