websocket icon indicating copy to clipboard operation
websocket copied to clipboard

feat: add experimental http2 support

Open mafredri opened this issue 3 months ago • 1 comments

This change adds experimental support for WebSockets over HTTP/2 (RFC 8441).

Implementation notes:

  • To avoid breaking any flows, HTTP/2 functionality is currently opt-in on both server and client
  • The HTTP/2 tests are currently kept in internal/thirdparty/http2 (they require importing golang.org/x/net/http2, see https://github.com/golang/go/issues/67810)
  • HTTP/2 extended CONNECT must currently be enabled via GODEBUG, see https://github.com/golang/go/issues/53208
  • Explicit selection of HTTP/1 or HTTP/2 is required during Dial because we cannot test the underlying transport for support (for instance, http.Transport does not allow setting the HTTP/2 pseudo-header :protocol)
  • To Dial with HTTP/2 support, a http2.Transport must be provided via DialOptions.Client (since we do not import golang.org/x/net/http2)
  • There are a few changes and cleanups unrelated to this feature, if needed they can be broken out into a separate branch

Closes #4


This change does not yet include benchmark tests for HTTP/2, those will be added at a later date as a follow-up.

mafredri avatar Sep 29 '25 17:09 mafredri

Have you tried to establish a connection using other tools? JS possibly?

While I was testing I experimented with connecting to the server example from a JS (Node) client. Here is what I used for anyone interested (needs ws).

const http2 = require("http2")
const { Receiver, Sender } = require("ws")

const client = http2.connect("http://127.0.0.1:8080")

const req = client.request({
 ":method": "CONNECT",
 ":protocol": "websocket",
 ":scheme": "http",
 ":path": "/",
 ":authority": "127.0.0.1",
 "Sec-WebSocket-Key": "AQIDBAUGBwgJCgsMDQ4PEC==",
 "Sec-WebSocket-Version": "13",
})

req.on("response", (headers) => {
 console.log("status:", headers[":status"])
})

req.on("error", (err) => {
 console.log("error:", err)
})

const receiver = new Receiver()
receiver.on("message", (data) => {
 console.log("got message:", data.toString())
})

req.on("data", (chunk) => {
 receiver.write(chunk)
})

setInterval(() => {
 const sender = new Sender(req)
 sender.send("hey", { binary: false, mask: true, fin: true })
}, 1000)

sample output:

status: 200
got message: hey
got message: hey
got message: hey

code-asher avatar Oct 03 '25 19:10 code-asher