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 16:43:18 UTC