From: Brad Fitzpatrick Date: Thu, 16 Jan 2014 18:25:45 +0000 (-0800) Subject: net/http: cache transport environment lookup X-Git-Tag: go1.3beta1~955 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=4deead7645fbb7302e0e86594445268085ded330;p=gostls13.git net/http: cache transport environment lookup Apparently this is expensive on Windows. Fixes #7020 R=golang-codereviews, alex.brainman, mattn.jp, dvyukov CC=golang-codereviews https://golang.org/cl/52840043 --- diff --git a/src/pkg/net/http/export_test.go b/src/pkg/net/http/export_test.go index 22b7f27968..0494991bde 100644 --- a/src/pkg/net/http/export_test.go +++ b/src/pkg/net/http/export_test.go @@ -63,4 +63,9 @@ func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler { return &timeoutHandler{handler, f, ""} } +func ResetCachedEnvironment() { + httpProxyEnv.reset() + noProxyEnv.reset() +} + var DefaultUserAgent = defaultUserAgent diff --git a/src/pkg/net/http/transport.go b/src/pkg/net/http/transport.go index f6871afacd..8fc7329e36 100644 --- a/src/pkg/net/http/transport.go +++ b/src/pkg/net/http/transport.go @@ -99,7 +99,7 @@ type Transport struct { // A nil URL and nil error are returned if no proxy is defined in the // environment, or a proxy should not be used for the given request. func ProxyFromEnvironment(req *Request) (*url.URL, error) { - proxy := getenvEitherCase("HTTP_PROXY") + proxy := httpProxyEnv.Get() if proxy == "" { return nil, nil } @@ -243,11 +243,42 @@ func (t *Transport) CancelRequest(req *Request) { // Private implementation past this point. // -func getenvEitherCase(k string) string { - if v := os.Getenv(strings.ToUpper(k)); v != "" { - return v +var ( + httpProxyEnv = &envOnce{ + names: []string{"HTTP_PROXY", "http_proxy"}, } - return os.Getenv(strings.ToLower(k)) + noProxyEnv = &envOnce{ + names: []string{"NO_PROXY", "no_proxy"}, + } +) + +// envOnce looks up an environment variable (optionally by multiple +// names) once. It mitigates expensive lookups on some platforms +// (e.g. Windows). +type envOnce struct { + names []string + once sync.Once + val string +} + +func (e *envOnce) Get() string { + e.once.Do(e.init) + return e.val +} + +func (e *envOnce) init() { + for _, n := range e.names { + e.val = os.Getenv(n) + if e.val != "" { + return + } + } +} + +// reset is used by tests +func (e *envOnce) reset() { + e.once = sync.Once{} + e.val = "" } func (t *Transport) connectMethodForRequest(treq *transportRequest) (*connectMethod, error) { @@ -550,7 +581,7 @@ func useProxy(addr string) bool { } } - no_proxy := getenvEitherCase("NO_PROXY") + no_proxy := noProxyEnv.Get() if no_proxy == "*" { return false } diff --git a/src/pkg/net/http/transport_test.go b/src/pkg/net/http/transport_test.go index 2ce2b6b518..cb54a7b419 100644 --- a/src/pkg/net/http/transport_test.go +++ b/src/pkg/net/http/transport_test.go @@ -1566,6 +1566,7 @@ func TestProxyFromEnvironment(t *testing.T) { for _, tt := range proxyFromEnvTests { os.Setenv("HTTP_PROXY", tt.env) os.Setenv("NO_PROXY", tt.noenv) + ResetCachedEnvironment() reqURL := tt.req if reqURL == "" { reqURL = "http://example.com"