diff options
author | Brad Fitzpatrick <bradfitz@golang.org> | 2018-07-09 17:30:10 +0000 |
---|---|---|
committer | Brad Fitzpatrick <bradfitz@golang.org> | 2018-07-09 19:35:21 +0000 |
commit | 4da84adc0c55d92f20b761d24a6ec0508e55dc7c (patch) | |
tree | a62fa80f57772a287e2f3ab097184d91e1dabe73 /src/vendor | |
parent | 1da7f1e2ee6eea4ee22fc943e2aed41778ea6fbb (diff) | |
download | go-4da84adc0c55d92f20b761d24a6ec0508e55dc7c.tar.gz go-4da84adc0c55d92f20b761d24a6ec0508e55dc7c.zip |
vendor: update vendored x/net/http/httpproxy
This updates x/net/http/httpproxy to git rev c21de06a for:
http/httpproxy: support CIDR notation and ports with NO_PROXY
https://golang.org/cl/115255
Fixes #16704
Change-Id: Ic96a0a36828779f88e68cd715bd076f36fd45e7a
Reviewed-on: https://go-review.googlesource.com/122655
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Michael Fraenkel <michael.fraenkel@gmail.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/vendor')
3 files changed, 241 insertions, 51 deletions
diff --git a/src/vendor/golang_org/x/net/http/httpproxy/export_test.go b/src/vendor/golang_org/x/net/http/httpproxy/export_test.go index 36b29d2db6..5d30018fbd 100644 --- a/src/vendor/golang_org/x/net/http/httpproxy/export_test.go +++ b/src/vendor/golang_org/x/net/http/httpproxy/export_test.go @@ -4,4 +4,10 @@ package httpproxy -var ExportUseProxy = (*Config).useProxy +func ExportUseProxy(cfg *Config, host string) bool { + cfg1 := &config{ + Config: *cfg, + } + cfg1.init() + return cfg1.useProxy(host) +} diff --git a/src/vendor/golang_org/x/net/http/httpproxy/proxy.go b/src/vendor/golang_org/x/net/http/httpproxy/proxy.go index f82748d208..0409f4340c 100644 --- a/src/vendor/golang_org/x/net/http/httpproxy/proxy.go +++ b/src/vendor/golang_org/x/net/http/httpproxy/proxy.go @@ -37,13 +37,18 @@ type Config struct { HTTPSProxy string // NoProxy represents the NO_PROXY or no_proxy environment - // variable. It specifies URLs that should be excluded from - // proxying as a comma-separated list of domain names or a - // single asterisk (*) to indicate that no proxying should be - // done. A domain name matches that name and all subdomains. A - // domain name with a leading "." matches subdomains only. For - // example "foo.com" matches "foo.com" and "bar.foo.com"; - // ".y.com" matches "x.y.com" but not "y.com". + // variable. It specifies a string that contains comma-separated values + // specifying hosts that should be excluded from proxying. Each value is + // represented by an IP address prefix (1.2.3.4), an IP address prefix in + // CIDR notation (1.2.3.4/8), a domain name, or a special DNS label (*). + // An IP address prefix and domain name can also include a literal port + // number (1.2.3.4:80). + // A domain name matches that name and all subdomains. A domain name with + // a leading "." matches subdomains only. For example "foo.com" matches + // "foo.com" and "bar.foo.com"; ".y.com" matches "x.y.com" but not "y.com". + // A single asterisk (*) indicates that no proxying should be done. + // A best effort is made to parse the string and errors are + // ignored. NoProxy string // CGI holds whether the current process is running @@ -55,6 +60,26 @@ type Config struct { CGI bool } +// config holds the parsed configuration for HTTP proxy settings. +type config struct { + // Config represents the original configuration as defined above. + Config + + // httpsProxy is the parsed URL of the HTTPSProxy if defined. + httpsProxy *url.URL + + // httpProxy is the parsed URL of the HTTPProxy if defined. + httpProxy *url.URL + + // ipMatchers represent all values in the NoProxy that are IP address + // prefixes or an IP address in CIDR notation. + ipMatchers []matcher + + // domainMatchers represent all values in the NoProxy that are a domain + // name or hostname & domain name + domainMatchers []matcher +} + // FromEnvironment returns a Config instance populated from the // environment variables HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the // lowercase versions thereof). HTTPS_PROXY takes precedence over @@ -92,29 +117,40 @@ func getEnvAny(names ...string) string { // 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 (cfg *Config) ProxyFunc() func(reqURL *url.URL) (*url.URL, error) { - // Prevent Config changes from affecting the function calculation. - // TODO Preprocess proxy settings for more efficient evaluation. - cfg1 := *cfg + // Preprocess the Config settings for more efficient evaluation. + cfg1 := &config{ + Config: *cfg, + } + cfg1.init() return cfg1.proxyForURL } -func (cfg *Config) proxyForURL(reqURL *url.URL) (*url.URL, error) { - var proxy string +func (cfg *config) proxyForURL(reqURL *url.URL) (*url.URL, error) { + var proxy *url.URL if reqURL.Scheme == "https" { - proxy = cfg.HTTPSProxy + proxy = cfg.httpsProxy } - if proxy == "" { - proxy = cfg.HTTPProxy - if proxy != "" && cfg.CGI { + if proxy == nil { + proxy = cfg.httpProxy + if proxy != nil && cfg.CGI { return nil, errors.New("refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy") } } - if proxy == "" { + if proxy == nil { return nil, nil } if !cfg.useProxy(canonicalAddr(reqURL)) { return nil, nil } + + return proxy, nil +} + +func parseProxy(proxy string) (*url.URL, error) { + if proxy == "" { + return nil, nil + } + proxyURL, err := url.Parse(proxy) if err != nil || (proxyURL.Scheme != "http" && @@ -136,58 +172,105 @@ func (cfg *Config) proxyForURL(reqURL *url.URL) (*url.URL, error) { // 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 (cfg *Config) useProxy(addr string) bool { +func (cfg *config) useProxy(addr string) bool { if len(addr) == 0 { return true } - host, _, err := net.SplitHostPort(addr) + host, port, err := net.SplitHostPort(addr) if err != nil { return false } if host == "localhost" { return false } - if ip := net.ParseIP(host); ip != nil { + ip := net.ParseIP(host) + if ip != nil { if ip.IsLoopback() { return false } } - noProxy := cfg.NoProxy - if noProxy == "*" { - return false + addr = strings.ToLower(strings.TrimSpace(host)) + + if ip != nil { + for _, m := range cfg.ipMatchers { + if m.match(addr, port, ip) { + return false + } + } } + for _, m := range cfg.domainMatchers { + if m.match(addr, port, ip) { + return false + } + } + return true +} - addr = strings.ToLower(strings.TrimSpace(addr)) - if hasPort(addr) { - addr = addr[:strings.LastIndex(addr, ":")] +func (c *config) init() { + if parsed, err := parseProxy(c.HTTPProxy); err == nil { + c.httpProxy = parsed + } + if parsed, err := parseProxy(c.HTTPSProxy); err == nil { + c.httpsProxy = parsed } - for _, p := range strings.Split(noProxy, ",") { + for _, p := range strings.Split(c.NoProxy, ",") { p = strings.ToLower(strings.TrimSpace(p)) if len(p) == 0 { continue } - if hasPort(p) { - p = p[:strings.LastIndex(p, ":")] + + if p == "*" { + c.ipMatchers = []matcher{allMatch{}} + c.domainMatchers = []matcher{allMatch{}} + return } - if addr == p { - return false + + // IPv4/CIDR, IPv6/CIDR + if _, pnet, err := net.ParseCIDR(p); err == nil { + c.ipMatchers = append(c.ipMatchers, cidrMatch{cidr: pnet}) + continue } - if len(p) == 0 { + + // IPv4:port, [IPv6]:port + phost, pport, err := net.SplitHostPort(p) + if err == nil { + if len(phost) == 0 { + // There is no host part, likely the entry is malformed; ignore. + continue + } + if phost[0] == '[' && phost[len(phost)-1] == ']' { + phost = phost[1 : len(phost)-1] + } + } else { + phost = p + } + // IPv4, IPv6 + if pip := net.ParseIP(phost); pip != nil { + c.ipMatchers = append(c.ipMatchers, ipMatch{ip: pip, port: pport}) + continue + } + + if len(phost) == 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 + + // domain.com or domain.com:80 + // foo.com matches bar.foo.com + // .domain.com or .domain.com:port + // *.domain.com or *.domain.com:port + if strings.HasPrefix(phost, "*.") { + phost = phost[1:] } - if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' { - // no_proxy "foo.com" matches "bar.foo.com" - return false + matchHost := false + if phost[0] != '.' { + matchHost = true + phost = "." + phost } + c.domainMatchers = append(c.domainMatchers, domainMatch{host: phost, port: pport, matchHost: matchHost}) } - return true } var portMap = map[string]string{ @@ -237,3 +320,51 @@ func isASCII(s string) bool { } return true } + +// matcher represents the matching rule for a given value in the NO_PROXY list +type matcher interface { + // match returns true if the host and optional port or ip and optional port + // are allowed + match(host, port string, ip net.IP) bool +} + +// allMatch matches on all possible inputs +type allMatch struct{} + +func (a allMatch) match(host, port string, ip net.IP) bool { + return true +} + +type cidrMatch struct { + cidr *net.IPNet +} + +func (m cidrMatch) match(host, port string, ip net.IP) bool { + return m.cidr.Contains(ip) +} + +type ipMatch struct { + ip net.IP + port string +} + +func (m ipMatch) match(host, port string, ip net.IP) bool { + if m.ip.Equal(ip) { + return m.port == "" || m.port == port + } + return false +} + +type domainMatch struct { + host string + port string + + matchHost bool +} + +func (m domainMatch) match(host, port string, ip net.IP) bool { + if strings.HasSuffix(host, m.host) || (m.matchHost && host == m.host[1:]) { + return m.port == "" || m.port == port + } + return false +} diff --git a/src/vendor/golang_org/x/net/http/httpproxy/proxy_test.go b/src/vendor/golang_org/x/net/http/httpproxy/proxy_test.go index fde2514832..8791f64bcd 100644 --- a/src/vendor/golang_org/x/net/http/httpproxy/proxy_test.go +++ b/src/vendor/golang_org/x/net/http/httpproxy/proxy_test.go @@ -16,6 +16,9 @@ import ( "golang_org/x/net/http/httpproxy" ) +// setHelper calls t.Helper() for Go 1.9+ (see go19_test.go) and does nothing otherwise. +var setHelper = func(t *testing.T) {} + type proxyForURLTest struct { cfg httpproxy.Config req string // URL to fetch; blank means "http://example.com" @@ -141,7 +144,7 @@ var proxyForURLTests = []proxyForURLTest{{ HTTPProxy: "proxy", }, req: "http://example.com/", - want: "<nil>", + want: "http://proxy", }, { cfg: httpproxy.Config{ NoProxy: "ample.com", @@ -166,7 +169,7 @@ var proxyForURLTests = []proxyForURLTest{{ }} func testProxyForURL(t *testing.T, tt proxyForURLTest) { - t.Helper() + setHelper(t) reqURLStr := tt.req if reqURLStr == "" { reqURLStr = "http://example.com" @@ -266,19 +269,36 @@ var UseProxyTests = []struct { {"[::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" + {"192.168.1.1", false}, // matches exact IPv4 + {"192.168.1.2", true}, // ports do not match + {"192.168.1.3", false}, // matches exact IPv4:port + {"192.168.1.4", true}, // no match + {"10.0.0.2", false}, // matches IPv4/CIDR + {"[2001:db8::52:0:1]", false}, // matches exact IPv6 + {"[2001:db8::52:0:2]", true}, // no match + {"[2001:db8::52:0:3]", false}, // matches exact [IPv6]:port + {"[2002:db8:a::123]", false}, // matches IPv6/CIDR + {"[fe80::424b:c8be:1643:a1b6]", true}, // no match + + {"barbaz.net", true}, // does not match as .barbaz.net + {"www.barbaz.net", false}, // does match as .barbaz.net + {"foobar.com", false}, // does match as foobar.com + {"www.foobar.com", false}, // match because NO_PROXY includes "foobar.com" + {"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, wrong domain + {"wildcard.io", true}, // does not match as *.wildcard.io + {"nested.wildcard.io", false}, // match as *.wildcard.io + {"awildcard.io", true}, // not a match because of '*' } +var noProxy = "foobar.com, .barbaz.net, *.wildcard.io, 192.168.1.1, 192.168.1.2:81, 192.168.1.3:80, 10.0.0.0/30, 2001:db8::52:0:1, [2001:db8::52:0:2]:443, [2001:db8::52:0:3]:80, 2002:db8:a::45/64" + func TestUseProxy(t *testing.T) { cfg := &httpproxy.Config{ - NoProxy: "foobar.com, .barbaz.net", + NoProxy: noProxy, } for _, test := range UseProxyTests { if httpproxy.ExportUseProxy(cfg, test.host+":80") != test.match { @@ -296,3 +316,36 @@ func TestInvalidNoProxy(t *testing.T) { t.Errorf("useProxy unexpected return; got false; want true") } } + +func TestAllNoProxy(t *testing.T) { + cfg := &httpproxy.Config{ + NoProxy: "*", + } + for _, test := range UseProxyTests { + if httpproxy.ExportUseProxy(cfg, test.host+":80") != false { + t.Errorf("useProxy(%v) = true, want false", test.host) + } + } +} + +func BenchmarkProxyForURL(b *testing.B) { + cfg := &httpproxy.Config{ + HTTPProxy: "http://proxy.example.org", + HTTPSProxy: "https://proxy.example.org", + NoProxy: noProxy, + } + for _, test := range UseProxyTests { + u, err := url.Parse("https://" + test.host + ":80") + if err != nil { + b.Fatalf("parsed failed: %s", test.host) + } + proxyFunc := cfg.ProxyFunc() + b.Run(test.host, func(b *testing.B) { + for n := 0; n < b.N; n++ { + if au, e := proxyFunc(u); e != nil && test.match == (au != nil) { + b.Errorf("useProxy(%v) = %v, want %v", test.host, !test.match, test.match) + } + } + }) + } +} |