Visitar URL original
Do not open a stream for a PRIORITY frame by kazu-yamamoto · Pull Request #195 · kazu-yamamoto/http2 · GitHub
Skip to content

Do not open a stream for a PRIORITY frame - #195

Merged
kazu-yamamoto merged 1 commit into
mainfrom
fix-idle-priority
Sep 26, 2026
Merged

kazu-yamamoto merged 1 commit into
mainfrom
fix-idle-priority

Conversation

@kazu-yamamoto

Copy link
Copy Markdown
Owner

A peer could shut a server connection off from new requests with 64 PRIORITY frames.

The problem

A PRIORITY frame for a stream id the server had not seen yet went down the same path as HEADERS in getOddStream. That path goes through openOddStreamCheck, which creates the stream and inserts it into the table, where it takes a concurrency slot. No HEADERS need ever follow, and nothing else removes an idle stream, so the slot was never given back.

With the default SETTINGS_MAX_CONCURRENT_STREAMS of 64, 64 PRIORITY frames on unused ids were enough: every request after them was refused with REFUSED_STREAM for the rest of the connection. Old Firefox's idle placeholder streams (PRIORITY frames used to build a tree) leaked the same way, only slowly.

The fix

PRIORITY does not open a stream: RFC 9113 §5.1 lists it among the frames an idle stream may receive, with no change of state. So a PRIORITY on an idle id no longer creates anything. It is checked and dropped by the branch of controlOrStream that already handles PRIORITY for a stream we do not have.

With no idle entry left behind, two related cases now come out as the RFC has them:

  • A later HEADERS on an id lower than a stream already opened is a decreasing stream id (§5.1.1), a connection error. Before, the leftover entry made it look like an idle stream being opened.
  • RST_STREAM on such an id is a frame on an idle stream, also a connection error.

One change in behaviour

A PRIORITY on an idle stream that depends on itself used to open the stream and draw RST_STREAM on it. §5.1 does not allow RST_STREAM on an idle stream either. It is now a connection error, the same as for a stream we do not have. h2spec gives the same results as before, including its self-dependency test.

Test

New in test/HTTP2/ServerSpec.hs. Over a raw socket, since the client library does not send PRIORITY, the test sends PRIORITY frames for 100 streams that are never opened, then a request on stream 203, and reads what the server answers:

  • main: RST_STREAM RefusedStream, 3 of 3 runs.
  • This PR: the response HEADERS.

The code that reads frames off the raw socket, recvFrame, is now shared with the MadeYouReset test (#190), which still passes.

🤖 Generated with Claude Code

A PRIORITY frame for a stream id the server had not seen yet went
through openOddStreamCheck like HEADERS, and so created the stream and
put it in the table, where it took a concurrency slot.  No HEADERS need
ever follow, and nothing else removes an idle stream, so the slot was
never given back: 64 PRIORITY frames -- the default
SETTINGS_MAX_CONCURRENT_STREAMS -- and every request after them was
refused with REFUSED_STREAM, for the rest of the connection.

PRIORITY does not open a stream (RFC 9113, section 5.1).  It is now
checked and dropped like one for a stream we do not have.  With no idle
entry left behind, a later HEADERS on a lower id than the PRIORITY's is
a decreasing stream id, and RST_STREAM on it a frame on an idle stream,
both connection errors as the RFC has them.

A PRIORITY on an idle stream that depends on itself is now a connection
error rather than RST_STREAM on the idle stream, which section 5.1 does
not allow either.  h2spec gives the same results as before.

The test reads frames off a raw socket; the helper for that is shared
with the MadeYouReset test now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kazu-yamamoto
kazu-yamamoto merged commit 04fdfde into main Sep 26, 2026
8 checks passed
@kazu-yamamoto
kazu-yamamoto deleted the fix-idle-priority branch September 26, 2026 02:16
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