-
Notifications
You must be signed in to change notification settings - Fork 60
Description
quic-go uses qtls which requires a match between its data structures and the standard library to match. This includes tls.Config
, tls.ConnectionState
, tls.ClientHelloInfos
, and some others. See https://github.com/quic-go/qtls-go1-20/blob/master/unsafe.go
It would be nice if we can reduce custom fields unless there is no other way. This can be done through contexts.
In commit 01665a5 ("crypto/tls: add CFControl parameter to Config"), we add a tls.Config#CFControl
field to propagate information from the TLS handshake (tls.Config#GetConfigForClient
) to a HTTP request handler (http.Request#TLS.CFControl
). To replace this with contexts:
- Set
http.Server#ConnContext
to add an empty structure to the context of the connection. - In
tls.Config#GetConfigForClient
, extract the previous structure pointer from the context throughtls.ClientHelloInfos#Context
and initialize it. Note that while modifications to the context itself are not propagated, any changes to the context value (a pointer to a structure) will be preserved. This is a crucial detail. - In a HTTP handler, extract the structure pointer again from the context through
http.Request#Context
.
To demonstrate the above conversion:
type contextKey struct{ name string }
var controlContextKey = &contextKey{"connection-metadata"}
type ConnState struct {
ExampleValue []byte
}
func main() {
srv := http.Server{
ConnContext: func(ctx context.Context, c net.Conn) context.Context {
// 1. allocate context value in the connection context
return context.WithValue(ctx, controlContextKey, &ConnState{})
},
}
cert, err := tls.LoadX509KeyPair("example-cert.pem", "example-key.pem")
if err != nil {
log.Fatal(err)
}
tlsConfig := &tls.Config{
GetConfigForClient: func(chi *tls.ClientHelloInfo) (*tls.Config, error) {
// 2. initialize state in context value
cs := chi.Context().Value(controlContextKey).(*ConnState)
cs.ExampleValue = []byte("hello world\n")
return &tls.Config{Certificates: []tls.Certificate{cert}}, nil
},
}
http.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) {
// 3. extract value from context
cs := req.Context().Value(controlContextKey).(*ConnState)
rw.Write(cs.ExampleValue)
})
// Setup HTTPS server
netListener, err := net.Listen("tcp", "localhost:4433")
if err != nil {
log.Fatal(err)
}
ln := tls.NewListener(netListener, tlsConfig)
if err := srv.Serve(ln); err != http.ErrServerClosed {
log.Fatal(err)
}
}
I'll work on changes to the cf
branch to remove the CFControl
field and investigate how other fields can similarly be removed.