diff options
| author | Roger Peppe <rogpeppe@gmail.com> | 2017-10-04 19:28:59 +0100 |
|---|---|---|
| committer | Brad Fitzpatrick <bradfitz@golang.org> | 2018-05-29 21:54:57 +0000 |
| commit | 210a9e0c7dbe9bc16522387e7a0c902d29a5f85c (patch) | |
| tree | d1eb5200a686afd44d93be3c129525c1429e03f9 /src/net/http | |
| parent | b28e33d371cf96f6f34bbc576b6fd95468ea25bc (diff) | |
| download | go-210a9e0c7dbe9bc16522387e7a0c902d29a5f85c.tar.xz | |
net/http: vendor x/net/http/httpproxy, use it in net/http
From x/net git rev c7086645de2.
Updates #16704
Change-Id: I4d642478fc69a52c973964845fca2fd402716e57
Reviewed-on: https://go-review.googlesource.com/68091
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/net/http')
| -rw-r--r-- | src/net/http/export_test.go | 4 | ||||
| -rw-r--r-- | src/net/http/proxy_test.go | 39 | ||||
| -rw-r--r-- | src/net/http/transport.go | 141 | ||||
| -rw-r--r-- | src/net/http/transport_test.go | 60 |
4 files changed, 60 insertions, 184 deletions
diff --git a/src/net/http/export_test.go b/src/net/http/export_test.go index 1825acd9be..e0ceb40021 100644 --- a/src/net/http/export_test.go +++ b/src/net/http/export_test.go @@ -76,9 +76,7 @@ func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler { } func ResetCachedEnvironment() { - httpProxyEnv.reset() - httpsProxyEnv.reset() - noProxyEnv.reset() + resetProxyConfig() } func (t *Transport) NumPendingRequestsForTesting() int { diff --git a/src/net/http/proxy_test.go b/src/net/http/proxy_test.go index f59a551f0a..eef0ca82f8 100644 --- a/src/net/http/proxy_test.go +++ b/src/net/http/proxy_test.go @@ -13,37 +13,6 @@ import ( // TODO(mattn): // test ProxyAuth -var UseProxyTests = []struct { - host string - match bool -}{ - // Never proxy localhost: - {"localhost", false}, - {"127.0.0.1", false}, - {"127.0.0.2", false}, - {"[::1]", false}, - {"[::2]", true}, // not a loopback address - - {"barbaz.net", false}, // match as .barbaz.net - {"foobar.com", false}, // have a port but match - {"foofoobar.com", true}, // not match as a part of foobar.com - {"baz.com", true}, // not match as a part of barbaz.com - {"localhost.net", true}, // not match as suffix of address - {"local.localhost", true}, // not match as prefix as address - {"barbarbaz.net", true}, // not match because NO_PROXY have a '.' - {"www.foobar.com", false}, // match because NO_PROXY includes "foobar.com" -} - -func TestUseProxy(t *testing.T) { - ResetProxyEnv() - os.Setenv("NO_PROXY", "foobar.com, .barbaz.net") - for _, test := range UseProxyTests { - if useProxy(test.host+":80") != test.match { - t.Errorf("useProxy(%v) = %v, want %v", test.host, !test.match, test.match) - } - } -} - var cacheKeysTests = []struct { proxy string scheme string @@ -74,14 +43,8 @@ func TestCacheKeys(t *testing.T) { } func ResetProxyEnv() { - for _, v := range []string{"HTTP_PROXY", "http_proxy", "NO_PROXY", "no_proxy"} { + for _, v := range []string{"HTTP_PROXY", "http_proxy", "NO_PROXY", "no_proxy", "REQUEST_METHOD"} { os.Unsetenv(v) } ResetCachedEnvironment() } - -func TestInvalidNoProxy(t *testing.T) { - ResetProxyEnv() - os.Setenv("NO_PROXY", ":1") - useProxy("example.com:80") // should not panic -} diff --git a/src/net/http/transport.go b/src/net/http/transport.go index cce88ca239..5bf9ff951f 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -29,6 +29,7 @@ import ( "time" "golang_org/x/net/http/httpguts" + "golang_org/x/net/http/httpproxy" ) // DefaultTransport is the default implementation of Transport and is @@ -272,39 +273,7 @@ func (t *Transport) onceSetNextProtoDefaults() { // As a special case, if req.URL.Host is "localhost" (with or without // a port number), then a nil URL and nil error will be returned. func ProxyFromEnvironment(req *Request) (*url.URL, error) { - var proxy string - if req.URL.Scheme == "https" { - proxy = httpsProxyEnv.Get() - } - if proxy == "" { - proxy = httpProxyEnv.Get() - if proxy != "" && os.Getenv("REQUEST_METHOD") != "" { - return nil, errors.New("net/http: refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy") - } - } - if proxy == "" { - return nil, nil - } - if !useProxy(canonicalAddr(req.URL)) { - return nil, nil - } - proxyURL, err := url.Parse(proxy) - if err != nil || - (proxyURL.Scheme != "http" && - proxyURL.Scheme != "https" && - proxyURL.Scheme != "socks5") { - // proxy was bogus. Try prepending "http://" to it and - // see if that parses correctly. If not, we fall - // through and complain about the original one. - if proxyURL, err := url.Parse("http://" + proxy); err == nil { - return proxyURL, nil - } - - } - if err != nil { - return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err) - } - return proxyURL, nil + return envProxyFunc()(req.URL) } // ProxyURL returns a proxy function (for use in a Transport) @@ -574,44 +543,25 @@ func (t *Transport) cancelRequest(req *Request, err error) { // var ( - httpProxyEnv = &envOnce{ - names: []string{"HTTP_PROXY", "http_proxy"}, - } - httpsProxyEnv = &envOnce{ - names: []string{"HTTPS_PROXY", "https_proxy"}, - } - noProxyEnv = &envOnce{ - names: []string{"NO_PROXY", "no_proxy"}, - } + // proxyConfigOnce guards proxyConfig + envProxyOnce sync.Once + envProxyFuncValue func(*url.URL) (*url.URL, error) ) -// 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 +// defaultProxyConfig returns a ProxyConfig value looked up +// from the environment. This mitigates expensive lookups +// on some platforms (e.g. Windows). +func envProxyFunc() func(*url.URL) (*url.URL, error) { + envProxyOnce.Do(func() { + envProxyFuncValue = httpproxy.FromEnvironment().ProxyFunc() + }) + return envProxyFuncValue } -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 = "" +// resetProxyConfig is used by tests. +func resetProxyConfig() { + envProxyOnce = sync.Once{} + envProxyFuncValue = nil } func (t *Transport) connectMethodForRequest(treq *transportRequest) (cm connectMethod, err error) { @@ -1235,63 +1185,6 @@ func (w persistConnWriter) Write(p []byte) (n int, err error) { return } -// useProxy reports whether requests to addr should use a proxy, -// according to the NO_PROXY or no_proxy environment variable. -// addr is always a canonicalAddr with a host and port. -func useProxy(addr string) bool { - if len(addr) == 0 { - return true - } - host, _, err := net.SplitHostPort(addr) - if err != nil { - return false - } - if host == "localhost" { - return false - } - if ip := net.ParseIP(host); ip != nil { - if ip.IsLoopback() { - return false - } - } - - noProxy := noProxyEnv.Get() - if noProxy == "*" { - return false - } - - addr = strings.ToLower(strings.TrimSpace(addr)) - if hasPort(addr) { - addr = addr[:strings.LastIndex(addr, ":")] - } - - for _, p := range strings.Split(noProxy, ",") { - p = strings.ToLower(strings.TrimSpace(p)) - if len(p) == 0 { - continue - } - if hasPort(p) { - p = p[:strings.LastIndex(p, ":")] - } - if addr == p { - return false - } - if len(p) == 0 { - // There is no host part, likely the entry is malformed; ignore. - continue - } - if p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:]) { - // no_proxy ".foo.com" matches "bar.foo.com" or "foo.com" - return false - } - if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' { - // no_proxy "foo.com" matches "bar.foo.com" - return false - } - } - return true -} - // connectMethod is the map key (in its String form) for keeping persistent // TCP connections alive for subsequent HTTP requests. // diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 5e35812c7b..57309bbac1 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -2381,7 +2381,7 @@ var proxyFromEnvTests = []proxyFromEnvTest{ // where HTTP_PROXY can be attacker-controlled. {env: "http://10.1.2.3:8080", reqmeth: "POST", want: "<nil>", - wanterr: errors.New("net/http: refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy")}, + wanterr: errors.New("refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy")}, {want: "<nil>"}, @@ -2392,28 +2392,50 @@ var proxyFromEnvTests = []proxyFromEnvTest{ {noenv: ".foo.com", req: "http://example.com/", env: "proxy", want: "http://proxy"}, } +func testProxyForRequest(t *testing.T, tt proxyFromEnvTest, proxyForRequest func(req *Request) (*url.URL, error)) { + t.Helper() + reqURL := tt.req + if reqURL == "" { + reqURL = "http://example.com" + } + req, _ := NewRequest("GET", reqURL, nil) + url, err := proxyForRequest(req) + if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e { + t.Errorf("%v: got error = %q, want %q", tt, g, e) + return + } + if got := fmt.Sprintf("%s", url); got != tt.want { + t.Errorf("%v: got URL = %q, want %q", tt, url, tt.want) + } +} + func TestProxyFromEnvironment(t *testing.T) { ResetProxyEnv() defer ResetProxyEnv() for _, tt := range proxyFromEnvTests { - os.Setenv("HTTP_PROXY", tt.env) - os.Setenv("HTTPS_PROXY", tt.httpsenv) - os.Setenv("NO_PROXY", tt.noenv) - os.Setenv("REQUEST_METHOD", tt.reqmeth) - ResetCachedEnvironment() - reqURL := tt.req - if reqURL == "" { - reqURL = "http://example.com" - } - req, _ := NewRequest("GET", reqURL, nil) - url, err := ProxyFromEnvironment(req) - if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e { - t.Errorf("%v: got error = %q, want %q", tt, g, e) - continue - } - if got := fmt.Sprintf("%s", url); got != tt.want { - t.Errorf("%v: got URL = %q, want %q", tt, url, tt.want) - } + testProxyForRequest(t, tt, func(req *Request) (*url.URL, error) { + os.Setenv("HTTP_PROXY", tt.env) + os.Setenv("HTTPS_PROXY", tt.httpsenv) + os.Setenv("NO_PROXY", tt.noenv) + os.Setenv("REQUEST_METHOD", tt.reqmeth) + ResetCachedEnvironment() + return ProxyFromEnvironment(req) + }) + } +} + +func TestProxyFromEnvironmentLowerCase(t *testing.T) { + ResetProxyEnv() + defer ResetProxyEnv() + for _, tt := range proxyFromEnvTests { + testProxyForRequest(t, tt, func(req *Request) (*url.URL, error) { + os.Setenv("http_proxy", tt.env) + os.Setenv("https_proxy", tt.httpsenv) + os.Setenv("no_proxy", tt.noenv) + os.Setenv("REQUEST_METHOD", tt.reqmeth) + ResetCachedEnvironment() + return ProxyFromEnvironment(req) + }) } } |
