This package provides a lightweight WebTransport-over-HTTP/3 server implementation in Go.
WebTransport (https://www.w3.org/TR/webtransport/) is a 21st century replacement for WebSockets. It's currently supported by Chrome, with support in other browsers coming shortly. It has several benefits over WebSockets:
It's built on top of HTTP/3 which uses QUIC (not TCP) as the transport. QUIC provides significant performance advantages over TCP, especially on congested networks.
Unlike WebSockets where you get just a single send/receive stream, WebTransport provides for multiple server-initiated or client-initiated unidirectional or bidirectional (reliable) streams, plus (unreliable) datagrams.
Unlike XHR/fetch calls, WebTransport provides a means (serverCertificateHashes) for a browser to communicate securely with endpoints where issuance of Web PKI certificates is not possible, for example, a device over the LAN which is only accessible via a private IP address and hence a self-signed certificate. This is potentially big for IoT.
Neither WebTransport nor HTTP/3 are standardized yet. We adhere to: draft-ietf-quic-http-34 and draft-ietf-webtrans-http3-02.
GoDoc has full documentation on webtransport-go
's public API.
Take a look at the webtransport-go-example repo for a complete server (and browser client) example.
You'll need to get a certificate for your server. Please read the comments at the top of Google's WebTransport server example in Python for detailed instructions.
First, set up WebTransport server parameters:
server := &webtransport.Server{
ListenAddr: ":4433",
TLSCert: webtransport.CertFile{Path: "cert.pem"},
TLSKey: webtransport.CertFile{Path: "cert.key"},
}
Then, set up an http.Handler
to accept a session, wait for an incoming bidirectional stream from the client, then (in this example) receive data and echo it back:
http.HandleFunc("/counter", func(rw http.ResponseWriter, r *http.Request) {
session := r.Body.(*webtransport.Session)
session.AcceptSession()
defer session.CloseSession()
// Wait for incoming bidi stream
s, err := session.AcceptStream()
if err != nil {
return
}
for {
buf := make([]byte, 1024)
n, err := s.Read(buf)
if err != nil {
break
}
fmt.Printf("Received from bidi stream %v: %s\n", s.StreamID(), buf[:n])
sendMsg := bytes.ToUpper(buf[:n])
fmt.Printf("Sending to bidi stream %v: %s\n", s.StreamID(), sendMsg)
s.Write(sendMsg)
}
}
Finally, start the server:
ctx, cancel := context.WithCancel(context.Background())
server.Run(ctx)
Here is a simple Chrome browser client to talk to this server. You can open a new browser tab and paste it into the Chrome DevTools console:
let transport = new WebTransport("https://localhost:4433/counter");
await transport.ready;
let stream = await transport.createBidirectionalStream();
let encoder = new TextEncoder();
let decoder = new TextDecoder();
let writer = stream.writable.getWriter();
let reader = stream.readable.getReader();
await writer.write(encoder.encode("Hello, world!"))
console.log(decoder.decode((await reader.read()).value));
transport.close();
This package was written by me, drawing on several sources of inspiration:
quic-go
package, which implements (an earlier draft spec for) WebTransport but is a heavyweight structural change for the package for which there doesn't appear any appetite from the package maintainer.quic-go
. This package doesn't use quic-go
's HTTP/3 server (only the underlying QUIC transport implementation), but webtransport-go
implements its own minimal HTTP/3 server as needed to support WebTransport.Instead, webtransport-go
implements the latest draft standards for all the supported protocols, is very lightweight, and doesn't depend on any non-standard Go package forks or WIP modules.
Provided under the MIT license.