h2 icon indicating copy to clipboard operation
h2 copied to clipboard

Support WebSocket upgrade

Open ubnt-intrepid opened this issue 6 years ago • 8 comments

RFC 8441 specifies the upgrade mechanism to WebSocket over HTTP/2. Since it includes several extensions to the HTTP/2 protocol, the server API might need to support the configuration of them.

ubnt-intrepid avatar Feb 23 '19 13:02 ubnt-intrepid

Would you be able to provide a summary of the ways in which h2 needs to change to support this use case?

carllerche avatar Feb 25 '19 19:02 carllerche

Here is a summary of RFC:

  • Adds a new SETTINGS parameter SETTINGS_ENABLE_CONNECT_PROTOCOL (=0x8) is added[^1]. The server sends this parameter with the value 1 at the beginning of connection.
  • The handshake request uses CONNECT method instead of GET [^2]. The header fields used for handshaking are similar to the current WebSocket spec, but several pseudo-header been added or removed.
  • After the request is upgraded, the payloads are send and received via DATA frame.

[^1] : https://tools.ietf.org/html/rfc8441#section-3 [^2] : https://tools.ietf.org/html/rfc8441#section-4

ubnt-intrepid avatar Feb 26 '19 06:02 ubnt-intrepid

Seems plausible. I wonder if we need to add support for that specific settings option or we can extend h2 to allow for configurable settings.

carllerche avatar Feb 26 '19 17:02 carllerche

I've been looking into this. Adding SETTINGS_ENABLE_CONNECT_PROTOCOL is straightforward but I'm stuck on exposing the :protocol pseudo-header.

The client needs to send it, the server needs to receive it, but I can't pass it through http::Request because it's not a legal traditional HTTP header. Suggestions on how to proceed welcome.

bnoordhuis avatar Sep 12 '21 09:09 bnoordhuis

Hm, ya... The first thing that comes to my mind is to store some h2::Something type in the Request::extensions() indicating a :protocol pseudo header. Does that work cleanly? Should it?

seanmonstar avatar Sep 15 '21 17:09 seanmonstar

I had the same thought and have a local prototype that adds a pub fn websocket(req: &mut Request<()>) { /* ... */ } that does that (but probably needs a better name.)

It feels kind of icky for reasons I can't quite articulate though. :-)

bnoordhuis avatar Sep 15 '21 20:09 bnoordhuis

Is it feasible to add the pseudo header information to the RecvStream?

Icelk avatar Aug 23 '22 19:08 Icelk

Is there any way I can help? I would appreciate if we can implement this, so I can use WebSockets on HTTP/2 :)

Icelk avatar Aug 29 '22 19:08 Icelk

I believe this was largely done by @nox in #565 a year ago. Anything missing? It looks like h2::ext::Protocol is not added as extension to the Request, at least I don't see it even though tracing shows that h2 received headers containing the :protocol pseudo header (set to b"websocket" in my case).

The server tests don't seem to cover this. https://github.com/hyperium/h2/blob/07d20b19abfd3bf57bd80976089e3c24a3166bca/tests/h2-tests/tests/server.rs I was hoping for an example that shows how one can check for "the-bread-protocol" on the server side.

cloneable avatar Dec 22 '22 18:12 cloneable

I added the following code to server::Peer::convert_poll_message() and that exposes the protocol as Request extension. This works for me. Not sure if this is the right place to do this, though.

if let Some(p) = pseudo.protocol {
    if let Some(exts) = b.extensions_mut() {
        exts.insert(p);
    }
}

https://github.com/hyperium/h2/blob/07d20b19abfd3bf57bd80976089e3c24a3166bca/src/server.rs#L1427-L1535

cloneable avatar Dec 22 '22 19:12 cloneable