Repository navigation
Conversation
469125f to
9470379
Compare
0616f05 to
047d1c0
Compare
code-asher
left a comment
There was a problem hiding this comment.
Very nice 🤌 I messed around with the example as well and it all looks good.
mtojek
left a comment
There was a problem hiding this comment.
Amazing skills 👍 👍
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 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: |
Remove bench-dev and test-dev jobs from daily workflow since the dev branch no longer exists. Add ci/static.sh script to generate coverage badge for GitHub Pages. Refs #539
Add resp.ProtoMajor check in verifyServerResponseH1 for consistency with verifyServerResponseH2 which already performs this check. Refs #539
ea8732a to
00fa373
Compare
Add resp.ProtoMajor check in verifyServerResponseH1 for consistency with verifyServerResponseH2 which already performs this check. Refs #539
|
Are there plans to ship this? |
|
@Nek-12 yes, absolutely! This fell on the back-burner but I will take another look at it next week. |
|
@mafredri With go 1.27 x/net/http2 moved into the standard library. It might be worth to relook into the changes. See: https://pkg.go.dev/golang.org/x/net/http2#section-readme I would be interested in the http2 websocket server. Do you need any help to ship this? |
Remove bench-dev and test-dev jobs from daily workflow since the dev branch no longer exists. Add ci/static.sh script to generate coverage badge for GitHub Pages. Refs #539
Nothing calls ci/static.sh, and it reads ci/out/profile.txt, which ci/test.sh does not write (it writes ci/out/coverage.prof). The script only works with the ci/test.sh changes in #539, so it moves back there and this PR only drops the dev branch jobs.
Remove bench-dev and test-dev jobs from daily workflow since the dev branch no longer exists. Add ci/static.sh script to generate coverage badge for GitHub Pages. Refs #539
Add resp.ProtoMajor check in verifyServerResponseH1 for consistency with verifyServerResponseH2 which already performs this check. Refs #539
- Fix 'exampels' typo in CI workflow comments - Fix README paths to use 'internal/examples/http2' - Change visibleAddr to display 0.0.0.0 for all-interface bindings
Rename the Protocol type to HTTPProtocol for consistency with existing HTTPClient and HTTPHeader field naming in DialOptions. Constants are also renamed: - ProtocolHTTP1 → HTTPProtocol1 - ProtocolHTTP2 → HTTPProtocol2 - ProtocolAcceptAny → HTTPProtocolAny
Simplify the TLS example by requiring -cert and -key flags instead of auto-generating a self-signed certificate. This reduces complexity and keeps the example focused on HTTP/2 WebSocket usage.
Add resp.ProtoMajor check in verifyServerResponseH1 for consistency with verifyServerResponseH2 which already performs this check. Refs #539
3f2f9af to
fd5e7f7
Compare
verifyServerResponseH1 rejects responses whose ProtoMajor is not 1. These mock responses left it at 0, so each dial failed before the check its test targets. Responses from net/http always set ProtoMajor.
Without the check, an HTTP/3 response goes on to the Upgrade header checks, so Dial could treat it as an HTTP/1.1 upgrade.
http2.Transport.DialTLS is deprecated, and staticcheck fails lint on it. DialTLSContext also lets the transport cancel a dial when the request's context ends.
http2.Transport.DialTLS is deprecated, and staticcheck fails lint on it.
Custom RoundTrippers that build the 101 response by hand may leave ProtoMajor at 0. Rejecting those would break dials that work today, while any other version except 1 is still rejected.
This reverts 3866540. With an unset ProtoMajor accepted, these tests again cover the hand-built responses that custom RoundTrippers return.
govulncheck reports 8 vulnerabilities in x/net v0.44.0 and x/text v0.29.0 that the HTTP/2 example calls. internal/thirdparty uses the same x/net for the HTTP/2 tests and moves with it. Every x/net release with the fixes requires go 1.26.0, so both go directives change from 1.26 to 1.26.0.
x/net deprecates http2.ConfigureServer and h2c. Since Go 1.24, net/http serves HTTP/2 extended CONNECT and cleartext HTTP/2 itself, and it reads GODEBUG=http2xconnect the same way. The tests now run Accept behind the server most users run.
x/net deprecates http2.Transport, but Go 1.26's net/http Transport fails the extended CONNECT request with "invalid header field name ":protocol"", so the example client and the tests still need it.
7f2b1c4 renamed the option from Protocol to HTTPProtocol, but doc.go and README.md still named Protocol.
On Go 1.27, x/net's http2.Transport passes requests to net/http, which fails the extended CONNECT request with "invalid header field name ":protocol"". Building with -tags http2legacy restores x/net's own client, and the example client then connects.
RFC 8441 section 5 drops Sec-WebSocket-Accept, and Kestrel, Jetty, axum, libwebsockets and nghttpx send none, so Dial failed against them. An accept that is present still has to match our key. The key is still sent, because proxies such as Envoy forward it to HTTP/1.1 servers.
Chrome and Firefox send no Sec-WebSocket-Key on an HTTP/2 extended
CONNECT, as RFC 8441 section 5 specifies, so Accept rejected them with
400. A key that is present is still answered with Sec-WebSocket-Accept,
because proxies such as Envoy forward an HTTP/1.1 client's key
without generating the accept that client checks.
Adds experimental WebSocket over HTTP/2 (RFC 8441 extended CONNECT). It is opt-in on both sides through the new
HTTPProtocolfield inAcceptOptionsandDialOptions. The zero value keeps today's HTTP/1.1 behavior, and on Wasm the field does nothing.AcceptwithHTTPProtocol2accepts only extended CONNECT, and withHTTPProtocolAnyit accepts both. The process must run withGODEBUG=http2xconnect=1, because Go has disabled extended CONNECT by default since Go 1.24 (net/http: do not enable HTTP/2 extended connect by default golang/go#71128). CL 776300 proposes a per-server option instead (net/http: add support for SETTINGS_ENABLE_CONNECT_PROTOCOL golang/go#53208). Over HTTP/2,Acceptdoes not requireSec-WebSocket-Key, because Chrome and Firefox send none (RFC 8441 §5). It answers a key that is present withSec-WebSocket-Accept, for HTTP/1.1 clients behind proxies such as Envoy.DialwithHTTPProtocol2needs anhttp.Clientthat usesgolang.org/x/net/http2.Transport, becausenet/http's own client rejects the:protocolpseudo-header. On Go 1.27 the program must also be built with-tags http2legacy, becausex/net's transport otherwise hands requests tonet/http.Dialcannot detect whether a transport supports HTTP/2, so the caller chooses the protocol.Dialstill sends a key over HTTP/2, for HTTP/1.1 servers behind proxies, and checksSec-WebSocket-Acceptonly when the server sends one.The HTTP/2 tests are in
internal/thirdparty/http2, because they importx/net.ci/test.shruns them withGODEBUG=http2xconnect=0and with=1, and adds their coverage to the main report. A runnable example is ininternal/examples/http2.Dialnow also rejects an HTTP/1.1 handshake response with aProtoMajorother than 0 or 1 (#550), so an HTTP/3 response is not treated as an upgrade. HTTP/2 benchmarks are not included.Closes #4
Decision log
ProtoMajor0 is accepted: custom RoundTrippers that build the response by hand may leave it unset, and rejecting them would break dials that work today.net/http(Server.Protocols, Go 1.24+), notx/net'sConfigureServerandh2c, whichx/netdeprecates. Thex/netclient stays, with//lint:file-ignore SA1019, becausenet/httphas no way to send:protocolyet.golang.org/x/net(v0.62.0) andgolang.org/x/text(v0.43.0) are bumped in the internal modules to clear govulncheck. Thosex/netreleases requirego 1.26.0, so both internalgodirectives say1.26.0.-tags http2legacyand passes with it.