Subject: [hybi] Revising handshake and AddChannelRequest considering HTTP/2.0

From: Takeshi Yoshino <[email-masked]>
Date: Fri, 27 Jul 2012 17:19:10 +0900

=== First, clarify AddChannelRequest base spec ===

Let's break down headers in handshake.

  Upgrade: websocket
  Connection: Upgrade
  Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
  Sec-WebSocket-Version: 13
  Sec-WebSocket-Extensions: ext-for-logical-ch, mux, ext-for-physical-conn

These headers (except for ext-for-logical-ch) are meaningful only when
performing HTTP Upgrade and establishing physical connection.

  GET /chat HTTP/1.1
  Host: server.example.com
  Sec-WebSocket-Protocol: chat, superchat
  Origin: http://example.com
  Cookie: foobar
  Sec-WebSocket-Extensions: ext-for-logical-ch
  Any-Other-Header: Its-Value

These headers determine by which virtual server / service and how the
logical channel are handled. So, the initial delta encoding base should
consist of only these headers.

(
It looks like moving these headers to mux's extension parameter make sense.
But please note that we can't do it because of the requirement
that WebSocket server w/o mux support should be able to accept the
connection by just rejecting mux extension. Like this:

  HTTP/1.1 101 Switching Protocols
  ...
  Sec-WebSocket-Extensions: ext-for-logical-ch
  ...
)

----

Response headers are also broken down into two groups. The following
headers should be excluded from delta encoding base.

  Upgrade: websocket
  Connection: Upgrade
  Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

(It's off topic but needs discussion whether we should send reply to
implicitly opened channel by physical channel handshake response or we
should send a separate AddChannelResponse for that. The latter allows
opening mux connection even when the request for implicitly opened channel
is bad e.g. 404)

===  Negotiation of logical channel protocol capability ===

So, how should we negotiate HTTP capability? I propose that we'll add
"protocol" extension parameter. The server announces supported logical
channel protocols.

WebSocket server w/o HTTP/2.0 support announces only ws (or may omit the
parameter) like this:

  Sec-WebSocket-Extensions: ext-for-logical-ch, mux; protocol="ws",
ext-for-physical-conn

Clients has to fall back to normal HTTP to send HTTP requests.

An HTTP/2.0 server supporting both protocol announces both like this:

  Sec-WebSocket-Extensions: ext-for-logical-ch, mux; protocol="ws, http",
ext-for-physical-conn

----

Does everything above work well when an HTTP request is the initiator? It's
often that an HTTP request precedes WebSocket connection attempt. Actually,
current WebSocket handshake + extension cannot provide clean way path for
this.

Current bootstrap of WebSocket multiplexing allows for fallback to
non-muxed WebSocket connection without extra RTT. It's good but for this
case, we don't want to establish an unnecessary WebSocket connection.
There's no way to prevent existing non-mux capable WebSocket servers from
accepting HTTP/2.0 bootstrap by mistake.

So for this case the differentiation between WebSocket and HTTP/2.0 needs
to be done by Upgrade type.

  GET /meaningless HTTP/1.1
  Connection: Upgrade
  Upgrade: http2
  Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
  Sec-WebSocket-Extensions: mux; protocol="ws, http", ext-for-physical-conn

Just headers for upgrade and security are incorporated. Mux will be
established but default channel won't be opened. WebSocket servers w/o mux
support rejects this handshake.

If over NPN,

  Sec-WebSocket-Extensions: mux; protocol="ws, http", ext-for-physical-conn

This is the only information to exchange.

---

From: Bruce Atherton <[email-masked]>
Date: Mon, 30 Jul 2012 11:20:44 -0700

> performing HTTP Upgrade and establishing physical connection.

I agree for most of these, but I am uncertain about the 
Sec-WebSocket-Key. The point of that (and the Sec-WebSocket-Accept) is 
to guarantee that both ends are actually WS endpoints. Are there not 
usecases where a datacenter frontend might split a muxed connection and 
send the channels to different servers? If so, I think we need to 
validate all the endpoints of channels by sending a nonce and receiving 
the correct hash back.

Unless people have changed their minds about the usefulness of this 
technique.

>
>   GET /chat HTTP/1.1
>   Host: server.example.com <http://server.example.com/>
>   Origin: http://example.com <http://example.com/>
>   Cookie: foobar
> logical channel are handled. So, the initial delta encoding base 
> should consist of only these headers.
>
> (
> It looks like moving these headers to mux's extension parameter make 
> sense. But please note that we can't do it because of the requirement 

Do you mean adding any number of headers as parameters to the mux 
extension declaration in the Sec-Websocket-Extensions header? A list of 
headers inside a single header seems needlessly complicated to me, even 
without the need for a fallback for non-muxing servers.

> opening mux connection even when the request for implicitly opened 
> channel is bad e.g. 404)

IMHO the 404 should result in the connection being handled the same way 
it would for an HTTP XHR request. Ditto for 300 statuses where the 
resource might have moved to another server that requires a different 
connection to be established.

What if, in a particular session, there was only going to be that one 
channel request that had failed? A useless connection would be hanging 
around for no reason. If there is a second channel request that would be 
successful, then that can be the implicitly opened channel that 
establishes the mux connection.

> WebSocket server w/o HTTP/2.0 support announces only ws (or may omit 
> the parameter) like this:
>
>   Sec-WebSocket-Extensions: ext-for-logical-ch, mux; protocol="ws, 
> http", ext-for-physical-conn
>
> ----
>
> Does everything above work well when an HTTP request is the initiator? 
> It's often that an HTTP request precedes WebSocket connection attempt. 
> Actually, current WebSocket handshake + extension cannot provide clean 
> way path for this.
>
> non-muxed WebSocket connection without extra RTT. It's good but for 
> this case, we don't want to establish an unnecessary WebSocket 
> connection. There's no way to prevent existing non-mux capable 
> WebSocket servers from accepting HTTP/2.0 bootstrap by mistake.
>
> So for this case the differentiation between WebSocket and HTTP/2.0 
> needs to be done by Upgrade type.
>
> established but default channel won't be opened. WebSocket servers w/o 
> mux support rejects this handshake.
>
> If over NPN,
>

If we think of the framing layer as separate from both Websockets and 
HTTP 2.0, we can look at the headers and consider which they belong to.

I think the Upgrade header belongs to the framing layer. Maybe it should 
be "Upgrade: frames" with another header "Sec-Framing-Protocols: http2, 
websocket" to establish the protocols that should be carried. Who knows, 
perhaps eventually there will be others such as a peer-to-peer protocol 
between browsers.

A server response could indicate which protocols it actually supported. 
This would not be backward compatible with the WS 1.0 handshake, but 
servers could support both methods fairly easily.