Subject: REFUSED_STREAM and CANCEL

From: Martin Thomson <[email-masked]>
Date: Tue, 26 Mar 2013 11:16:23 -0700

I'm just going through the HTTP/2.0 error codes and this leaped out.

Can anyone explain the useful difference between the REFUSED_STREAM
and CANCEL error codes?

All the superficial reasons carry no semantic value:
 - who initiated the stream
 - whether the stream has (or has not) started processing

Furthermore, the implication from REFUSED_STREAM is that this is a
rejection, but we don't block stream use on permission.  We just send
data and wait for the other side to object.

Abandonment of a stream doesn't just occur from the stream initiator
side (even if that is the natural place to abort from).  The
implication of asymmetry seems unnecessary.

All of the use cases could be covered with a single CANCEL_STREAM code.

---

From: Roberto Peon <[email-masked]>
Date: Tue, 26 Mar 2013 12:40:55 -0700

REFUSED_STREAM is current for the receiver of a pushed stream to indicate
that it is refusing the stream for a protocol semantics violation, as
opposed to cancelling it (ostensibly because it already has it).
In the case of Chome, this can happen when the server erroneously pushes a
resource and doesn't say the associated (request) stream.

It exists just so that the error code give some idea of what went wrong ,
instead of having to rely upon text in the error which is supposed to be
opaque and implementation dependent.

-=R

---

From: William Chan (陈智昌) <[email-masked]>
Date: Tue, 26 Mar 2013 12:42:07 -0700

REFUSED_STREAM says it was refused before any processing happened. This can
happen due to GOAWAY races or SETTINGS max_concurrent_streams races. The
client knows that it's safe to retry the SYN_STREAM later on (after the
number of streams goes down below the concurrency limit) or on a new
connection (after a GOAWAY).

---

From: Roberto Peon <[email-masked]>
Date: Tue, 26 Mar 2013 13:08:05 -0700

What Will says is also right, and what he stated is the main reason it was
put in (I think mnot was the one who suggested it, actually!)

Indeed yes-- this is documented in a conversation on spdy-dev back on
11/18/09

As Will puts it:
We're being inconsistent here and should fix it :)
-=R

On Tue, Mar 26, 2013 at 12:42 PM, William Chan (陈智昌)
<[email-masked]>wrote:

> REFUSED_STREAM says it was refused before any processing happened. This
> can happen due to GOAWAY races or SETTINGS max_concurrent_streams races.
> The client knows that it's safe to retry the SYN_STREAM later on (after the
> On Tue, Mar 26, 2013 at 11:16 AM, Martin Thomson <[email-masked]
> > wrote:
>

---

From: Martin Thomson <[email-masked]>
Date: Tue, 26 Mar 2013 13:16:15 -0700

On 26 March 2013 12:42, William Chan (陈智昌) <[email-masked]> wrote:
> REFUSED_STREAM says it was refused before any processing happened.

The temporal distinction ("before any processing happened") isn't a
useful one.  "Do not want" and "do not want any more" are so finely
graded in distinction as to be meaningless.

More importantly, given that a stream can contain frames that mutate
session state, ALL frames need to be processed to some extent.  If
this is to mean that it never surfaced above the framing layer, I
don't believe that information is actionable.

> The
> client knows that it's safe to retry the SYN_STREAM later on [...]

Presumably not for the same stream ;)

Bottom line: what do I do differently in response to a refused stream
as opposed to a cancelled one?

---

From: Roberto Peon <[email-masked]>
Date: Tue, 26 Mar 2013 13:28:12 -0700

You (rather, a browser) can automatically send the request again in the
case where a non-idempotent method such as POST was used.
With cancel, the browser would have to ask the client to confirm before
doing so.
-=R

---

From: Martin Thomson <[email-masked]>
Date: Tue, 26 Mar 2013 13:41:43 -0700

On 26 March 2013 13:28, Roberto Peon <[email-masked]> wrote:
> You (rather, a browser) can automatically send the request again in the case
> where a non-idempotent method such as POST was used.

Why would the client not retry?  I would have modeled all of these as
being equivalent to an HTTP/1.1 connection drop (albeit with a nicer
recovery story).

It might help to examine why a server would send a RST_STREAM w/
CANCEL.  Here's what I can think of:
 - server is overloaded, wants to send Retry-After for requests but
not lose the connection
 - resource disappeared or changed mid-response
 - an upstream connection or request broke

Anything else?  Because I can't see why retry is a bad idea in any of
these cases, subject to the normal restrictions (idempotence, stream
availability, etc...).

---

From: Martin Thomson <[email-masked]>
Date: Tue, 26 Mar 2013 13:55:49 -0700

OK, I got it.  This is a new feature that the framing layer offers the
HTTP usage, namely: safely retry non-idempotent requests.

It's not explained especially well, so I've added an issue to track
the creation of a better explanation:

https://github.com/http2/http2-spec/issues/57

---

From: Amos Jeffries <[email-masked]>
Date: Thu, 28 Mar 2013 00:07:58 +1300

> these cases, subject to the normal restrictions (idempotence, stream

A CONNECT or Upgrade: request is requiring the TCP connection be 
converted from HTTP/1 to something else. New streams are not necessarily 
unwanted, but cannot use the connection now.

Amos