Subject: Re: Design Issue: Max Concurrent Streams Limit and Unidirectional Streams

From: James M Snell <[email-masked]>
Date: Wed, 1 May 2013 10:26:26 -0700

Why not just bring the UNIDIRECTIONAL flag back as a PUSH_PROMISE
frame-specific flag? If a PUSH_PROMISE frame has the unidirectional
flag set, the stream is automatically half-closed in the return
direction. If the flag is unset, the promised stream remains half-open
until the client half-closes or a rst_stream is sent.

On Mon, Apr 29, 2013 at 2:44 PM, William Chan (陈智昌)
<[email-masked]> wrote:
> Remember we originally *had* a flag for UNIDIRECTIONAL, which we removed
> because it was redundant in the traditional HTTP use cases.
>
>
> On Mon, Apr 29, 2013 at 6:39 PM, Roberto Peon <[email-masked]> wrote:
>>
>> At worst, we burn a flag which states it is half-closed or unidirectional,
>> or provide some other information which identifies the IANA port number for
>> the overlayed protocol or something.
>> Anyway, *shrug*.
>> -=R
>>
>>
>> On Mon, Apr 29, 2013 at 2:32 PM, William Chan (陈智昌)
>> <[email-masked]> wrote:
>>>
>>> On Mon, Apr 29, 2013 at 6:17 PM, James M Snell <[email-masked]> wrote:
>>>>
>>>> +1 on this.  I like this approach.
>>>>
>>>> On Apr 29, 2013 2:15 PM, "Roberto Peon" <[email-masked]> wrote:
>>>>>
>>>>> I had thought to provide no explicit limit for PUSH_PROMISE, just as
>>>>> there is no limit to the size of a webpage, or the number of links upon it.
>>>>> The memory requirements for PUSH are similar or the same (push should
>>>>> consume a single additional bit of overhead per url, when one considers that
>>>>> the URL should be parsed, enqueued, etc.).
>>>>> If the browser isn't done efficiently, or, the server is for some
>>>>> unknown reason being stupid and attempting to DoS the browser with many
>>>>> resources that it will never use, then the client sends RST_STREAM for the
>>>>> ones it doesn't want, and makes a request on its own. all tidy.
>>>
>>>
>>> I don't feel too strongly here. I do feel like this is more of an edge
>>> case, possibly important for forward proxies (or reverse proxies speaking to
>>> backends over a multiplexed channel like HTTP/2). It doesn't really matter
>>> for my browser, so unless servers chime in and say they'd prefer a limit,
>>> I'm fine with this.
>>>
>>>>>
>>>>> As for PUSH'd streams, the easiest solution is likely to assume that
>>>>> the stream starts out in a half-closed state.
>>>
>>>
>>> I looked into our earlier email threads and indeed this is what we agreed
>>> on (http://lists.w3.org/Archives/Public/ietf-http-wg/2013JanMar/1106.html).
>>> I voiced some mild objection since if you view the HTTP/2 framing layer as a
>>> transport for another application protocol, then bidirectional server
>>> initiated streams might be nice. But in absence of any such protocol, this
>>> is a nice simplification.
>>>
>>>>>
>>>>> -=R
>>>>>
>>>>>
>>>>> On Mon, Apr 29, 2013 at 12:33 PM, William Chan (陈智昌)
>>>>> <[email-masked]> wrote:
>>>>>>
>>>>>> On Mon, Apr 29, 2013 at 3:46 PM, James M Snell <[email-masked]>
>>>>>> wrote:
>>>>>>>
>>>>>>>
>>>>>>> On Apr 29, 2013 11:36 AM, "William Chan (陈智昌)"
>>>>>>> <[email-masked]> wrote:
>>>>>>> >
>>>>>>> [snip]
>>>>>>>
>>>>>>> >
>>>>>>> >
>>>>>>> > Oops, forgot about that. See, the issue with that is now we've made
>>>>>>> > PUSH_PROMISE as potentially expensive as a HEADERS frame, since it does more
>>>>>>> > than just simple stream id allocation. I guess it's not really a huge issue,
>>>>>>> > since if it's used correctly (in the matter you described), then it
>>>>>>> > shouldn't be too expensive. If clients attempt to abuse it, then servers
>>>>>>> > should probably treat it in a similar manner as they treat people trying to
>>>>>>> > abuse header compression in all other frames with the header block, and kill
>>>>>>> > the connection accordingly.
>>>>>>> >
>>>>>>>
>>>>>>> Not just "potentially" as expensive..   As soon as we get a push
>>>>>>> promise we need to allocate state and hold onto it for an indefinite period
>>>>>>> of time. We do not yet know exactly when that compression context can be let
>>>>>>> go because it has not yet been bound to stream state.  Do push streams all
>>>>>>> share the same compression state? Do those share the same compression state
>>>>>>> as the originating stream? The answers might be obvious but they haven't yet
>>>>>>> been written down.
>>>>>>
>>>>>>
>>>>>> I guess I don't see per-stream state as being that expensive.
>>>>>> Compression contexts are a fixed state on a per-connection basis, meaning
>>>>>> that additional streams don't add to that state. The main cost, as I see it,
>>>>>> is the decompressed headers. I said potentially since that basically only
>>>>>> means the URL (unless there are other headers important for caching due to
>>>>>> Vary), and additional headers can come in the HEADERS frame. Also,
>>>>>> PUSH_PROMISE doesn't require allocating other state, like backend/DB
>>>>>> connections, if you only want to be able to handle (#MAX_CONCURRENT_STREAMs)
>>>>>> of those backend connections in parallel.
>>>>>>
>>>>>> If they're not specified, then we should specify it, but I've always
>>>>>> understood the header compression contexts to be directional and apply to
>>>>>> all frames sending headers in a direction. Therefore there should be two
>>>>>> compression contexts in a connection, one for header blocks being sent and
>>>>>> one for header blocks being received. If this is controversial, let's fork a
>>>>>> thread and discuss it.
>>>>>>
>>>>>>>
>>>>>>> >>
>>>>>>> >>
>>>>>>> >> > As far as the potential problem above, the root problem is that
>>>>>>> >> > when you
>>>>>>> >> > have limits you can have hangs. We see this all the time today
>>>>>>> >> > with browsers
>>>>>>> >> > (it's only reason people do domain sharding so they can bypass
>>>>>>> >> > limits). I'm
>>>>>>> >> > not sure I see the value of introducing the new proposed limits.
>>>>>>> >> > They don't
>>>>>>> >> > solve the hangs, and I don't think the granularity addresses any
>>>>>>> >> > of the
>>>>>>> >> > costs in a finer grained manner. I'd like to hear clarification
>>>>>>> >> > on what
>>>>>>> >> > costs the new proposed limits will address.
>>>>>>> >>
>>>>>>> >> I don't believe that the proposal improves the situation enough
>>>>>>> >> (or at
>>>>>>> >> all) to justify the additional complexity.  That's something that
>>>>>>> >> you
>>>>>>> >> need to assess for yourself.  This proposal provides more granular
>>>>>>> >> control, but it doesn't address the core problem, which is that
>>>>>>> >> you
>>>>>>> >> and I can only observe each other actions after some delay, which
>>>>>>> >> means that we can't coordinate those actions perfectly.  Nor can
>>>>>>> >> be
>>>>>>> >> build a perfect model of the other upon which to observe and act
>>>>>>> >> upon.
>>>>>>> >>  The usual protocol issue.
>>>>>>> >
>>>>>>> >
>>>>>>> > OK then. My proposal is to add a new limit for PUSH_PROMISE frames
>>>>>>> > though, separately from the MAX_CONCURRENT_STREAMS limit, since PUSH_PROMISE
>>>>>>> > exists as a promise to create a stream, explicitly so we don't have to count
>>>>>>> > it toward the existing MAX_CONCURRENT_STREAMS limit (I searched the spec and
>>>>>>> > this seems to be inadequately specced). Roberto and I discussed that before
>>>>>>> > and may have written an email somewhere in spdy-dev@, but I don't think
>>>>>>> > we've ever raised it here.
>>>>>>> >
>>>>>>>
>>>>>>> Well,  there is an issue tracking it in the github repo now, at
>>>>>>> least.  As currently defined in the spec,  it definitely needs to be
>>>>>>> addressed.
>>>>>>
>>>>>> Great. You guys are way better than I am about tracking all known
>>>>>> issues. I just have it mapped fuzzily in my head :)
>>>>>
>>>>>
>>>
>>
>

---

From: William Chan (陈智昌) <[email-masked]>
Date: Wed, 1 May 2013 14:46:35 -0300

The only benefit to that is supporting non-HTTP/2 application layering
semantics, which is intended not to change from HTTP/1.X. So there's
currently no use to allow the server to initiate streams with the
client=>server direction open.

I consider the current trend of our discussions to tend towards eliminating
complexity and targeting for HTTP/2 application layering semantics. I think
if we have another use case come up that would require supporting server
initiated bidirectional streams, I think at that point it'd be worthwhile
to revisit how we do this.

I'd like to hear from others if they disagree with my assessment of how
most people feel so far. FWIW, I personally would like us to support server
initiated bidirectional streams.

On Wed, May 1, 2013 at 2:26 PM, James M Snell <[email-masked]> wrote:

> >>> on (
> http://lists.w3.org/Archives/Public/ietf-http-wg/2013JanMar/1106.html).

---

From: Roberto Peon <[email-masked]>
Date: Wed, 1 May 2013 12:55:44 -0700

I still want to be able to support the WS API over HTTP/2. It would be
tragic to have N+1 connections instead of 1 when 1 works better anyway...

-=R

On Wed, May 1, 2013 at 10:46 AM, William Chan (陈智昌)
<[email-masked]>wrote:

> I consider the current trend of our discussions to tend towards
> eliminating complexity and targeting for HTTP/2 application layering
> semantics. I think if we have another use case come up that would require
> supporting server initiated bidirectional streams, I think at that point
> it'd be worthwhile to revisit how we do this.
>

---

From: William Chan (陈智昌) <[email-masked]>
Date: Wed, 1 May 2013 16:58:44 -0300

I agree with that, although there's no current WebSocket API use case for
server initiated bidirectional streams. I could imagine it in the future
though.

On Wed, May 1, 2013 at 4:55 PM, Roberto Peon <[email-masked]> wrote:

> On Wed, May 1, 2013 at 10:46 AM, William Chan (陈智昌) <[email-masked]
> > wrote:
>
>>> >>>>>> On Mon, Apr 29, 2013 at 3:46 PM, James M Snell <[email-masked]
>>> >
>>> >>>>>> wrote:
>>> >>>>>>>
>>> >>>>>>>

---

From: James M Snell <[email-masked]>
Date: Wed, 1 May 2013 13:00:31 -0700

> client=>server direction open.

"currently no use" is not a very good reason to not allow for it. We
(a) have the flag bits available and (b) can easily say that in the
http/2 semantic layer, the unidirectional bit must always be set. That
meets our current needs and leaves the door open for later.

- James

> initiated bidirectional streams, I think at that point it'd be worthwhile to
> revisit how we do this.
>
> I'd like to hear from others if they disagree with my assessment of how most
> people feel so far. FWIW, I personally would like us to support server
>> >>> on
>> >>> (http://lists.w3.org/Archives/Public/ietf-http-wg/2013JanMar/1106.html).

---

From: William Chan (陈智昌) <[email-masked]>
Date: Wed, 1 May 2013 17:15:08 -0300

Well, it would have been great then if people supported me when I was the
only one who stood up for this standpoint, but we went ahead and removed
UNIDIRECTIONAL for being redundant anyways:
http://lists.w3.org/Archives/Public/ietf-http-wg/2013JanMar/1055.html :P

I'm with you, but we should fork this thread to have that discussion on
what degree of flexibility we want in the layers below the application
semantic layer. UNIDIRECTIONAL was removed for being redundant, conditioned
on HTTP semantic usage. If we can reverse that decision, we can reopen
these other discussions where simplifications were made due to assuming on
HTTP semantic usage.

On Wed, May 1, 2013 at 5:00 PM, James M Snell <[email-masked]> wrote:

> eliminating
> most
> >> >> or provide some other information which identifies the IANA port
> number
> >> >> for
> >> >>> (
> >> >>> initiated streams might be nice. But in absence of any such
> protocol,
> >> >>> this
> >> >>>>>>> > PUSH_PROMISE as potentially expensive as a HEADERS frame,
> since
> >> >>>>>>> > it does more
> >> >>>>>>> > than just simple stream id allocation. I guess it's not
> really a
> >> >>>>>>> > huge issue,
> >> >>>>>>> > since if it's used correctly (in the matter you described),
> then
> >> >>>>>>> > it
> >> >>>>>>> > shouldn't be too expensive. If clients attempt to abuse it,
> then
> >> >>>>>>> > servers
> >> >>>>>>> of time. We do not yet know exactly when that compression
> context
> >> >>>>>>> can be let
> >> >>>>>> that additional streams don't add to that state. The main cost,
> as
> >> >>>>>> I see it,
> >> >>>>>> means the URL (unless there are other headers important for
> caching
> >> >>>>>> due to
> >> >>>>>>> >> > solve the hangs, and I don't think the granularity
> addresses
> >> >>>>>>> >> > any
> >> >>>>>>> >> > of the
> >> >>>>>>> > though, separately from the MAX_CONCURRENT_STREAMS limit,
> since
> >> >>>>>>> > PUSH_PROMISE
> >> >>>>>>> > it toward the existing MAX_CONCURRENT_STREAMS limit (I
> searched
> >> >>>>>>> > the spec and
> >> >>>>>>> > this seems to be inadequately specced). Roberto and I
> discussed
> >> >>>>>>> > that before
> >> >>>>>>> least.  As currently defined in the spec,  it definitely needs
> to
> >> >>>>>>> be
> >> >>>>>>> addressed.
> >> >>>>>>

---

From: James M Snell <[email-masked]>
Date: Thu, 2 May 2013 10:14:17 -0700

My apologies for missing that previous conversation. In my view,
allowing for the additional flag has only a minimal cost with
potentially significant benefit down the road. I see absolutely no
reason to rule it out. Forbidding it because it seems "less
complicated that way" is short sighted and, frankly, just rather
silly. However, I fully recognize that, currently, the HTTP/2 use case
does not require fully bidirectional pushed streams and that http/2
implementors may not want to have to do anything to deal with it. So
with that in mind, how about this...

Rather than make it a UNIDIRECTIONAL bit (on PUSH_PROMISE) that has to
be set in order for the stream to be viewed as being Unidirectional,
let's switch it and make it a BIDIRECTIONAL flag. If the flag is not
set (the default), then the stream is unidirectional and automatically
half-closed in the return direction. If the bit is set, the pushed
stream is bidirectional and would need to be half-closed by the
receiving side. Then, at the HTTP/2 semantics layer, we would simply
say that, for HTTP push streams, the bit always has to be switched
off. This would mean that any other subprotocol could come along later
and make use of the bit but http/2 only implementations would only be
required to contain minimal support. If an implementation that does
not support bidirectional pushed streams receives a PUSH_PROMISE with
the bidirectional flag set, it would respond with a RST_STREAM Cancel
or an empty data frame with the FINAL bit set. Simple, elegant and
flexible.

Also, this would address the original concern I had about push-streams
being left in the half-open state

- James

On Wed, May 1, 2013 at 1:15 PM, William Chan (陈智昌)
<[email-masked]> wrote:
> I'm with you, but we should fork this thread to have that discussion on what
> degree of flexibility we want in the layers below the application semantic
> layer. UNIDIRECTIONAL was removed for being redundant, conditioned on HTTP
> semantic usage. If we can reverse that decision, we can reopen these other
> discussions where simplifications were made due to assuming on HTTP semantic
> usage.
>
>
>> >> >>>>> there is no limit to the size of a webpage, or the number of
>> >> >>>>> links
>> >> >>>>> upon it.
>> >> >>>>> unknown reason being stupid and attempting to DoS the browser
>> >> >>>>> with
>> >> >>>>> many
>> >> >>>>> resources that it will never use, then the client sends
>> >> >>>>> RST_STREAM
>> >> >>>>> for the
>> >> >>>>>>> > Oops, forgot about that. See, the issue with that is now
>> >> >>>>>>> > we've
>> >> >>>>>>> > made
>> >> >>>>>>> as the originating stream? The answers might be obvious but
>> >> >>>>>>> they
>> >> >>>>>>> haven't yet
>> >> >>>>>>> been written down.
>> >> >>>>>>
>> >> >>>>>>
>> >> >>>>>> Compression contexts are a fixed state on a per-connection
>> >> >>>>>> basis,
>> >> >>>>>> meaning
>> >> >>>>>> all frames sending headers in a direction. Therefore there
>> >> >>>>>> should
>> >> >>>>>> be two
>> >> >>>>>> compression contexts in a connection, one for header blocks
>> >> >>>>>> being
>> >> >>>>>> sent and
>> >> >>>>>>> > exists as a promise to create a stream, explicitly so we
>> >> >>>>>>> > don't
>> >> >>>>>>> > have to count

---

From: Martin Thomson <[email-masked]>
Date: Thu, 2 May 2013 10:22:19 -0700

I think that this is going in the wrong direction.

If you consider that streams are not the owners of state, but each
direction is the owner of state, then this whole issue disappears.
For a push, the client->server direction never opens*.  Simple.  No
flags.

---

From: James M Snell <[email-masked]>
Date: Thu, 2 May 2013 10:41:01 -0700

That works too. If folks are agreement on that particular approach I
can work up the spec changes necessary to describe stream state in
those terms.

On Thu, May 2, 2013 at 10:22 AM, Martin Thomson
<[email-masked]> wrote:
>[snip[

---

From: Martin Thomson <[email-masked]>
Date: Fri, 3 May 2013 09:52:09 -0700

On 3 May 2013 09:44, William Chan (陈智昌) <[email-masked]> wrote:
> I'd like server folks to chime in, but doing this makes me feel a bit
> nervous. I feel this effectively disables the directional concurrent streams
> limit. The bidirectional full-close essentially acts like an ACK, so
> removing it might result in an unbounded number of streams.

I think that I know what you mean here, but can you try to expand a
little?  Do you refer to the possible gap between close on the
initiating direction and the first frame on the responding direction;
a gap that might cause the stream to escape accounting?  I think that
is a tractable problem - any unbounded-ness is under the control of
the initiating peer.

---

From: William Chan (陈智昌) <[email-masked]>
Date: Fri, 3 May 2013 13:44:46 -0300

I'd like server folks to chime in, but doing this makes me feel a bit
nervous. I feel this effectively disables the directional concurrent
streams limit. The bidirectional full-close essentially acts like an ACK,
so removing it might result in an unbounded number of streams.

---

From: William Chan (陈智昌) <[email-masked]>
Date: Fri, 3 May 2013 14:34:09 -0300

As I understand the proposal, which I believe ties into the issue James
raised at the beginning here, the goal is to be able to open and close a
directional stream without an ACK, which I am nervous about as I said above
without much detail. Concretely speaking, a HTTP GET is a HEADERS+PRIORITY
frame with a FINAL flag or an extra DATA frame with FINAL flag. This means
that the request effectively never gets counted against the directional
stream limit, as controlled by the receiver which sends a
MAX_CONCURRENT_STREAMS setting, since it open and closes the direction in
the same frame (or closes in the subsequent empty DATA frame).

On Fri, May 3, 2013 at 1:52 PM, Martin Thomson <[email-masked]>wrote:

> streams

---

From: James M Snell <[email-masked]>
Date: Fri, 3 May 2013 10:35:34 -0700

> is a tractable problem - any unbounded-ness is under the control of

Well, there are really several issues here...

1. The server endpoint would be allowed to initiate an unbounded
number of push streams, up to MAX_CONCURRENT_STREAMS at any given
time, but with no reasonable upper limit on the overall total number
of streams. For instance, given a MAX_CONCURRENT_STREAMS of 10, it
could initiate 10 streams and close them, initiate 10 more and close
those, initiate 10 more.. ad infinitum. Generally speaking, I don't
believe this is really a serious issue, however. The receiving end can
simply opt to reject pushed streams from misbehaving servers once a
particular threshold was reached (which is where I was going with the
credit limit proposal before).

2. There may be (as you suggest) a gap of time between when the server
sends it's FINAL frame on a pushed stream and the time when a client
might send it's FINAL (if any FINAL frame comes at all). Here, again,
however, the MAX_CONCURRENT_STREAMS bounds the total number of open
streams the server can push at any given time and the client can opt
to reject additional streams if the flow becomes unmanageable.

Modeling this out, I believe that clients will definitely have to
implement some protections against abuse but those protections do not
need to be standardized. Clients would simply implement whatever
algorithm they wish to determine whether or not to accept new pushed
streams. MAX_CONCURRENT_STREAMS would cover only streams that are
initiated by and opened for the sending peer. If a peer closes it's
side, it decrements the counter for that peer. Half-open streams in
the other direction would not count. A receiving peer that is dealing
with a misbehaving endpoint can use RST_STREAM to refuse new pushes or
send a SETTINGS with a new, more restrictive MAX_CONCURRENT_STREAMS
value.

Btw... Another possible dimension we could consider is a new
RST_STREAM error code indicating that a stream is being rejected
because too many streams are being opened too quickly.

- James

---

From: James M Snell <[email-masked]>
Date: Fri, 3 May 2013 11:02:40 -0700

The impact on client-to-server initiated streams is another reason why
I suggested the credit-based approach and why it would likely be good
to have an RST_STREAM "ENHANCE YOUR CALM" error code [1]. If the
client misbehaves and sends too much too quickly, we have flow
control, settings, rst_stream and goaway options to deal with it.

[1] http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Server_Error

---

From: William Chan (陈智昌) <[email-masked]>
Date: Fri, 3 May 2013 15:28:48 -0300

I guess I kinda think that we're worrying too much about this corner of the
spec. I don't view it as a big deal in practice. The problem described
happens when MAX_CONCURRENT_STREAMS is too low to allow enough parallelism
per roundtrip. I would advise people to simply increase their
MAX_CONCURRENT_STREAMS in that case. I kinda think this is only problematic
when we have very high latencies and devices that can't handle high
parallelism, like an interplanetary refrigerator that speaks HTTP/2 for
some reason. <shrug>

I am unsure how to feel about a ENHANCE YOUR CALM code as it's not well
defined. I don't mind RST_STREAMs on exceeding limits, like the initial
MAX_CONCURRENT_STREAMS, since they're usually the result of a race (the
possible initial SETTINGS frame race) and we won't have to keep continually
sending RST_STREAMs to rate limit appropriately.

On Fri, May 3, 2013 at 3:02 PM, James M Snell <[email-masked]> wrote:

> [1]
> http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Server_Error
>
> > On Fri, May 3, 2013 at 1:52 PM, Martin Thomson <[email-masked]
> >
> > wrote:
> >>

---

From: Roberto Peon <[email-masked]>
Date: Fri, 3 May 2013 14:20:09 -0700

The biggest rub in Martin's suggestion is that, as a stream initiator, I no
longer know for how long I should keep the original "request" headers
around.
I view that as an annoying problem (I want every response to be
attributable to a request).

I also think it is a bit confusing-- how would it be used in cases where
I've sent all my data on what I thought was a unidirectional stream, and
then receive bytes from the other side on that stream. That'd be... weird.

With the unidirectional bit (or similar declaration of half-closed
start-state), I now know (by fiat, essentially) that I will not receive a
response on that stream ID, and so I don't need to keep the "request"
headers around after I've finished pushing the stream. Logging accomplished.

I think this is an easy issue to solve by reinstating the unidirectional
bit (for now). It is certainly minimal work to have servers which do server
push set that bit.

To Will's point, I agree that an "ENHANCE YOUR CALM" code seems redundant.
In my case I believe it redundant because the remote side has already
received my settings frame, or is sending without having known it (i.e.
within the initial RTT), and will be receiving the SETTINGS frame before it
could process this new code anyway (assuming I'm following spec and sending
SETTINGS immediately upon session establishment).
-=R

On Fri, May 3, 2013 at 11:28 AM, William Chan (陈智昌)
<[email-masked]>wrote:

> I guess I kinda think that we're worrying too much about this corner of
> the spec. I don't view it as a big deal in practice. The problem described
>> > On Fri, May 3, 2013 at 1:52 PM, Martin Thomson <
>> [email-masked]>
>> > wrote:
>> >>

---

From: William Chan (陈智昌) <[email-masked]>
Date: Fri, 3 May 2013 18:35:05 -0300

On Fri, May 3, 2013 at 6:20 PM, Roberto Peon <[email-masked]> wrote:

> The biggest rub in Martin's suggestion is that, as a stream initiator, I
> no longer know for how long I should keep the original "request" headers
> around.

I spent time thinking about whether or not this was a problem. I certainly
found it annoying, but it might be change aversion from SPDY. My mental
reduction of this suggestion was the coupling of directions of the stream
was moving out of the streaming layer to the application semantics layer.
At the HTTP semantic layer, where you want a response for every request,
the client can open a client=>server direction stream at the streaming
layer and register a callback for the server=>client direction stream at
the streaming layer. The client keeps the original request headers as long
as it's expecting the response stream.

I think there's a certain aesthetic layering cleanliness to Martin's
suggestion in this way by moving the coupling out from a lower layer to a
higher layer. I am still mulling it over. I also haven't thought through
the server case, so I might have missed how this is annoying for them.

> then receive bytes from the other side on that stream. That'd be... weird.

That'd be wrong at the semantic layer and should probably trigger an error.

> bit (for now). It is certainly minimal work to have servers which do server

Agreed.

> On Fri, May 3, 2013 at 11:28 AM, William Chan (陈智昌) <[email-masked]
> > wrote:
>

---

From: Roberto Peon <[email-masked]>
Date: Fri, 3 May 2013 14:43:54 -0700

responses inline

> the server case, so I might have missed how this is annoying for them.

No argument there. Except for the issue with the lifetime of headers, it is
a very pretty design (the only thing you end up having to change is the
strict requirement on stream ID mentions being in monotonic order, which
we're loosening anyway for push). As a matter of fact this was one of the
ways we had discussed for SPDY/1 :)

In general any server/proxy/loadbalancer will wish to keep headers about
for the shortest time period possible as they typically represent either
the largest or a very large chunk of the state the server keeps resident.
It is possible to do fancy/complicated things to reduce this burden.. but
they're fancy/complicated :)

-=R

---

From: James M Snell <[email-masked]>
Date: Fri, 3 May 2013 18:19:28 -0700

Ok.. going back over the thread in detail and over the spec again, one
approach to addressing the overall concern here (and hopefully bring a
bit more rigor to the overall design) is to redefine the stream states
slightly along the same lines already suggested by Martin. Each
endpoint would maintain its own view of the current activity state of
every stream in a session, however, the state would only reflect the
actions taken by the peer endpoint. There are five possible activity
states:

Unused
  The peer endpoint has not reserved or used the stream in any way.

Open
  The endpoint has received frames on the stream from the peer, none
of which are type RST_STREAM or have the FINAL flag set.

Closed
  The endpoint has received an RST_STREAM frame, or any frame with the
FINAL flag set from the peer

Reserved-Open
  The peer has reserved the stream identifier for future use but
frames have not yet been received on that stream. The receiving
endpoint is expected to send its own frames on the same stream.

Reserved-Closed
  The peer has reserved the stream identifier for future use but
frames have not yet been received on that stream. The receiving
endpoint is not expected to send its own frames on the same stream.

MAX_CONCURRENT_STREAMS == The number of streams in the Open state the
endpoint will permit the peer to initiate at any given time. Once that
limit is reached, the receiving endpoint will likely begin rejecting
new streams using RST_STREAM. In other words, right now,
MAX_CONCURRENT_STREAMS is defined in terms of what the sending
endpoint must not do. This changes the definition to an indication of
what the receiving endpoint will do once a particular threshold is
reached. Any endpoint that wants to be able to keep creating streams
must be diligent about sending FINAL frames, etc.

As for the Request-Response bounding issue, that's really an HTTP
semantic layer notion. I'm not fully convinced we really need to
handle that issue in the framing layer at all.

On Fri, May 3, 2013 at 2:20 PM, Roberto Peon <[email-masked]> wrote:
> I view that as an annoying problem (I want every response to be attributable
> to a request).
>
> I think this is an easy issue to solve by reinstating the unidirectional bit
> (for now). It is certainly minimal work to have servers which do server push
> set that bit.
>
> On Fri, May 3, 2013 at 11:28 AM, William Chan (陈智昌) <[email-masked]>
> wrote:
>>
>> parallelism, like an interplanetary refrigerator that speaks HTTP/2 for some
>> reason. <shrug>
>>
>>> > On Fri, May 3, 2013 at 1:52 PM, Martin Thomson

---

From: Roberto Peon <[email-masked]>
Date: Fri, 3 May 2013 18:49:57 -0700

Hey, you're the one worried about the size of the compressor state (which
would be ~4 or 8k)! :)
Headers are sometimes larger individually, and the upper limit to the size
of this state is the sum of 10s to 100s of these.

I think that, pragmatically, since there is a framing-layer solution which
ensures that one must not store headers for longer that necessary, and
which is semantic-layer agnostic, it is a decent bet.
Any approach other than declaring it as unidirectional (or equivalent)
either requires the caching of much state, or a NACK from the remote side.

That isn't to say that Martin's approach doesn't have appeal. I want to
like it, but unless we are to have unlimited streams in some limbo state,
it would require a NACK, else I won't be able to correlate sets of headers
on a stream and the NACK both requires more machinery at both ends, and
consumes more bytes on the wire.
-=R

On Fri, May 3, 2013 at 6:19 PM, James M Snell <[email-masked]> wrote:

> no
> bit
> some
> >>> > raised at the beginning here, the goal is to be able to open and
> close
> >>> > a
> >>> > MAX_CONCURRENT_STREAMS setting, since it open and closes the
> direction
> >>> > in

---

From: William Chan (陈智昌) <[email-masked]>
Date: Fri, 3 May 2013 22:59:38 -0300

I think there's some missing context not being stated in the emails which
is leading to confusion. Can we revisit why decoupling the stream
directions will lead to a longer header lifetime? I would think that when
the streaming layer encounters headers for a stream, it would hand it up to
the HTTP semantic layer. As long as the HTTP semantic layer understands the
request-response coupling, it knows to keep the request headers around
until the response arrives, right?

On Fri, May 3, 2013 at 10:49 PM, Roberto Peon <[email-masked]> wrote:

>> >>> > directional stream without an ACK, which I am nervous about as I
>> said
>> >>> > above

---

From: James M Snell <[email-masked]>
Date: Fri, 3 May 2013 19:41:46 -0700

I suspect that it is largely going to depend largely on the
compression mechanism we ultimately choose.

A client opens a new session with the server and sends it's first
HEADERS+PRIORITIES frame. The server will initialize the decompression
context for the headers, storing some bit of data. A while later, the
client closes the stream. Is the server required to maintain that
compression state for an indefinite period of time through the life of
the session or is there some reliable way of dumping the state because
you know it will no longer be used? The server can't really know if
the client is done sending requests using the same compression
context. The client could end up waiting quite a while before sending
a new HEADERS+PRIORITIES frame that uses the exact same compression
state. AFAICT none of the proposals on the table for header
compression really answer this question effectively.

If streams are allowed to linger in an indefinitely long half-open
state, then it becomes much more difficult to answer this question.
Consider.. The states I suggest are all take from the receiving
endpoints point of view... A stream in the Open, Reserved-Open, and
Reserved-Closed states means that the endpoint can expect to receive
new headers at some point; once the state changes to Closed, no new
headers on that stream are possible. That doesn't mean we can dump the
stored headers yet tho. Currently, we have absolutely no mechanism
with which to determine when it's ok to dump those stored headers.

On Fri, May 3, 2013 at 6:59 PM, William Chan (陈智昌)
<[email-masked]> wrote:
> I think there's some missing context not being stated in the emails which is
> leading to confusion. Can we revisit why decoupling the stream directions
> will lead to a longer header lifetime? I would think that when the streaming
> layer encounters headers for a stream, it would hand it up to the HTTP
> semantic layer. As long as the HTTP semantic layer understands the
> request-response coupling, it knows to keep the request headers around until
> the response arrives, right?
>
>
>> ensures that one must not store headers for longer that necessary, and which
>> is semantic-layer agnostic, it is a decent bet.
>> like it, but unless we are to have unlimited streams in some limbo state, it
>> would require a NACK, else I won't be able to correlate sets of headers on a
>> stream and the NACK both requires more machinery at both ends, and consumes
>> more bytes on the wire.
>> -=R
>>
>>
>>
>>> >>> > frame with a FINAL flag or an extra DATA frame with FINAL flag.
>>> >>> > This
>>> >>> > means
>>> >>> >> > I'd like server folks to chime in, but doing this makes me feel
>>> >>> >> > a
>>> >>> >> > bit

---

From: Martin Thomson <[email-masked]>
Date: Sat, 4 May 2013 12:15:01 -0700

> ways we had discussed for SPDY/1 :)

I don't see headers being a property of the streaming or framing
layers, so I never really considered this to be a problem.  The fact
that they live beyond the lifetime of a single stream direction
doesn't mean that you need to change how you write your code.

To give another case, headers already need to live beyond the lifetime
of both directions of a stream when that stream triggers a push.

> In general any server/proxy/loadbalancer will wish to keep headers about for
> the shortest time period possible as they typically represent either the
> largest or a very large chunk of the state the server keeps resident. It is
> possible to do fancy/complicated things to reduce this burden.. but they're
> fancy/complicated :)

Wasn't that always true though?  I can't imagine that a high
performance server would be holding strings for most headers.  The
only cases that might be needed for are cookies (as always).