Description
net/http is hardcoded to work with crypto/tls, and it's currently impossible to use any other TLS stack. That meant that for example to develop the tls-tris fork of crypto/tls with TLS 1.3 support I had to do horrible things which made life pretty hard for other developers on the project.
@rsc mentioned he'd be interested in making it possible to use non-crypto/tls stacks with net/http. While I don't think we should necessarily encourage it (it's good that "what TLS stack do I want?" is not a question a Go developer has to ask themselves), I agree it would be nice to at least make it possible.
Here's a survey of the coupling points, and proposals on how to fix (most) of them.
/cc @agl @bradfitz @rsc @mholt
Server side
Request.TLS
has type*tls.ConnectionState
- I think it's acceptable to require external stacks to use the crypto/tls
ConnectionState
type
- I think it's acceptable to require external stacks to use the crypto/tls
serve
type-asserts*tls.Conn
to detect a TLS connection and callHandshake()
andConnectionState()
- Here we can type-assert to an (probably unexported) interface with those two methods
Server.TLSNextProto
has typemap[string]func(*Server, *tls.Conn, Handler)
- This is a problem: the function here will have to work with the concrete
*tls.Conn
, and there is code out there written with this assumption, including the standard http2 library
- This is a problem: the function here will have to work with the concrete
ServeTLS
andListenAndServeTLS
- These are helpers, no reason they shouldn't use crypto/tls
Server.TLSConfig
has type*tls.Config
- We can mostly ignore this, as it's only used for
ServeTLS
/ListenAndServeTLS
and for some auto-HTTP/2 logic
- We can mostly ignore this, as it's only used for
Client side
Client
assertstls.RecordHeaderError
to detect a mistaken plain HTTP response- External stacks can just use the crypto/tls error type
Response.TLS
has type*tls.ConnectionState
- As above, it's acceptable to require external stacks to use the crypto/tls
ConnectionState
type
- As above, it's acceptable to require external stacks to use the crypto/tls
Transport.TLSClientConfig
has type*tls.Config
- We can ignore this, as it can be overridden with
DialTLS
, so it's basically part of a helper
- We can ignore this, as it can be overridden with
- Auto-HTTP/2 is disabled by
DialTLS
being set- I think the client logic should be more like the server one, where the HTTP stack is configured if the TLS state is unknown (
t.DialTLS != nil
) or the TLS Config is available (t.TLSClientConfig != nil
) andh2
is enabled (this might be a separate issue)
- I think the client logic should be more like the server one, where the HTTP stack is configured if the TLS state is unknown (
Transport.TLSNextProto
has typemap[string]func(authority string, c *tls.Conn) RoundTripper
- Like
Server.TLSNextProto
, this is a problem
- Like
dialConn
type-asserts*tls.Conn
- Like above, we can instead assert an interface
Conclusion
The main problem are the two TLSNextProto
, for which I have no good solution. The auto-HTTP/2 logic might also need attention. Other parts can be either ignored as helpers, or discreetly fixed.