Dialing HTTP/3

This package provides a http.RoundTripper implementation that can be used on the http.Client:

tr := &http3.Transport{
	TLSClientConfig: &tls.Config{},  // set a TLS client config, if desired
	QUICConfig:      &quic.Config{}, // QUIC connection options
}
defer tr.Close()
client := &http.Client{
	Transport: tr,
}

The http.Client can then be used to perform HTTP requests over HTTP/3.

Using a quic.Transport

To use a custom quic.Transport, the function used to dial new QUIC connections can be configured:

tr := quic.Transport{}
h3tr := &http3.Transport{
	TLSClientConfig: &tls.Config{},  // set a TLS client config, if desired 
	QUICConfig:      &quic.Config{}, // QUIC connection options 
	Dial: func(ctx context.Context, addr string, tlsConf *tls.Config, quicConf *quic.Config) (quic.EarlyConnection, error) {
		a, err := net.ResolveUDPAddr("udp", addr)
		if err != nil {
			return nil, err
		}
		return tr.DialEarly(ctx, a, tlsConf, quicConf)
	},
}

This gives the application more fine-grained control over the configuration of the quic.Transport.

Running Client and Server on the Same Socket

Since QUIC demultiplexes packets based on their connection IDs, it is possible allows running a QUIC server and client on the same UDP socket. This also works when using HTTP/3: HTTP requests can be sent from the same socket that a server is listening on.

To achieve this using this package, first initialize a single quic.Transport, and pass a quic.EarlyListner obtained from that transport to http3.Server.ServeListener, and use the DialEarly function of the transport as the Dial function for the http3.Transport.

Using 0-RTT

The use of 0-RTT was not anticipated by Go’s standard library, and Go doesn’t have 0-RTT support, neither in its crypto/tls nor in its net/http implementation (not even for TLS 1.3 on top of TCP). The http3 package therefore defines two new request methods: http3.MethodGet0RTT for GET requests and http3.MethodHead0RTT for HEAD requests.

⚠️
Support for the “Early-Data” header field, as well as the “Too Early” status code (425) defined in RFC 8470 is not yet implemented. See 📝 Future Work.

It is the application’s responsibility to make sure that it is actually safe to send a request in 0-RTT, as outlined in Security Properties of 0-RTT. Requests sent in 0-RTT can be replayed on a new connection by an on-path attacker, so 0-RTT should only be used for idempotent requests. RFC 8740 defines some guidance on how to use 0-RTT in HTTP.

tr := &http3.Transport{
	TLSClientConfig: &tls.Config{
		ClientSessionCache: tls.NewLRUClientSessionCache(100),
	},
}
req, err := http.NewRequest(http3.MethodGet0RTT, "https://my-server/path", nil)
// ... handle error ...
tr.RoundTrip(req)

The code snippet shows all the knobs that need to be turned to send a request in 0-RTT data:

  1. TLS session resumption must be enabled by configuring a tls.ClientSessionCache on the tls.Config.
  2. The request method needs to be set to http3.MethodGet0RTT.

📝 Future Work