Skip to content

proposal: net/http: #73297

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
turettn opened this issue Apr 9, 2025 · 1 comment
Closed

proposal: net/http: #73297

turettn opened this issue Apr 9, 2025 · 1 comment
Labels
LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool Proposal
Milestone

Comments

@turettn
Copy link

turettn commented Apr 9, 2025

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.

@gopherbot gopherbot added this to the Proposal milestone Apr 9, 2025
@gabyhelp gabyhelp added the LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool label Apr 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool Proposal
Projects
None yet
Development

No branches or pull requests

4 participants