Hard-enforce max_active_streams_in at HTTP/2 stream creation#13386
Open
maskit wants to merge 1 commit into
Open
Hard-enforce max_active_streams_in at HTTP/2 stream creation#13386maskit wants to merge 1 commit into
maskit wants to merge 1 commit into
Conversation
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.
Contributor
There was a problem hiding this comment.
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_into 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_into200000and 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. |
bryancall
approved these changes
Jul 15, 2026
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.
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.