Skip to content

Hard-enforce max_active_streams_in at HTTP/2 stream creation#13386

Open
maskit wants to merge 1 commit into
apache:masterfrom
maskit:h2-zero-window-cap-master
Open

Hard-enforce max_active_streams_in at HTTP/2 stream creation#13386
maskit wants to merge 1 commit into
apache:masterfrom
maskit:h2-zero-window-cap-master

Conversation

@maskit

@maskit maskit commented Jul 15, 2026

Copy link
Copy Markdown
Member

proxy.config.http2.max_active_streams_in has only adjusted the advertised SETTINGS_MAX_CONCURRENT_STREAMS, leaving the proxy unable to bound buffered response memory against clients that open streams faster than the advisory throttle reacts. It now also carries a finite default so the cap is no longer effectively unlimited.

The new knob proxy.config.http2.max_active_streams_policy_in selects enforcement. Value 0 keeps the advisory behavior that lowers advertised concurrency to proxy.config.http2.min_concurrent_streams_in. Value 1 refuses new inbound streams with REFUSED_STREAM once the process-wide active-stream count reaches the limit and leaves the advertised value untouched, so the min_concurrent_streams_in reduction that disrupts some clients no longer applies.

max_active_streams_in now defaults to 200000 rather than 0. Operators should size it to their available memory budget; set it to 0 to disable the cap.

Refusal happens after HPACK decoding so the dynamic table stays in sync with the client and the connection survives. proxy.process.http2.max_active_streams_exceeded_in counts each refusal.

proxy.config.http2.max_active_streams_in has only adjusted the advertised SETTINGS_MAX_CONCURRENT_STREAMS, leaving the proxy unable to bound buffered response memory against clients that open streams faster than the advisory throttle reacts. It now also carries a finite default so the cap is no longer effectively unlimited.

The new knob proxy.config.http2.max_active_streams_policy_in selects enforcement. Value 0 keeps the advisory behavior that lowers advertised concurrency to proxy.config.http2.min_concurrent_streams_in. Value 1 refuses new inbound streams with REFUSED_STREAM once the process-wide active-stream count reaches the limit and leaves the advertised value untouched, so the min_concurrent_streams_in reduction that disrupts some clients no longer applies.

max_active_streams_in now defaults to 200000 rather than 0. Operators should size it to their available memory budget; set it to 0 to disable the cap.

Refusal happens after HPACK decoding so the dynamic table stays in sync with the client and the connection survives. proxy.process.http2.max_active_streams_exceeded_in counts each refusal.
@maskit maskit self-assigned this Jul 15, 2026
Copilot AI review requested due to automatic review settings July 15, 2026 14:05
@maskit maskit added the HTTP/2 label Jul 15, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request hard-enforces the inbound HTTP/2 proxy.config.http2.max_active_streams_in limit at stream creation time (after HPACK decode), adding a new policy knob to choose between the existing advisory throttling behavior and hard refusal with REFUSED_STREAM. It also updates defaults, documentation, and adds gold tests to validate enforcement and HPACK dynamic-table synchronization across refused streams.

Changes:

  • Add proxy.config.http2.max_active_streams_policy_in to select advisory throttling (0) vs hard refusal (1) when the process-wide inbound active-stream cap is reached.
  • Change the default proxy.config.http2.max_active_streams_in to 200000 and update HTTP/2 code paths to refuse new inbound streams under the enforce policy while leaving advertised concurrency unchanged.
  • Add gold tests (replays + custom HTTP/2 client) to validate enforcement behavior and ensure HPACK stays in sync across refused streams.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/gold_tests/h2/replay/http2_max_active_streams_enforce.replay.yaml New origin replay for enforce-mode scenario (streams refused at proxy).
tests/gold_tests/h2/replay/http2_max_active_streams_advisory.replay.yaml New origin replay for advisory-mode scenario (all streams reach origin).
tests/gold_tests/h2/http2_max_active_streams.test.py New gold test covering policy behavior and HPACK sync expectations.
tests/gold_tests/h2/clients/h2_max_active_streams.py New custom HTTP/2 client used by the gold test to exercise stream refusal and HPACK dynamic table reuse.
src/records/RecordsConfig.cc Update defaults and add the new max_active_streams_policy_in records.yaml knob definition.
src/proxy/http2/Http2ConnectionState.cc Enforce the cap on inbound streams after header decode and bypass advisory throttling of advertised settings under enforce policy.
src/proxy/http2/HTTP2.cc Initialize/read the new config, update default for max_active_streams_in, and register a new metric counter.
include/proxy/http2/HTTP2.h Add the new config field and new stats counter to the HTTP/2 stats block.
doc/admin-guide/files/records.yaml.en.rst Document default change and add docs for the new enforcement policy knob.

Comment thread tests/gold_tests/h2/clients/h2_max_active_streams.py
Comment thread tests/gold_tests/h2/http2_max_active_streams.test.py
Comment thread src/proxy/http2/Http2ConnectionState.cc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants