Subject: #38 - HTTP2 min value for server supported max_concurrent_streams

From: Osama Mazahir <[email-masked]>
Date: Fri, 22 Feb 2013 05:00:46 +0000

Hi,

Issue #38 [1] tracks:
	(1) What is the minimum number of concurrent client-initiated streams that a server must be able to support?
	(2) What is the default value?

We have a race condition where the client can initiate more streams to the server before the server can advertise its stream accept limit to the client.  I suggest making the default the same as the minimum so that we can prevent that race.  And tighten the wording on what is legal.  For example, something like:

A server MUST be able to handle at least 8 concurrent streams initiated by the client. A server MUST NOT advertise a value less than 8. A client MUST generate a session error if it receives a value less than 8 from the server. The default value server limit is 8.

Thanks,
--Osama.

[1] https://github.com/http2/http2-spec/issues/38

---

From: Yoav Nir <[email-masked]>
Date: Fri, 22 Feb 2013 09:47:08 +0000

Hi Osama

Wouldn't 1 be a better initial value?  This would allow minimal implementations with no concurrency at all. It's usually the case for browser that the first round-trip contains exactly one request (for the HTML file), and only when the response is received do more requests come out. So this wouldn't slow us down much.

Yoav

---

From: Roberto Peon <[email-masked]>
Date: Fri, 22 Feb 2013 05:18:15 -0800

Why 1?
On mobile devices you often end up reconnecting, and waiting yet and extra
RT to continue your session (likely using multiple streams) would add
latency. :/

With HTTP/2 (as inherited from SPDY), the server has a mechanism to tell
the client that it didn't process a stream at all, so the race is already
handled.
If a client sends 8 concurrent streams, and the server only wants to handle
6 (say, because it is particularly resource limited right now), then it
will reset two of the streams with an error code that essentially says
try-again-later, and it can do so after sending the SETTINGS frame.

-=R

On Fri, Feb 22, 2013 at 1:47 AM, Yoav Nir <[email-masked]> wrote:

> Hi Osama
>
> Wouldn't 1 be a better initial value?  This would allow minimal
> implementations with no concurrency at all. It's usually the case for
> browser that the first round-trip contains exactly one request (for the
> HTML file), and only when the response is received do more requests come
> out. So this wouldn't slow us down much.
>
> Yoav
>
> >       (1) What is the minimum number of concurrent client-initiated
> streams that a server must be able to support?
> > We have a race condition where the client can initiate more streams to
> the server before the server can advertise its stream accept limit to the
> client.  I suggest making the default the same as the minimum so that we
> can prevent that race.  And tighten the wording on what is legal.  For
> example, something like:
> >
> > A server MUST be able to handle at least 8 concurrent streams initiated
> by the client. A server MUST NOT advertise a value less than 8. A client
> MUST generate a session error if it receives a value less than 8 from the
> server. The default value server limit is 8.
> >
> > Thanks,
> > --Osama.
> >

---

From: Martin Thomson <[email-masked]>
Date: Fri, 22 Feb 2013 08:16:53 -0800

> Why 1?

1 seems a little restrictive, especially since 6 concurrent
connections is the current expectation in many browsers.

> 6 (say, because it is particularly resource limited right now), then it will
> reset two of the streams with an error code that essentially says

This is not isomorphic with a lower stream limit simply because the
client will have sent some amount of data for the 2 rejected streams,
expending some of what is a fairly limited resource (INIT CWD being as
it is).  That's probably OK if you consider that it's the client
prerogative and responsibility.

---

From: Roberto Peon <[email-masked]>
Date: Fri, 22 Feb 2013 08:26:56 -0800

On Fri, Feb 22, 2013 at 8:16 AM, Martin Thomson <[email-masked]>wrote:

> will

Well, we get to waste resources either way unless the default MAX_STREAMS
is already optimal (fat chance)-- one way wastes bandwidth by pushing at
most 1 BDP worth of bytes on the wire, and the other has the connection
idle for a BDP or more. :/

I'm guessing that the latter ends up dominating thanks to short NAT
timeouts, etc, when a user comes back to the browser after some period of
being idle. Also, assuming we do keep persisted SETTINGS, then the impact
of opening up too many streams becomes even more limited as it happens far
more rarely.

-=R

---

From: Yoav Nir <[email-masked]>
Date: Fri, 22 Feb 2013 22:19:50 +0000

> connections is the current expectation in many browsers.

Defaulting to 1 allows for a simple server that never has to handle multiple concurrent streams, one that can be implemented with much fewer lines of code, but is still compliant. Great for serving software updates, large files, CRLs, etc. Not so great for web pages.

Other servers will quickly raise the limit via a SETTINGS frame.

Yoav

---

From: William Chan (陈智昌) <[email-masked]>
Date: Fri, 22 Feb 2013 14:36:04 -0800

We always have to examine what the choices end up being for which parties.
If servers end up limiting parallelism, or requiring roundtrips to ramp up
parallelism, then clients which want speed (browsers) will be incentivized
to simply open up more connections to bypass the low parallelism limit or
slow start.

Overall, I think it's better to tolerate the minor suboptimality of having
servers RST_STREAM streams if they don't want so much parallelism, rather
than incentivize browsers to open more connections.

On Fri, Feb 22, 2013 at 2:19 PM, Yoav Nir <[email-masked]> wrote:

> Defaulting to 1 allows for a simple server that never has to handle
> multiple concurrent streams, one that can be implemented with much fewer
> lines of code, but is still compliant. Great for serving software updates,
> large files, CRLs, etc. Not so great for web pages.
>

---

From: Osama Mazahir <[email-masked]>
Date: Fri, 22 Feb 2013 23:45:17 +0000

As Martin said, 1 seems overly restrictive.

My major concern is not the value of the number, but that we have a minimum value and the default be the same as the minimum.  Otherwise, we leave the race hole open then we are just increasing complexity.

1.       Client will have to track negative allowance (because it did not know how many requests it allowed to send)

2.       Server has to promise that RST_STREAM due to max_concurrent_stream overflow did not have any side effects

o   The server should verb agnostic (i.e. GET vs POST) and just look at some streamCount variable.

o   Otherwise, client will have to pend all non-idempotent requests until it gets the SETTINGS frame from the server

3.       Client will have to resubmit the request into its queue to be sent when the allowance opens up

4.       If the “blind” request(s) (i.e. sent before client received the SETTINGS frame) have entity-body then client

o   Must wait until the server’s SETTINGS frame before sending entity-body OR

o   Be able to regenerate the entity-body when the “blind” request is RST_STREAMed

§  This means the layer on top of client stack needs to be able to handle a “retry” error and resubmit the entity-body OR

§  The client stack buffers all the entity-body, as it converts it into DATA frames, until it knows that the request won’t get RST_STREAM due to max_concurrent_stream

o   Or just blow up and complain to the user

In general, I would prefer if we made HTTP/2.0 to not have such races to begin with instead of piling on complexity to react to the races.

From: [email-masked] [mailto:[email-masked]] On Behalf Of William Chan (???)
Sent: Friday, February 22, 2013 2:36 PM
To: Yoav Nir
Cc: Martin Thomson; Roberto Peon; Osama Mazahir; [email-masked] Group
Subject: Re: #38 - HTTP2 min value for server supported max_concurrent_streams

We always have to examine what the choices end up being for which parties. If servers end up limiting parallelism, or requiring roundtrips to ramp up parallelism, then clients which want speed (browsers) will be incentivized to simply open up more connections to bypass the low parallelism limit or slow start.

Overall, I think it's better to tolerate the minor suboptimality of having servers RST_STREAM streams if they don't want so much parallelism, rather than incentivize browsers to open more connections.

On Fri, Feb 22, 2013 at 2:19 PM, Yoav Nir <[email-masked]<mailto:[email-masked]>> wrote:

On Feb 22, 2013, at 6:16 PM, Martin Thomson <[email-masked]<mailto:[email-masked]>> wrote:

> On 22 February 2013 05:18, Roberto Peon <[email-masked]<mailto:[email-masked]>> wrote:
>> Why 1?
>

Yoav

---

From: William Chan (陈智昌) <[email-masked]>
Date: Fri, 22 Feb 2013 15:56:30 -0800

On Fri, Feb 22, 2013 at 3:45 PM, Osama Mazahir <[email-masked]> wrote:

>  ** **
>
> As Martin said, 1 seems overly restrictive.  ****
>
> ** **
>
> My major concern is not the value of the number, but that we have a
> minimum value and the default be the same as the minimum.  Otherwise, we
> leave the race hole open then we are just increasing complexity.
>

Do you feel like the complexity is that bad? In my experience, from
implementing SPDY, it is not.

> ****
>
> **1.       **Client will have to track negative allowance (because it did
> not know how many requests it allowed to send)
>

Isn't this easy? The client always has to track how many outstanding
streams it has in order to respect the limit.

> ****
>
> **2.       **Server has to promise that RST_STREAM due to
> max_concurrent_stream overflow did not have any side effects****
>
> **o   **The server should verb agnostic (i.e. GET vs POST) and just look
> at some streamCount variable.****
>
> **o   **Otherwise, client will have to pend all non-idempotent requests
> until it gets the SETTINGS frame from the server
>

Since RST_STREAM has an error code, this is easy to define.

> ****
>
> **3.       **Client will have to resubmit the request into its queue to
> be sent when the allowance opens up
>
Clients already have to know how to do this due to the GOAWAY race. They
also have to handle this in HTTP/1.X today. For example, if we get an error
when reusing a persistent HTTP connection (e.g. TCP RST), we will resend
the HTTP request over a new connection.

> ****
>
> **4.       **If the “blind” request(s) (i.e. sent before client received
> the SETTINGS frame) have entity-body then client****
>
> **o   **Must wait until the server’s SETTINGS frame before sending
> entity-body OR****
>
> **o   **Be able to regenerate the entity-body when the “blind” request is
> RST_STREAMed****
>
> **§  **This means the layer on top of client stack needs to be able to
> handle a “retry” error and resubmit the entity-body OR****
>
> **§  **The client stack buffers all the entity-body, as it converts it
> into DATA frames, until it knows that the request won’t get RST_STREAM due
> to max_concurrent_stream****
>
> **o   **Or just blow up and complain to the user
>

Again, clients already have to handle this.

> ****
>
> ** **
>
> In general, I would prefer if we made HTTP/2.0 to not have such races to
> begin with instead of piling on complexity to react to the races.
>

As someone with experience implementing a SPDY client, I do not believe
this is a big burden. If you believe it is, I would like to hear why.

> ****
>
> ** **
>
> ** **
>
> *From:* [email-masked] [mailto:[email-masked]] *On Behalf Of *William
> Chan (???)
> *Sent:* Friday, February 22, 2013 2:36 PM
> *To:* Yoav Nir
> *Cc:* Martin Thomson; Roberto Peon; Osama Mazahir; [email-masked]
> *Subject:* Re: #38 - HTTP2 min value for server supported
> max_concurrent_streams****
>
> ** **
>
> slow start.****
> ** **
> than incentivize browsers to open more connections.****
>
> ** **
>
> ** **
>
> ** **
>
> On Fri, Feb 22, 2013 at 2:19 PM, Yoav Nir <[email-masked]> wrote:****
>
>
> > connections is the current expectation in many browsers.****
>
> Yoav****
>  ** **

---

From: Patrick McManus <[email-masked]>
Date: Sat, 23 Feb 2013 15:49:15 -0500

8 is simply way too low for today's internet subresource count.. and
we can't rely on the main resource (html) round trip to establish the
setting as subresources are often served from a different host (e.g. a
cdn).

the guiding metric shouldn't be http/1 conns-per-hostname (6) but
rather the realistic conns-per-page we see with sharding. Which is
MUCH higher.

The default should be at least on the order of 100 (as I believe it
currently is), but honestly with the newer flow control measures in
place should perhaps be even higher to let the parallelism and
prioritization mechanisms work unobstructed.

---

From: Osama Mazahir <[email-masked]>
Date: Tue, 26 Feb 2013 08:34:32 +0000

Internet Explorer has similar gymnastics.  However, I don’t think that is just cause to reinvent the same problems again.

In general, the problem we have is that one side initiates operations without knowing the peer’s limits.  MaxConcurrentStreams is one example and negative flow control bytecounts is another (i.e. where the receiver is trying to advertise that it has small buffers but we shove data down its throat and dictate that it “MUST be prepared to receive the entire amount” [1]).

Possible solutions include:

1.       Handshake Advertise: Advertise limits as part of handshake/negotiation.  That way, upon session start each side knows the other’s limit and can guarantee that it won’t violate it.  That way, we can simplify all parts of the protocol that are dealing with limit-exceed cases.

2.       Defaults and minimums: In the spec we pick some defaults and minimums so that each endpoint starts at a known initial state and each endpoint can thus guarantee that it won’t violate the peer’s limits.  The initial SETTINGS frame can grow those limits.  Thus, we can simplify/delete all the limit-exceed handling.

3.       Don’t fix: Not really a “solution”.  We write pages of protocol text describing the races, how to workaround limit-exceed cases, code and test it, and put that burden on all future implementers.

In short, I ask the WG to not summarily dismiss this issue.  We should devote some energy to ensure that the protocol is robust by-design.

[1] http://http2.github.com/http2-spec/#rfc.section.3.7.9.3

From: [email-masked] [mailto:[email-masked]] On Behalf Of William Chan (???)
Sent: Friday, February 22, 2013 3:57 PM
To: Osama Mazahir
Cc: Yoav Nir; Martin Thomson; Roberto Peon; [email-masked] Group
Subject: Re: #38 - HTTP2 min value for server supported max_concurrent_streams

On Fri, Feb 22, 2013 at 3:45 PM, Osama Mazahir <[email-masked]<mailto:[email-masked]>> wrote:

Do you feel like the complexity is that bad? In my experience, from implementing SPDY, it is not.

Isn't this easy? The client always has to track how many outstanding streams it has in order to respect the limit.

3.       Client will have to resubmit the request into its queue to be sent when the allowance opens up
Clients already have to know how to do this due to the GOAWAY race. They also have to handle this in HTTP/1.X today. For example, if we get an error when reusing a persistent HTTP connection (e.g. TCP RST), we will resend the HTTP request over a new connection.

*  This means the layer on top of client stack needs to be able to handle a “retry” error and resubmit the entity-body OR

*  The client stack buffers all the entity-body, as it converts it into DATA frames, until it knows that the request won’t get RST_STREAM due to max_concurrent_stream

As someone with experience implementing a SPDY client, I do not believe this is a big burden. If you believe it is, I would like to hear why.

From: [email-masked]<mailto:[email-masked]> [mailto:[email-masked]<mailto:[email-masked]>] On Behalf Of William Chan (???)
Sent: Friday, February 22, 2013 2:36 PM
To: Yoav Nir
Cc: Martin Thomson; Roberto Peon; Osama Mazahir; [email-masked]<mailto:[email-masked]> Group
Subject: Re: #38 - HTTP2 min value for server supported max_concurrent_streams

Yoav

---

From: Eliot Lear <[email-masked]>
Date: Tue, 26 Feb 2013 10:08:51 +0100

Hi,

> prioritization mechanisms work unobstructed.

Osama asked two questions.  Let's separate them:

1.  What is the default value?

That is, if SETTINGS_MAX_CONCURRENT_STREAMS isn't set what value is
chosen?  Currently the default indicates no limit.  One approach is to
NOT have a default and require an advertised value at the beginning. 
That would lead to lock step behavior, however, which would add
latency.  Therefore, seems to me that 8 is actually a good number until
another number is received.  Except for one thing.  What to do about
tiny implementations?

2.  What is the minimum value?

That is, what are legal values for SETTINGS_MAX_CONCURRENT_STREAMS?

Is there a reason why a very simple implementation should NOT be able to
advertise a value of 1?

Eliot

---

From: Albert Lunde <[email-masked]>
Date: Tue, 26 Feb 2013 05:29:52 -0600

On 2/26/2013 2:34 AM, Osama Mazahir wrote:
> 1.Handshake Advertise: Advertise limits as part of 
> handshake/negotiation.  That way, upon session start each side knows the 
> other’s limit and can guarantee that it won’t violate it.  That way, we 
> can simplify all parts of the protocol that are dealing with 
> limit-exceed cases.

Would there be any merit to having a limited number of low-resource
profiles which would set several parameters? The chief virtue seems that
they might be specified in fewer bits.

I can't see this as a mechanism that scales up indefinitely unless the
profiles could be monotonically ordered, and the parameters given by
some sort of power or exponential function (this could be hard to tune).

So this idea is mainly an escape hatch for low-end clients or servers.

---

From: Patrick McManus <[email-masked]>
Date: Tue, 26 Feb 2013 08:58:02 -0500

On Tue, Feb 26, 2013 at 4:08 AM, Eliot Lear <[email-masked]> wrote:

>[..]
> chosen?  Currently the default indicates no limit.

The draft provides the advice that all implementations should support
100. That is decently sized to the problem being solved by a parallel
prioritized protocol. imo it should actually be bigger. 8 is crazy
small. It also provides a mechanism to reject streams greater than it
wants to support should you create a server incapable of doing this
(i.e. it can send a settings followed by the rsts)

The cost of setting it to 8 means you force queuing in the very common
"fetch 84 subresources from this CDN" case. Honestly, that case is
where http-2 gets almost all of its performance benefit and addresses
a well known weakness with http/1. It's pipelining with mux and
prioritization done to eliminate client side queues - that's the key
to the whole shebang. We need to emphasize the parameters that make
that work well (i.e. parallelism, responsive frame sizes, well
structured priorities). Adding a round trip just to figure out that
the protocol designed for parallelism does a decent amount of
parallelism is counter productive.

-P

---

From: William Chan (陈智昌) <[email-masked]>
Date: Tue, 26 Feb 2013 08:26:59 -0800

Thank you for continuing to raise this issue. I definitely think this is
worth discussing. I've reflected a bit on what you and others have said. If
I understand you correctly, you are primarily concerned with the races
before limits can be negotiated, and would like to see them fixed. I've
pointed out that the races existed in HTTP/1.X and still exist with things
like GOAWAY. It sounds like you'd like to fix them. I'm OK with fixing them
as long as they do not impose performance costs due to extra roundtrips to
reach appropriate parallelism. I think we disagree on acceptable code
complexity. We've already implemented this logic in Chromium and believe it
not to be burdensome.

So, on that point, I think we may agree to disagree and see how the rest of
the working group feels.

But, can we fix the race without imposing performance costs? Let's examine
the cases:
(1) Upgrade, where, assuming successful negotiation, the server begins
HTTP/2 in response to the HTTP/1.1 request with the Upgrade header.
(2) HTTPS negotiation via TLS-NPN style mechanism. Client speaks HTTP/2
first.
(3) Out of band discovery like DNS, and the client starts speaking HTTP/2

In (1), the server speaks first and can send SETTINGS immediately. So
respecting server limits is not a concern. Respecting client limits is a
concern. I think this falls into your "handshake advertise" scenario. We
could add a HTTP header for the relevant SETTINGS during the Upgrade. This
way, the server can respect the client limits.

In (2), the client speaks first, and will only respect server default
limits, not server specified limits since the server hasn't had a chance to
send them yet. It's conceivable we could add settings into the NPN
handshake. I'm a bit concerned about stashing so much into that handshake,
since we've also previously discussed expressing capabilities in the
handshake (e.g: supporting WebSockets over HTTP/2). If we wanted to do
something like this, we probably would need to convey such a requirement to
the TLS WG. I'm hesitant.

In (3), since there is no negotiation, only discovery via DNS mechanisms,
we'd have to stash the settings in DNS as discussed previously, and
probably sign it too.

I'm open to discussing conveying SETTINGS via the negotiation/discovery
mechanisms we have available, in order to attempt to reduce complexity. If
we can reasonably prevent races via conveying SETTINGS sooner, then great.
But I still believe the defaults should be chosen so they do not impose
performance costs due to roundtrips to raise the limits from a low default.
As Patrick says, 8 is far too small. The default should be on the order of
100. It's very common to do large domain sharding to a CDN, and we should
make sure we can handle that case with a single connection, rather than
incentivizing web devs to continue to do domain sharding to achieve desired
parallelism.

On Tue, Feb 26, 2013 at 12:34 AM, Osama Mazahir <[email-masked]>wrote:

>  Internet Explorer has similar gymnastics.  However, I don’t think that
> is just cause to reinvent the same problems again.****
>
> ** **
>
> In general, the problem we have is that one side initiates operations
> without knowing the peer’s limits.  MaxConcurrentStreams is one example and
> negative flow control bytecounts is another (i.e. where the receiver is
> trying to advertise that it has small buffers but we shove data down its
> throat and dictate that it “MUST be prepared to receive the entire amount”
> [1]).****
>
> ** **
>
> Possible solutions include:****
>
> **1.       **Handshake Advertise: Advertise limits as part of
> other’s limit and can guarantee that it won’t violate it.  That way, we can
> simplify all parts of the protocol that are dealing with limit-exceed cases.
> ****
>
> **2.       **Defaults and minimums: In the spec we pick some defaults and
> minimums so that each endpoint starts at a known initial state and each
> endpoint can thus guarantee that it won’t violate the peer’s limits.  The
> initial SETTINGS frame can grow those limits.  Thus, we can simplify/delete
> all the limit-exceed handling.****
>
> **3.       **Don’t fix: Not really a “solution”.  We write pages of
> protocol text describing the races, how to workaround limit-exceed cases,
> code and test it, and put that burden on all future implementers.****
>
> ** **
>
> In short, I ask the WG to not summarily dismiss this issue.  We should
> devote some energy to ensure that the protocol is robust by-design.****
>
> ** **
>
> [1] http://http2.github.com/http2-spec/#rfc.section.3.7.9.3****
>
> ** **
>
> ** **
>
> *Sent:* Friday, February 22, 2013 3:57 PM
> *To:* Osama Mazahir
> *Cc:* Yoav Nir; Martin Thomson; Roberto Peon; [email-masked] Group
>
> On Fri, Feb 22, 2013 at 3:45 PM, Osama Mazahir <[email-masked]>
> wrote:****
>
>   ****
>
> leave the race hole open then we are just increasing complexity.****
>
>  ** **
>
> implementing SPDY, it is not.****
>
>  ****
>
>  1.       Client will have to track negative allowance (because it did
> not know how many requests it allowed to send)****
>
>  ** **
>
> streams it has in order to respect the limit.****
>
>  ****
>
>  2.       Server has to promise that RST_STREAM due to
> o   The server should verb agnostic (i.e. GET vs POST) and just look at
> some streamCount variable.****
>
> o   Otherwise, client will have to pend all non-idempotent requests until
> it gets the SETTINGS frame from the server****
>
>  ** **
>
> Since RST_STREAM has an error code, this is easy to define.****
>
>  ****
>
>  3.       Client will have to resubmit the request into its queue to be
> sent when the allowance opens up****
>
> the HTTP request over a new connection.****
>
>  4.       If the “blind” request(s) (i.e. sent before client received the
> SETTINGS frame) have entity-body then client****
>
> o   Must wait until the server’s SETTINGS frame before sending
> entity-body OR****
>
> o   Be able to regenerate the entity-body when the “blind” request is
> RST_STREAMed****
>
> §  This means the layer on top of client stack needs to be able to handle
> a “retry” error and resubmit the entity-body OR****
>
> §  The client stack buffers all the entity-body, as it converts it into
> DATA frames, until it knows that the request won’t get RST_STREAM due to
> max_concurrent_stream****
>
> o   Or just blow up and complain to the user****
>
>  ** **
>
> Again, clients already have to handle this.****
>
>  ****
>
>   ****
>
> begin with instead of piling on complexity to react to the races.****
>
>  ** **
>
> this is a big burden. If you believe it is, I would like to hear why.****
>
>  ****
>
>   ****
>
>  ****
>

---

From: William Chan (陈智昌) <[email-masked]>
Date: Tue, 26 Feb 2013 08:29:16 -0800

On Tue, Feb 26, 2013 at 8:26 AM, William Chan (陈智昌)
<[email-masked]>wrote:

> So, on that point, I think we may agree to disagree and see how the rest
> of the working group feels.
>
> mechanisms we have available, in order
>

On re-reading this, "open to discussing" sound rather presumptuous. Just
wanted to follow up to say it's not my intent to decide what can and cannot
be discussed :P I blame the early morning.

> to attempt to reduce complexity. If we can reasonably prevent races via
> conveying SETTINGS sooner, then great. But I still believe the defaults
> should be chosen so they do not impose performance costs due to roundtrips
> to raise the limits from a low default. As Patrick says, 8 is far too
> small. The default should be on the order of 100. It's very common to do
> large domain sharding to a CDN, and we should make sure we can handle that
> case with a single connection, rather than incentivizing web devs to
> continue to do domain sharding to achieve desired parallelism.
>
>
>> **2.       **Defaults and minimums: In the spec we pick some defaults
>> and minimums so that each endpoint starts at a known initial state and each
>> o   Otherwise, client will have to pend all non-idempotent requests
>> until it gets the SETTINGS frame from the server****
>>
>>  ** **
>>
>>  Clients already have to know how to do this due to the GOAWAY race.
>> They also have to handle this in HTTP/1.X today. For example, if we get an
>> error when reusing a persistent HTTP connection (e.g. TCP RST), we will
>> resend the HTTP request over a new connection.****
>>
>>  4.       If the “blind” request(s) (i.e. sent before client received
>> §  This means the layer on top of client stack needs to be able to
>> We always have to examine what the choices end up being for which
>> parties. If servers end up limiting parallelism, or requiring roundtrips to
>> ramp up parallelism, then clients which want speed (browsers) will be
>> incentivized to simply open up more connections to bypass the low
>> parallelism limit or slow start.****
>>
>>  ****
>>
>> Overall, I think it's better to tolerate the minor suboptimality of
>> having servers RST_STREAM streams if they don't want so much parallelism,
>> rather than incentivize browsers to open more connections.****
>>
>>  ****
>>
>>  ****
>>
>>  ****
>>

---

From: Yoav Nir <[email-masked]>
Date: Tue, 26 Feb 2013 22:15:21 +0000

Hi

Thinking it over, even a minimal server that can only handle one request at a time can be accommodated by the current document. Such a server would send a SETTINGS frame ASAP, and would process incoming streams one by one. Streams where we get frames before the FLAG_FIN of a previous stream would get a RST. But GETs are more numerous than POSTs, so bodies tend to be empty. Until its SETTINGS is processed by the client, the requests will mostly be just SYN_STREAM frames with the FLAG_FIN set, and they can be handled sequentially. Depending on how the MUSTs and SHOULDs are phrased, this may or may not be compliant, but it would be interoperable with any HTTP/2 client.

Back to the issue, I don't think any number makes sense other than 1. So if we can't wait the round-trip, we might as well set no initial limit, and leave it up to the application (client) to decide how many are prudent initially. But instead the spec should make it clear that having a stream reset with REFUSED_STREAM or some such is not a fatal error, and the client should try again with a new stream. Servers should be advised to send the SETTINGS frame before sending any RST, so that the client doesn't retry when too many (in the server's opinion) streams are open.

On Feb 26, 2013, at 6:26 PM, William Chan (陈智昌) <[email-masked]<mailto:[email-masked]>> wrote:

Thank you for continuing to raise this issue. I definitely think this is worth discussing. I've reflected a bit on what you and others have said. If I understand you correctly, you are primarily concerned with the races before limits can be negotiated, and would like to see them fixed. I've pointed out that the races existed in HTTP/1.X and still exist with things like GOAWAY. It sounds like you'd like to fix them. I'm OK with fixing them as long as they do not impose performance costs due to extra roundtrips to reach appropriate parallelism. I think we disagree on acceptable code complexity. We've already implemented this logic in Chromium and believe it not to be burdensome.

So, on that point, I think we may agree to disagree and see how the rest of the working group feels.

But, can we fix the race without imposing performance costs? Let's examine the cases:
(1) Upgrade, where, assuming successful negotiation, the server begins HTTP/2 in response to the HTTP/1.1 request with the Upgrade header.
(2) HTTPS negotiation via TLS-NPN style mechanism. Client speaks HTTP/2 first.
(3) Out of band discovery like DNS, and the client starts speaking HTTP/2

In (1), the server speaks first and can send SETTINGS immediately. So respecting server limits is not a concern. Respecting client limits is a concern. I think this falls into your "handshake advertise" scenario. We could add a HTTP header for the relevant SETTINGS during the Upgrade. This way, the server can respect the client limits.

In (2), the client speaks first, and will only respect server default limits, not server specified limits since the server hasn't had a chance to send them yet. It's conceivable we could add settings into the NPN handshake. I'm a bit concerned about stashing so much into that handshake, since we've also previously discussed expressing capabilities in the handshake (e.g: supporting WebSockets over HTTP/2). If we wanted to do something like this, we probably would need to convey such a requirement to the TLS WG. I'm hesitant.

In (3), since there is no negotiation, only discovery via DNS mechanisms, we'd have to stash the settings in DNS as discussed previously, and probably sign it too.

I'm open to discussing conveying SETTINGS via the negotiation/discovery mechanisms we have available, in order to attempt to reduce complexity. If we can reasonably prevent races via conveying SETTINGS sooner, then great. But I still believe the defaults should be chosen so they do not impose performance costs due to roundtrips to raise the limits from a low default. As Patrick says, 8 is far too small. The default should be on the order of 100. It's very common to do large domain sharding to a CDN, and we should make sure we can handle that case with a single connection, rather than incentivizing web devs to continue to do domain sharding to achieve desired parallelism.

On Tue, Feb 26, 2013 at 12:34 AM, Osama Mazahir <[email-masked]<mailto:[email-masked]>> wrote:
Internet Explorer has similar gymnastics.  However, I don’t think that is just cause to reinvent the same problems again.

From: [email-masked]<mailto:[email-masked]> [mailto:[email-masked]<mailto:[email-masked]>] On Behalf Of William Chan (???)
Sent: Friday, February 22, 2013 3:57 PM
To: Osama Mazahir
Cc: Yoav Nir; Martin Thomson; Roberto Peon; [email-masked]<mailto:[email-masked]> Group

•  This means the layer on top of client stack needs to be able to handle a “retry” error and resubmit the entity-body OR

•  The client stack buffers all the entity-body, as it converts it into DATA frames, until it knows that the request won’t get RST_STREAM due to max_concurrent_stream

Yoav

Email secured by Check Point

---

From: Roberto Peon <[email-masked]>
Date: Tue, 26 Feb 2013 14:31:40 -0800

On Tue, Feb 26, 2013 at 2:15 PM, Yoav Nir <[email-masked]> wrote:

>  Hi
>
>  Thinking it over, even a minimal server that can only handle one request
> at a time can be accommodated by the current document. Such a server would
> send a SETTINGS frame ASAP, and would process incoming streams one by one.
> Streams where we get frames before the FLAG_FIN of a previous stream would
> get a RST. But GETs are more numerous than POSTs, so bodies tend to be
> empty. Until its SETTINGS is processed by the client, the requests will
> mostly be just SYN_STREAM frames with the FLAG_FIN set, and they can be
> handled sequentially. Depending on how the MUSTs and SHOULDs are phrased,
> this may or may not be compliant, but it would be interoperable with any
> HTTP/2 client.
>
>  Back to the issue, I don't think any number makes sense other than 1. So
> if we can't wait the round-trip, we might as well set no initial limit, and
> leave it up to the application (client) to decide how many are prudent
> initially.
>

As a server owner, that (unlimited) makes me a bit afraid-- I want to set a
high enough default that we don't cause additional latency most of the
time, but also small enough that I can catch misbehaving or malicious
parties in bad behavior as quickly as possible.
We picked 100 originally as it was the nearest power-of-something-familiar
where the limit was greater than the average number of resources on a page,
and it is reasonable for a server to reject what it needs to.
Sadly, we have experience with this already-- we have at least one domain
where the maximum concurrency is limited to 10, and so we do already have
experience in the scenario where the default is higher than we intend to
run with. It causes no real problems.

> But instead the spec should make it clear that having a stream reset with REFUSED_STREAM
> or some such is not a fatal error, and the client should try again with a
> new stream. Servers should be advised to send the SETTINGS frame before
> sending any RST, so that the client doesn't retry when too many (in the
> server's opinion) streams are open.
>

Agreed-- if the spec isn't already clear about it must be fixed!
-=R

>
>
>  On Feb 26, 2013, at 6:26 PM, William Chan (陈智昌) <[email-masked]>
> wrote:
>
>  In (3), since there is no negotiation, only discovery via DNS
> mechanisms, we'd have to stash the settings in DNS as discussed previously,
> and probably sign it too.
>

---

From: Yoav Nir <[email-masked]>
Date: Tue, 26 Feb 2013 23:01:20 +0000

On Feb 27, 2013, at 12:31 AM, Roberto Peon <[email-masked]<mailto:[email-masked]>>
 wrote:

On Tue, Feb 26, 2013 at 2:15 PM, Yoav Nir <[email-masked]<mailto:[email-masked]>> wrote:
Hi

Back to the issue, I don't think any number makes sense other than 1. So if we can't wait the round-trip, we might as well set no initial limit, and leave it up to the application (client) to decide how many are prudent initially.

As a server owner, that (unlimited) makes me a bit afraid-- I want to set a high enough default that we don't cause additional latency most of the time, but also small enough that I can catch misbehaving or malicious parties in bad behavior as quickly as possible.

OK. But suppose we set the default to 100 and a client opens 101 concurrent streams. What would a server do? It's very rare that a server can do anything interesting in real-time to enforce RFC requirements.

We picked 100 originally as it was the nearest power-of-something-familiar where the limit was greater than the average number of resources on a page, and it is reasonable for a server to reject what it needs to.
Sadly, we have experience with this already-- we have at least one domain where the maximum concurrency is limited to 10, and so we do already have experience in the scenario where the default is higher than we intend to run with. It causes no real problems.

A paragraph that ends with "causes no real problems" should not begin with "Sadly" :-)

But instead the spec should make it clear that having a stream reset with REFUSED_STREAM or some such is not a fatal error, and the client should try again with a new stream. Servers should be advised to send the SETTINGS frame before sending any RST, so that the client doesn't retry when too many (in the server's opinion) streams are open.

The spec is currently silent about why a server might want to close a stream with REFUSED_STREAM, or what a client should take that to mean. All it says is this:

   Upon receipt of a SYN_STREAM, the recipient can reject the stream by
   sending a stream error (Section 3.4.2<http://tools.ietf.org/html/draft-ietf-httpbis-http2-01#section-3.4.2>) with the error code
   REFUSED_STREAM.  Note, however, that the creating endpoint may have
   already sent additional frames for that stream which cannot be
   immediately stopped

So the server "can reject", but it's not clear why it would or what this means to the client.

Yoav

---

From: William Chan (陈智昌) <[email-masked]>
Date: Tue, 26 Feb 2013 17:03:45 -0800

On Tue, Feb 26, 2013 at 3:01 PM, Yoav Nir <[email-masked]> wrote:

>
>  On Feb 27, 2013, at 12:31 AM, Roberto Peon <[email-masked]>
>  wrote:
>
>>  Thinking it over, even a minimal server that can only handle one
>> request at a time can be accommodated by the current document. Such a
>> server would send a SETTINGS frame ASAP, and would process incoming streams
>> one by one. Streams where we get frames before the FLAG_FIN of a previous
>> stream would get a RST. But GETs are more numerous than POSTs, so bodies
>> tend to be empty. Until its SETTINGS is processed by the client, the
>> requests will mostly be just SYN_STREAM frames with the FLAG_FIN set, and
>> they can be handled sequentially. Depending on how the MUSTs and SHOULDs
>> are phrased, this may or may not be compliant, but it would be
>> interoperable with any HTTP/2 client.
>>
>>  Back to the issue, I don't think any number makes sense other than 1.
>> So if we can't wait the round-trip, we might as well set no initial limit,
>> and leave it up to the application (client) to decide how many are prudent
>> initially.
>>
>
>  As a server owner, that (unlimited) makes me a bit afraid-- I want to
> set a high enough default that we don't cause additional latency most of
> the time, but also small enough that I can catch misbehaving or malicious
>  OK. But suppose we set the default to 100 and a client opens 101
> concurrent streams. What would a server do? It's very rare that a server
> can do anything interesting in real-time to enforce RFC requirements.
>

Then the client isn't respecting the protocol. I favor quick session
termination - GOAWAY.

>   We picked 100 originally as it was the nearest
> power-of-something-familiar where the limit was greater than the average
> number of resources on a page, and it is reasonable for a server to reject
> what it needs to.
>  The spec is currently silent about why a server might want to close a
> stream with REFUSED_STREAM, or what a client should take that to mean.
> All it says is this:
>
>    sending a stream error (Section 3.4.2 <http://tools.ietf.org/html/draft-ietf-httpbis-http2-01#section-3.4.2>) with the error code

That's another place where we did not spec it out in SPDY as well as we
should have. In Chromium, we retry on REFUSED_STREAM, because we think it
happened due to a race or something (GOAWAY or SETTINGS race):
https://code.google.com/p/chromium/codesearch#chromium/src/net/http/http_network_transaction.cc&q=REFUSED_STREAM&sq=package:chromium&type=cs&l=1307.
For races, I think it's reasonable for the peer to send a RST_STREAM with
REFUSED_STREAM. For complete failures to respect the protocol, e.g. default
streams limit, I think the receiver should terminate the connection with a
GOAWAY with PROTOCOL_ERROR.