Skip to content

transport: add data_frame_budget to client Endpoint - #2834

Draft
LukasKorba wants to merge 1 commit into
grpc:masterfrom
LukasKorba:expose-data-frame-budget
Draft

transport: add data_frame_budget to client Endpoint#2834
LukasKorba wants to merge 1 commit into
grpc:masterfrom
LukasKorba:expose-data-frame-budget

Conversation

@LukasKorba

Copy link
Copy Markdown

⚠ Blocked on hyperium/hyper#4164 — draft until it merges and releases.

Motivation

h2 0.4.18 added a client-side budget (client::Builder::data_frame_budget)
that guards a connection against floods of small DATA frames, closing it
with GOAWAY(ENHANCE_YOUR_CALM) once the budget is exhausted. This is a
correctness fix in h2 (see hyperium/h2 CHANGELOG.md 0.4.16-0.4.18), but
its default of 25,600 bytes can be exhausted by well-behaved peers: a
server-streaming RPC that emits one small message per frame -- one
HTTP/2 DATA frame per item in a long stream -- can trip the guard
partway through, even though both sides are individually within the
HTTP/2 spec.

tonic already exposes sibling h2 client settings on Endpoint
(max_frame_size, initial_stream_window_size, http2_header_table_size,
...), but had no way to raise this new budget, so callers hitting the
guard had no mitigation short of pinning h2 below 0.4.16.

This depends on hyperium/hyper exposing the setting on
client::conn::http2::Builder, which it does not yet as of hyper 1.11.0
(see the sibling expose-data-frame-budget branch on hyperium/hyper);
this change should land after that one merges and releases.

Solution

Add a new data_frame_budget field to Endpoint, mirroring the
max_frame_size pattern:

  • Add data_frame_budget: Option<usize> field to Endpoint
  • Add public pub fn data_frame_budget(self, budget: impl Into<Option<usize>>) -> Self builder method
  • Wire the field through to hyper's Builder::data_frame_budget() in
    connection.rs
  • Include an integration test verifying the setting is accepted and a
    basic RPC still completes end-to-end (h2's own test suite covers the
    budget's flood-detection behavior; this test only proves tonic's
    plumbing)

Evidence

hyper, cargo test -p tonic --all-features is green (212 tests incl. a new doctest), a new
integration test (tests/data_frame_budget.rs) proves the plumbing end-to-end, and against a
real public gRPC server that emits one small DATA frame per message (Zcash lightwalletd,
10,000-message range): default budget trips h2's too_many_data_frames guard on the released
stack; endpoint.data_frame_budget(256_000) completes the same range; data_frame_budget(1024)
fails immediately — the knob verifiably drives h2 through the whole chain. Motivating context:
zcash/lightwalletd#593.

## Motivation

h2 0.4.18 added a client-side budget (client::Builder::data_frame_budget)
that guards a connection against floods of small DATA frames, closing it
with GOAWAY(ENHANCE_YOUR_CALM) once the budget is exhausted. This is a
correctness fix in h2 (see hyperium/h2 CHANGELOG.md 0.4.16-0.4.18), but
its default of 25,600 bytes can be exhausted by well-behaved peers: a
server-streaming RPC that emits one small message per frame -- one
HTTP/2 DATA frame per item in a long stream -- can trip the guard
partway through, even though both sides are individually within the
HTTP/2 spec.

tonic already exposes sibling h2 client settings on Endpoint
(max_frame_size, initial_stream_window_size, http2_header_table_size,
...), but had no way to raise this new budget, so callers hitting the
guard had no mitigation short of pinning h2 below 0.4.16.

This depends on hyperium/hyper exposing the setting on
client::conn::http2::Builder, which it does not yet as of hyper 1.11.0
(see the sibling `expose-data-frame-budget` branch on hyperium/hyper);
this change should land after that one merges and releases.

## Solution

Add a new `data_frame_budget` field to `Endpoint`, mirroring the
`max_frame_size` pattern:

- Add `data_frame_budget: Option<usize>` field to `Endpoint`
- Add public `pub fn data_frame_budget(self, budget: impl
  Into<Option<usize>>) -> Self` builder method
- Wire the field through to hyper's `Builder::data_frame_budget()` in
  connection.rs
- Include an integration test verifying the setting is accepted and a
  basic RPC still completes end-to-end (h2's own test suite covers the
  budget's flood-detection behavior; this test only proves tonic's
  plumbing)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@linux-foundation-easycla

Copy link
Copy Markdown

CLA Not Signed

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.

1 participant