// Copyright 2017 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.
// Package httpproxy provides support for HTTP proxy determination// based on environment variables, as provided by net/http's// ProxyFromEnvironment function.//// The API is not subject to the Go 1 compatibility promise and may change at// any time.
package httpproxyimport ()// Config holds configuration for HTTP proxy settings. See// FromEnvironment for details.typeConfigstruct {// HTTPProxy represents the value of the HTTP_PROXY or // http_proxy environment variable. It will be used as the proxy // URL for HTTP requests unless overridden by NoProxy. HTTPProxy string// HTTPSProxy represents the HTTPS_PROXY or https_proxy // environment variable. It will be used as the proxy URL for // HTTPS requests unless overridden by NoProxy. HTTPSProxy string// NoProxy represents the NO_PROXY or no_proxy environment // 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 // as a CGI handler (FromEnvironment infers this from the // presence of a REQUEST_METHOD environment variable). // When this is set, ProxyForURL will return an error // when HTTPProxy applies, because a client could be // setting HTTP_PROXY maliciously. See https://golang.org/s/cgihttpproxy. 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).//// The environment values may be either a complete URL or a// "host[:port]", in which case the "http" scheme is assumed. An error// is returned if the value is a different form.func () *Config {return &Config{HTTPProxy: getEnvAny("HTTP_PROXY", "http_proxy"),HTTPSProxy: getEnvAny("HTTPS_PROXY", "https_proxy"),NoProxy: getEnvAny("NO_PROXY", "no_proxy"),CGI: os.Getenv("REQUEST_METHOD") != "", }}func getEnvAny( ...string) string {for , := range {if := os.Getenv(); != "" {return } }return""}// ProxyFunc returns a function that determines the proxy URL to use for// a given request URL. Changing the contents of cfg will not affect// proxy functions created earlier.//// 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, as// defined by NO_PROXY.//// As a special case, if req.URL.Host is "localhost" or a loopback address// (with or without a port number), then a nil URL and nil error will be returned.func ( *Config) () func( *url.URL) (*url.URL, error) {// Preprocess the Config settings for more efficient evaluation. := &config{Config: *, } .init()return .proxyForURL}func ( *config) ( *url.URL) (*url.URL, error) {var *url.URLif .Scheme == "https" { = .httpsProxy } elseif .Scheme == "http" { = .httpProxyif != nil && .CGI {returnnil, errors.New("refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy") } }if == nil {returnnil, nil }if !.useProxy(canonicalAddr()) {returnnil, nil }return , nil}func parseProxy( string) (*url.URL, error) {if == "" {returnnil, nil } , := url.Parse()if != nil || .Scheme == "" || .Host == "" {// 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 , := url.Parse("http://" + ); == nil {return , nil } }if != nil {returnnil, fmt.Errorf("invalid proxy address %q: %v", , ) }return , nil}// 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 ( *config) ( string) bool {iflen() == 0 {returntrue } , , := net.SplitHostPort()if != nil {returnfalse }if == "localhost" {returnfalse } := net.ParseIP()if != nil {if .IsLoopback() {returnfalse } } = strings.ToLower(strings.TrimSpace())if != nil {for , := range .ipMatchers {if .match(, , ) {returnfalse } } }for , := range .domainMatchers {if .match(, , ) {returnfalse } }returntrue}func ( *config) () {if , := parseProxy(.HTTPProxy); == nil { .httpProxy = }if , := parseProxy(.HTTPSProxy); == nil { .httpsProxy = }for , := rangestrings.Split(.NoProxy, ",") { = strings.ToLower(strings.TrimSpace())iflen() == 0 {continue }if == "*" { .ipMatchers = []matcher{allMatch{}} .domainMatchers = []matcher{allMatch{}}return }// IPv4/CIDR, IPv6/CIDRif , , := net.ParseCIDR(); == nil { .ipMatchers = append(.ipMatchers, cidrMatch{cidr: })continue }// IPv4:port, [IPv6]:port , , := net.SplitHostPort()if == nil {iflen() == 0 {// There is no host part, likely the entry is malformed; ignore.continue }if [0] == '[' && [len()-1] == ']' { = [1 : len()-1] } } else { = }// IPv4, IPv6if := net.ParseIP(); != nil { .ipMatchers = append(.ipMatchers, ipMatch{ip: , port: })continue }iflen() == 0 {// There is no host part, likely the entry is malformed; ignore.continue }// domain.com or domain.com:80 // foo.com matches bar.foo.com // .domain.com or .domain.com:port // *.domain.com or *.domain.com:portifstrings.HasPrefix(, "*.") { = [1:] } := falseif [0] != '.' { = true = "." + }if , := idnaASCII(); == nil { = } .domainMatchers = append(.domainMatchers, domainMatch{host: , port: , matchHost: }) }}var portMap = map[string]string{"http": "80","https": "443","socks5": "1080",}// canonicalAddr returns url.Host but always with a ":port" suffixfunc canonicalAddr( *url.URL) string { := .Hostname()if , := idnaASCII(); == nil { = } := .Port()if == "" { = portMap[.Scheme] }returnnet.JoinHostPort(, )}// Given a string of the form "host", "host:port", or "[ipv6::address]:port",// return true if the string includes a port.func hasPort( string) bool { returnstrings.LastIndex(, ":") > strings.LastIndex(, "]") }func idnaASCII( string) (string, error) {// TODO: Consider removing this check after verifying performance is okay. // Right now punycode verification, length checks, context checks, and the // permissible character tests are all omitted. It also prevents the ToASCII // call from salvaging an invalid IDN, when possible. As a result it may be // possible to have two IDNs that appear identical to the user where the // ASCII-only version causes an error downstream whereas the non-ASCII // version does not. // Note that for correct ASCII IDNs ToASCII will only do considerably more // work, but it will not cause an allocation.ifisASCII() {return , nil }returnidna.Lookup.ToASCII()}func isASCII( string) bool {for := 0; < len(); ++ {if [] >= utf8.RuneSelf {returnfalse } }returntrue}// matcher represents the matching rule for a given value in the NO_PROXY listtype 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 inputstype allMatch struct{}func ( allMatch) (, string, net.IP) bool {returntrue}type cidrMatch struct { cidr *net.IPNet}func ( cidrMatch) (, string, net.IP) bool {return .cidr.Contains()}type ipMatch struct { ip net.IP port string}func ( ipMatch) (, string, net.IP) bool {if .ip.Equal() {return .port == "" || .port == }returnfalse}type domainMatch struct { host string port string matchHost bool}func ( domainMatch) (, string, net.IP) bool {ifstrings.HasSuffix(, .host) || (.matchHost && == .host[1:]) {return .port == "" || .port == }returnfalse}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.