Subject: Chrome experimental support for WebSockets over HTTP/2

From: Bence Béky <[email-masked]>
Date: Thu, 8 Mar 2018 13:16:02 -0500

Hi folks,

Chrome has experimental support for https://tools.ietf.org/
html/draft-ietf-httpbis-h2-websockets-00 starting with release 67
(currently only on Canary channel, soon Dev too).  One needs to invoke
Chrome with the --enable-websocket-over-http2 command line flag to enable
it.  The implementation is likely rough around the edges, so I am looking
for servers to test against.  If you are running a publicly available
server supporting WebSockets over HTTP/2, or have one (or a reverse proxy)
that I can compile and run locally, please let me know.

Thank you,

Bence

---

From: Andy Green <[email-masked]>
Date: Fri, 9 Mar 2018 08:10:09 +0800

On 03/09/2018 02:16 AM, Bence Béky wrote:
> Hi folks,
> 
> Chrome has experimental support for 
> https://tools.ietf.org/html/draft-ietf-httpbis-h2-websockets-00 
> <https://tools.ietf.org/html/draft-ietf-httpbis-h2-websockets-00> 
> starting with release 67 (currently only on Canary channel, soon Dev 
> too).  One needs to invoke Chrome with the --enable-websocket-over-http2 
> command line flag to enable it.  The implementation is likely rough 
> around the edges, so I am looking for servers to test against.  If you 
> are running a publicly available server supporting WebSockets over 
> HTTP/2, or have one (or a reverse proxy) that I can compile and run 
> locally, please let me know.

I am anxious to try it, but

Chrome Canary is currently not available on the Linux platform.

https://www.google.com/chrome/browser/canary.html

Is the Android build going to allow me to try it (and what's the 
equivalent of the commandline flag to enable it if so)?

-Andy

---

From: Bence Béky <[email-masked]>
Date: Fri, 9 Mar 2018 14:14:34 -0500

Hi Andy,

It should also work on Android.  The command line flag is the same.  67
should trickle down to Dev channel within a week.

I'm also anxious to hear if it works.

Bence

On Thu, Mar 8, 2018 at 7:10 PM, Andy Green <[email-masked]> wrote:

>> Chrome has experimental support for https://tools.ietf.org/html/dr
>> aft-ietf-httpbis-h2-websockets-00 <https://tools.ietf.org/html/d
>> raft-ietf-httpbis-h2-websockets-00> starting with release 67 (currently
>> only on Canary channel, soon Dev too).  One needs to invoke Chrome with the
>> --enable-websocket-over-http2 command line flag to enable it.  The
>> implementation is likely rough around the edges, so I am looking for
>> servers to test against.  If you are running a publicly available server
>> supporting WebSockets over HTTP/2, or have one (or a reverse proxy) that I
>> can compile and run locally, please let me know.
>>
>

---

From: Andy Green <[email-masked]>
Date: Sun, 11 Mar 2018 16:34:51 +0800

On 03/11/2018 04:29 PM, Tatsuhiro Tsujikawa wrote:
> Hi,
> 
> On Sat, Mar 10, 2018 at 4:14 AM, Bence Béky <[email-masked] 
> <mailto:[email-masked]>> wrote:
> 
>     Hi Andy,
> 
>     It should also work on Android.  The command line flag is the same. 
>     67 should trickle down to Dev channel within a week.
> 
> ​I just deployed my reverse proxy server with ws over http2 at 
> https://nghttp2.org/echo.
> It forwards websocket request to backend echo server.  (path should be 
> /echo)
> 
> I tested with chrome canary, and it seems to work. I first have to load 
> nghttp2.org <http://nghttp2.org> with h2 otherwise chrome uses 
> HTTP/1.1.  Probably it is intentional.
> 
> One thing I noticed is :authority header field with well known port 
> number.  Chrome sends request like this:
> 
> """
> :method: CONNECT
> :authority: nghttp2.org:443 <http://nghttp2.org:443>
> :scheme: https
> :path: /echo
> :protocol: websocket
> origin: https://nghttp2.org
> """
> 
> And the reverse proxy translates it into HTTP/1.1 request:
> 
> """
> GET /echo HTTP/1.1
> Host: nghttp2.org:443 <http://nghttp2.org:443>
> Origin: https://nghttp2.org
> """

Does your proxy stuff test WS CLOSE frames?  Because I also have it 
working except the browser doesn't seem to see the ws CLOSE reason.  I'm 
not sure if that's my problem, but I do seem to send the encapsulated 
CLOSE frame.

> The websocket echo server I use is echo server from 
> https://github.com/gorilla/websocket/tree/master/examples/echo, and it 
> compares nghttp2.org:443 <http://nghttp2.org:443> from Host header 
> field, and nghttp2.org <http://nghttp2.org> from Origin header field, 
> and it complains because they differ.
> I'm not sure this is standard practice, but I think they do this because 
> normally we have "Host: nghttp2.org <http://nghttp2.org>" in HTTP/1.1 
> request because 443 is default port number for https.
> 
> If these practice is widely used, it might be better to drop default 
> port number from :authority.  The extended CONNECT method is more like 
> regular GET because :authority is treated like authority component of 
> target URI rather than the remote server which an intermediary should 
> connect to.

I don't have any opinion about that but IMHO it would be good if there 
was a way you could understand that ws-over-h2 was in use from JS inside 
the browser.

-Andy

> Best regards,
> Tatsuhiro Tsujikawa
> 
> 
> 
>     On Thu, Mar 8, 2018 at 7:10 PM, Andy Green <[email-masked]
>     <mailto:[email-masked]>> wrote:
> 
> 
> 
>             <https://tools.ietf.org/html/draft-ietf-httpbis-h2-websockets-00
>             <https://tools.ietf.org/html/draft-ietf-httpbis-h2-websockets-00>>
>             starting with release 67 (currently only on Canary channel,
>             soon Dev too).  One needs to invoke Chrome with the
>             --enable-websocket-over-http2 command line flag to enable
>             it.  The implementation is likely rough around the edges, so
>             I am looking for servers to test against.  If you are
>             running a publicly available server supporting WebSockets
>             over HTTP/2, or have one (or a reverse proxy) that I can
>             compile and run locally, please let me know.
> 
> 
>         <https://www.google.com/chrome/browser/canary.html>
>

---

From: Tatsuhiro Tsujikawa <[email-masked]>
Date: Sun, 11 Mar 2018 17:29:48 +0900

Hi,

I tested with chrome canary, and it seems to work. I first have to load
nghttp2.org with h2 otherwise chrome uses HTTP/1.1.  Probably it is
intentional.

"""
:method: CONNECT
:authority: nghttp2.org:443
:scheme: https
:path: /echo
:protocol: websocket
origin: https://nghttp2.org
"""

"""
GET /echo HTTP/1.1
Host: nghttp2.org:443

The websocket echo server I use is echo server from
https://github.com/gorilla/websocket/tree/master/examples/echo, and it
compares nghttp2.org:443 from Host header field, and nghttp2.org from
Origin header field, and it complains because they differ.
I'm not sure this is standard practice, but I think they do this because
normally we have "Host: nghttp2.org" in HTTP/1.1 request because 443 is
default port number for https.

If these practice is widely used, it might be better to drop default port
number from :authority.  The extended CONNECT method is more like regular
GET because :authority is treated like authority component of target URI
rather than the remote server which an intermediary should connect to.

Best regards,
Tatsuhiro Tsujikawa

---

From: Tatsuhiro Tsujikawa <[email-masked]>
Date: Sun, 11 Mar 2018 17:44:46 +0900

Hi,

On Sun, Mar 11, 2018 at 5:34 PM, Andy Green <[email-masked]> wrote:

>> On Sat, Mar 10, 2018 at 4:14 AM, Bence Béky <[email-masked] <mailto:
>> [email-masked]>> wrote:
>>
>>     Hi Andy,
>>
> not sure if that's my problem, but I do seem to send the encapsulated CLOSE
> frame.
>
>
​My proxy just transfer bytes between client and backend websocket server
without inspecting ws frames, and if an endpoint sends CLOSE, then proxy
probably forwards it.  The one caveat is timeout case in backend
connection.  If that happens, proxy might just reset a stream, and no CLOSE
frame will be sent.​

> I don't have any opinion about that but IMHO it would be good if there was
> a way you could understand that ws-over-h2 was in use from JS inside the
> browser.
>
> -Andy
>
> Best regards,
>> Tatsuhiro Tsujikawa
>>
>>
>>

---

From: Bence Béky <[email-masked]>
Date: Sun, 11 Mar 2018 14:28:56 -0400

Hi,

Thank you for feedback.

Chrome sends the port in the :authority header because the example
in https://tools.ietf.org/html/draft-ietf-httpbis-h2-websockets-00 Section
5.1
<https://tools.ietf.org/html/draft-ietf-httpbis-h2-websockets-00#section-5.1>
has the port number.  However, Section 4
<https://tools.ietf.org/html/draft-ietf-httpbis-h2-websockets-00#section-4>
defers to RFC7540 Section 8.1.2.3
<https://tools.ietf.org/html/rfc7540#section-8.1.2.3> concerning
:authority, which in turn defers to RFC3986 Section 3.2
<https://tools.ietf.org/html/rfc3986#section-3.2>, where Section 3.2.3
<https://tools.ietf.org/html/rfc3986#section-3.2.3> explicitly states
default port should be omitted.  So I'm happy to omit the default port
number.

I'll look into the CLOSE frames this coming week.

Cheers,

Bence

On Sun, Mar 11, 2018 at 4:29 AM, Tatsuhiro Tsujikawa <[email-masked]>
wrote:

> The websocket echo server I use is echo server from https://github.com/
> gorilla/websocket/tree/master/examples/echo, and it compares
> nghttp2.org:443 from Host header field, and nghttp2.org from Origin
> header field, and it complains because they differ.
>>>> raft-ietf-httpbis-h2-websockets-00> starting with release 67

---

From: Patrick McManus <[email-masked]>
Date: Sun, 11 Mar 2018 21:30:14 +0000 (UTC)

Hey, this is s great to see (as both the author and co-chair). Also, from a
Mozilla POV this is great - we're intending to kick off this work very soon
so its great to have partners to test with.

I think this discussion indicates the example in 5.1 ought to be updated
(though both productions with and without the default are legal, so you
probably shouldn't do anything different based on it.)

On Sun, Mar 11, 2018 at 2:28 PM, Bence Béky <[email-masked]> wrote:

> in https://tools.ietf.org/html/draft-ietf-httpbis-h2-websockets-00
> Section 5.1
> On Sun, Mar 11, 2018 at 4:29 AM, Tatsuhiro Tsujikawa <
> [email-masked]> wrote:
>
>> Hi,
>>

---

From: Andy Green <[email-masked]>
Date: Mon, 12 Mar 2018 08:57:31 +0800

> I'll look into the CLOSE frames this coming week.

Please don't worry about that... I traced it to my not realizing 
END_STREAM flag when I send the last encapsulated ws frame is not 
enough, I have to also RST_STREAM.  Then as far as it goes, on my PC, 
the lws test server is working all on one socket :-)  Very nice.

However I met an instability in chrome canary.  If I either run my 
server under valgrind, or run it on ESP32, Chrome on Win 10 will crash, 
the whole app disappears.

An example of the crash is uploaded as crash id bdbb8e6f4d275a10.  I am 
not sure it has anything to do with the ws-over-h2 support though... if 
I dump my outgoing packets with and without valgrind, a) diffing them 
reveals no difference in what we send either way, and b) it changes the 
behaviour in chrome such that it reports "Failed to load resource: 
net::ERR_SPDY_PROTOCOL_ERROR" when the server is h2, not SPDY.  Removing 
the dumps makes Chrome die again, so some kind of race I guess.

Anyway great job, I can get some confidence the code on my side is 
basically working too.

-Andy

> Cheers,
> 
> Bence
> 
> On Sun, Mar 11, 2018 at 4:29 AM, Tatsuhiro Tsujikawa 
> <[email-masked] <mailto:[email-masked]>> wrote:
> 
>     Hi,
> 
>         It should also work on Android.  The command line flag is the
>         same.  67 should trickle down to Dev channel within a week.
> 
>     It forwards websocket request to backend echo server.  (path should
>     be /echo)
> 
>     I tested with chrome canary, and it seems to work. I first have to
>     load nghttp2.org <http://nghttp2.org> with h2 otherwise chrome uses
>     https://github.com/gorilla/websocket/tree/master/examples/echo
>     <https://github.com/gorilla/websocket/tree/master/examples/echo>,
>     and it compares nghttp2.org:443 <http://nghttp2.org:443> from Host
>     header field, and nghttp2.org <http://nghttp2.org> from Origin
>     I'm not sure this is standard practice, but I think they do this
>     because normally we have "Host: nghttp2.org <http://nghttp2.org>" in
>     HTTP/1.1 request because 443 is default port number for https.
> 
>     port number from :authority.  The extended CONNECT method is more
>     like regular GET because :authority is treated like authority
>     component of target URI rather than the remote server which an
>     intermediary should connect to.
> 
>     Best regards,
>     Tatsuhiro Tsujikawa
> 
> 
> 
>                 starting with release 67 (currently only on Canary
>                 channel, soon Dev too).  One needs to invoke Chrome with
>                 the --enable-websocket-over-http2 command line flag to
>                 enable it.  The implementation is likely rough around
>                 the edges, so I am looking for servers to test against. 
>                 If you are running a publicly available server
>                 supporting WebSockets over HTTP/2, or have one (or a
>                 reverse proxy) that I can compile and run locally,
>                 please let me know.
> 
> 
>             Is the Android build going to allow me to try it (and what's
>             the equivalent of the commandline flag to enable it if so)?
> 
>             -Andy
> 
>                 Thank you,
> 
>                 Bence
> 
> 
> 
> 
> 
>

---

From: Andy Green <[email-masked]>
Date: Tue, 13 Mar 2018 10:04:29 +0800

On 03/12/2018 05:30 AM, Patrick McManus wrote:
> Hey, this is s great to see (as both the author and co-chair). Also, 
> from a Mozilla POV this is great - we're intending to kick off this work 
> very soon so its great to have partners to test with.

This public server is now enabled for h2-over-ws...

https://libwebsockets.org/testserver/

... it works with Bence's Chrome 67 stuff.

The page loads some graphic elements and starts 3 x wss back to the 
server... here are some numbers on the effect of ws-over-h2:

  - disable caching option in dev tools
  - use performance tab "record"
  - use performance tab "Event log" display to subtract time of 
"navigationStart" from time of last wss handshake response received

Chrome 65.3325             (linux):   3940ms
Chrome 67.3368 (win10 VM on linux):    906ms

The server is in France and I am in Taipei, so eliminating the sets of 
TLS roundtrips is really effective, YMMV.  I also tried it on the ESP32 
equivalent test page, where mbedTLS takes >1s per TLS handshake in 
computation, but it triggers a crash in Chrome Canary.  When that's 
fixed the performance improvement there will be even more extreme.

Here's a short list of implementation things I noticed:

  - h2 connections normally have some kind of idle timeout, but if any 
child stream is a ws one, these have no inherent idle cutoff time... the 
ws protocol may implement it but by default the ws stream expects to 
stay up forever.  So this may affect the main transport stream's policy 
about idle timeout.

  - close is optional in ws[s] so code targeting it is at liberty to 
just bail if it sees something it doesn't like, and close the socket 
even partway through reading a frame.  For encapsulated ws-over-h2, it's 
only at liberty to close a stream cleanly (or ignore the rest of the 
frame).  That may have implications depending on what the original code 
looked like.

  - You can't get away with just setting the END_STREAM flag on the last 
DATA sent, with, eg, a WS CLOSE frame in it.  You have to issue a 
RST_STREAM after it.

  - DATA in flight from the peer that arrives after you sent a 
RST_STREAM and the stream is logically gone is a real thing that has to 
be taken care of.

-Andy

---

From: Ken Murchison <[email-masked]>
Date: Sun, 18 Mar 2018 17:43:08 -0400

Did you use a particular client within Chrome for testing?  I'm using 
the Simple Web Socket Client plugin to test against my server and the 
websocket connection doesn't negotiate h2 via ALPN and continues to use 
HTTP/1.1.

On 03/11/2018 04:29 AM, Tatsuhiro Tsujikawa wrote:
>             enable it.  The implementation is likely rough around the
>             edges, so I am looking for servers to test against.  If
>             you are running a publicly available server supporting
>             WebSockets over HTTP/2, or have one (or a reverse proxy)

-- 
Ken Murchison
Cyrus Development Team
FastMail US LLC