From 3b917b466cc540079b82bcd0fecdc46a2ba13644 Mon Sep 17 00:00:00 2001 From: sravan kilaru Date: Fri, 18 Sep 2026 02:41:05 -0500 Subject: [PATCH] oauthex: build defaultDiscoveryTransport lazily instead of at import (#1278) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #1276 ## What's happening `oauthex` builds `defaultDiscoveryTransport` as a package-level var, so it runs at import time. That path calls `proxyConfigured`, which invokes `http.DefaultTransport`'s proxy func (`http.ProxyFromEnvironment`). net/http reads the proxy env vars only once and caches the result, so this import-time call freezes the proxy config before `main()` can set `HTTPS_PROXY`. New in v1.8.0, which is why it's a regression — just importing `.../mcp` (which pulls in `oauthex`) triggers it, even with no use of the SDK. ## Fix Wrap the setup in `sync.OnceValue` so it runs on first use instead of at import. Still built once and reused. ## Testing (removed) - Ran the issue's repro against this branch via a local `replace`: prints `proxy=http://127.0.0.1:9` now, `proxy=` before. - Added a regression test in `oauthex`. - `go test ./...` passes. Co-authored-by: Guglielmo Colombo --- oauthex/oauth2.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/oauthex/oauth2.go b/oauthex/oauth2.go index caed92f..0e77149 100644 --- a/oauthex/oauth2.go +++ b/oauthex/oauth2.go @@ -16,6 +16,7 @@ import ( "net/netip" "net/url" "strings" + "sync" "syscall" "time" @@ -25,7 +26,15 @@ import ( const maxDiscoveryRedirects = 10 -var defaultDiscoveryTransport = newDiscoveryTransport(http.DefaultTransport) +// Build this on first use, not when the package loads. Building it checks the +// proxy via http.ProxyFromEnvironment, and net/http only reads the proxy +// environment once and then remembers it for the whole run. If that happened +// at import time, it would lock in the proxy setting before the app had a +// chance to set HTTPS_PROXY. sync.OnceValue waits until first use and still +// builds the transport just once. See #1276. +var defaultDiscoveryTransport = sync.OnceValue(func() http.RoundTripper { + return newDiscoveryTransport(http.DefaultTransport) +}) type httpStatusError struct { StatusCode int @@ -103,7 +112,7 @@ func checkHTTPSOrLoopback(addr string) error { } func newDiscoveryClient(c *http.Client) *http.Client { - transport := defaultDiscoveryTransport + transport := defaultDiscoveryTransport() if c != nil && c.Transport != nil { // a caller that has taken over dialing or TLS opts out of the checks if t, ok := c.Transport.(*http.Transport); ok && t.DialContext == nil && t.DialTLSContext == nil {