Description
Proposal
I propose an unexported context.Context
field is added to the ClientHelloInfo
and CertificateRequestInfo
crypto/tls
types. We should also add a Context() context.Context
method to these types to access this context. Further, we should add a new method, HandshakeContext(context.Context) error
to the tls.Conn
struct, which will be used to propagate a context down the handshake call stack. The existing Handshake() error
would call to the new method with a context.Background()
context.
Standard library uses of (*tls.Conn).Handshake()
should be moved over to the new method, where appropriate. For example, it is not clear that it is appropriate to change the existing Read
and Write
methods on the *tls.Conn
to use the new handshake method, but in (*net/http.Server).serve
, it is clear that moving to the new function would enhance the request lifetime control in the function.
The context.Context
provided to HandshakeContext
would only be used for cancellation of the handshake itself, and once the handshake has completed, cancelling the context will have no effect. This is in line with the predecent set by (*net.Dialer).DialContext
.
Motivation
In recent Go releases, we've been able to use the handy GetCertificate
and GetClientCertificate
methods of the *tls.Config
to dynamically control certificate management in Go apps. This is fantastic, and has lead to things like https://godoc.org/golang.org/x/crypto/acme/autocert and https://github.com/johanbrandhorst/certify which are somewhat unique to the Go ecosystem.
Unfortunately, one glaring omission from the API is a connection context for cancellation and request scoped variable propagation. This means users have to implement custom timeouts or block their TLS connections forever in case of problems. It also means powerful observability tools like tracing and metrics that make use of the context cannot be used.
Interaction with net/http
net/http.Server
provide BaseContext
, which is used to set a global context for the duration of (*http.Server).Serve
, and ConnContext
, which is used on every new connection. The context passed to (*tls.Conn).HandshakeContext
would necessarily be a child of these contexts, as the existing Handshake
call is made after these contexts are created. See