Repository navigation
fix: the h2 header-list bound is a limit, so it belongs in the reuse hash - #36
Merged
Merged
Conversation
Review follow-ups to #35, which merged before they were addressed. The doc contract was wrong in the case the setting exists for. It said h2 "allows a multiple of this value before failing the stream outright"; measured on this branch with a bound of 8KiB, 2x over drops the headers and errors the stream with the connection still usable, but 32x over raises a *connection* error — ENHANCE_YOUR_CALM, `header_list_way_too_large` — and the connection is finished. On a pooled, multiplexed upstream that fails every other request in flight on it. That blast radius is the trade the setting makes and it was neither documented nor tested; now it is both, including a case for the stream-local band the old comment described and nothing covered. `h2_max_header_list_size` now takes part in `reuse_hash`. Excluding it alongside the window sizes was wrong: those are tuning parameters, this is a limit, and sharing a pool across different values breaks it in both directions. A peer that asked for a bound gets none when an unbounded peer established the connection, and a peer that asked for none inherits a sibling's cap — which, past the abuse multiplier, takes the shared connection down with it. A limit that applies or not depending on who connected first is not a limit. `Some(0)` is rejected at handshake, as the window sizes already are. It does not mean "no bound": it makes the abuse limit zero too, so the first response on every connection to that peer is a connection error, with nothing pointing at the misconfiguration. The test server now `expect`s its send instead of swallowing the error. If a future h2 enforced the peer's advertised limit on send, the server would quietly send nothing and the oversize test would still pass on the resulting error — testing nothing.
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.
Follow-up to #35, which merged before these review findings were addressed.
The documented failure mode was wrong
#35 said h2 "allows a multiple of this value before failing the stream
outright". Measured on this branch, with a bound of 8KiB:
header_list_way_too_largePast the multiplier it is a connection error, not a stream one. On a
pooled, multiplexed upstream that also fails every other request in flight on
that connection — where before the setting existed the response was simply
accepted.
That blast radius is the actual trade this setting makes: a bounded failure
on a shared connection instead of an unbounded header list. It is now in the
doc comment, and there is a test for the stream-local band that the old
comment described and nothing covered — both earlier tests drove 32x, so only
the connection-error path was ever exercised.
Reuse hash
h2_max_header_list_sizenow takes part inreuse_hash. #35 excluded it "likethe window sizes"; that was the wrong comparison. Those are tuning
parameters, this is a limit, and sharing a pool across different values
breaks it in both directions:
by a peer that set nothing;
responses fail — taking the shared connection with them, per the table
above.
A limit that applies or not depending on which peer connected first is not a
limit. New test: peers differing only in this value do not share a
connection, and identical peers still pool.
Some(0)Rejected at handshake, as the two adjacent window settings already are. It
does not mean "no bound" — it makes the abuse limit zero too, so the first
response on every connection to that peer is a connection error, with no
diagnostic pointing at the config.
Test hardening
serve_one_fat_responseswallowed its send failure. If a future h2 enforcedthe peer's advertised
SETTINGS_MAX_HEADER_LIST_SIZEon send, the serverwould quietly send nothing and the oversize test would still pass on the
resulting error — going vacuous without failing. It now
expects the send.Not addressed here
The knob still has no consumer, so the exposure #35 described is not closed
by either PR. That is the synapse side and is next.
Verification
cargo fmt --checkclean;cargo clippy --all-targets --all --deny=warningsexit 0;
cargo test -p pingora-core --lib623 pass, with onlyconnectors::l4::tests::test_conn_error_addr_not_availfailing, which needshost networking. Each new guard was mutated: removing the reuse-hash entry,
removing the zero check, and widening the band test each fail exactly one
test.