-
Notifications
You must be signed in to change notification settings - Fork 18.6k
Description
Proposal Details
Summary
net/http.DefaultTransport is mutable, and many people clearly do not expect it to be mutated. This proposal suggests the addition of a new function to net/http to return the initial, un-mutated value of DefaultTransport.
Reasoning
In net/http, the DefaultTransport variable is configured to a non-trivial default, but can be mutated or replaced at run-time. This allows people to do all sorts of interesting things. For example:
type myTracer struct{
base http.RoundTripper
}
func (m *myTracer) RoundTrip(req *http.Request) (*http.Response, error) {
// Tracing stuff
resp, err := m.base.RoundTrip(req)
// Tracing stuff
return resp, err
}
func init() {
http.DefaultTransport = &myTracer{base: http.DefaultTransport}
}Whether or not this is a good idea is debatable, but the pattern is not un-common - a quick search turns up over 1000 people replacing DefaultTransport.
Problem 1: It's really hard to revert changes
Once DefaultTransport is mutated, the only real way to restore it to the defaults is to copy the code from the docs or source, and figure out how to avoid the non-exported function it includes.
Problem 2: People want access to the default transport
Additionally, when people do create their own net/http.Client objects with custom transports, they frequently want to start with the default transport that net/http provides - a common pattern is to cast http.DefaultTransport to *http.Transport, clone it, modify it, and create a client with it. A GitHub code search turns up over 7500 unchecked casts in this fashion. If something has previously replaced or modified http.DefaultTransport, these developers likely will not get what they're expecting. Sometimes they'll get a panic.
This seems to violate the principle of least surprise.
Proposed Change
I would like to add a function that returns a "truly default" *http.Transport to the net/http library.
func GetInitialDefaultTransport() *Transport {
return &Transport{
Proxy: ProxyFromEnvironment,
DialContext: defaultTransportDialContext(&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}),
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
}
var DefaultTransport RoundTripper = GetInitialDefaultTransport()This maintains consistently for all existing code, while giving new code a cleaner path forwards that avoids the pitfalls described above.