RE: Are HTTP/2 state changes atomic with respect to SETTINGS_MAX_CONCURRENT_STREAMS?

From 5.1:  "For the purpose of state transitions, the END_STREAM flag is
   processed as a separate event to the frame that bears it; a HEADERS
   frame with the END_STREAM flag set can cause two state transitions."

This means that the frame causes two events -- increasing the number of concurrent streams, then decreasing it again.  If the increase violates the limit, the limit has been violated -- regardless of the fact that you pop back below the limit as the next event.

Of course, that also means that despite your limit of 5, you could have twenty such HEADERS-w/-END_STREAM in a row and be perfectly compliant.

-----Original Message-----
From: Cory Benfield <cory@lukasa.co.uk> 
Sent: Friday, February 8, 2019 8:43 AM
To: ietf-http-wg@w3.org
Subject: Are HTTP/2 state changes atomic with respect to SETTINGS_MAX_CONCURRENT_STREAMS?

Hey folks,

While implementing (yet another) HTTP/2 stack, I have bumped into an edge case that I hadn't noticed before and wanted to get feedback from the WG about what the consensus interpretation is.

Here's the text of RFC 7540 § 5.1.2:

>  Streams that are in the "open" state or in either of the "half- 
> closed" states count toward the maximum number of streams that an 
> endpoint is permitted to open.  Streams in any of these three states 
> count toward the limit advertised in the 
> SETTINGS_MAX_CONCURRENT_STREAMS setting.  Streams in either of the 
> "reserved" states do not count toward the stream limit.

So far so clear. My question is this: if a server reserves a stream, and then on the reserved stream sends a HEADERS frame with END_STREAM set, does the reserved stream ever count against SETTINGS_MAX_CONCURRENT_STREAMS?

This can be thought about through this thought experiment. Imagine a client sets SETTINGS_MAX_CONCURRENT_STREAMS to 5. The server sends six PUSH_PROMISE frames, and then opens 5 streams with HEADERS(END_HEADERS). Then, the server sends
HEADERS(END_HEADERS,END_STREAM) on the sixth reserved stream. Does this trigger a violation of SETTINGS_MAX_CONCURRENT_STREAMS?

RFC 7540 never quite says whether or not HEADERS + END_STREAM triggers atomic or sequential state transitions. § 5.1 says (while describing the idle state):

> Sending or receiving a HEADERS frame causes the stream to become 
> "open".  The stream identifier is selected as described  in Section 
> 5.1.1.  The same HEADERS frame can also cause a stream to immediately 
> become "half-closed".

This says "immediately", without defining the semantics of immediacy.
This text stands in contrast to the text describing the reserved states, which says:

> The endpoint can send a HEADERS frame.  This causes the stream to open 
> in a "half-closed (remote)" state.

This does not say "immediately transition" but instead "open in".

This is made more complex by the stream state diagram, which implies that the END_STREAM transition occurs separately from the HEADERS transition.

My instinct is to say that these streams do not count against SETTINGS_MAX_CONCURRENT_STREAMS. This instinct is largely informed by Martin's previous characterisation of RFC 7540 as being written "as though headers were free". In a model where headers cost nothing, a stream guaranteed to be made only of headers never counts against SETTINGS_MAX_CONCURRENT_STREAMS.

That said, I'm interested in knowing what other implementations do. We should also consider whether it is worth drafting an erratum to attempt to clarify the state transitions of END_STREAM.

Thanks,

Cory

Received on Friday, 8 February 2019 23:41:11 UTC