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 httpproxy

import (
	
	
	
	
	
	
	

	
)
Config holds configuration for HTTP proxy settings. See FromEnvironment for details.
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.
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.
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.
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.
Config represents the original configuration as defined above.
httpsProxy is the parsed URL of the HTTPSProxy if defined.
httpProxy is the parsed URL of the HTTPProxy if defined.
ipMatchers represent all values in the NoProxy that are IP address prefixes or an IP address in CIDR notation.
domainMatchers represent all values in the NoProxy that are a domain name or hostname & domain name
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 HTTP_PROXY for https requests. 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 ( ...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" (with or without a port number), then a nil URL and nil error will be returned.
Preprocess the Config settings for more efficient evaluation.
	 := &config{
		Config: *,
	}
	.init()
	return .proxyForURL
}

func ( *config) ( *url.URL) (*url.URL, error) {
	var  *url.URL
	if .Scheme == "https" {
		 = .httpsProxy
	} else if .Scheme == "http" {
		 = .httpProxy
		if  != nil && .CGI {
			return nil, errors.New("refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy")
		}
	}
	if  == nil {
		return nil, nil
	}
	if !.useProxy(canonicalAddr()) {
		return nil, nil
	}

	return , nil
}

func ( string) (*url.URL, error) {
	if  == "" {
		return nil, nil
	}

	,  := url.Parse()
	if  != nil ||
		(.Scheme != "http" &&
			.Scheme != "https" &&
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 {
		return nil, 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 {
	if len() == 0 {
		return true
	}
	, ,  := net.SplitHostPort()
	if  != nil {
		return false
	}
	if  == "localhost" {
		return false
	}
	 := net.ParseIP()
	if  != nil {
		if .IsLoopback() {
			return false
		}
	}

	 = strings.ToLower(strings.TrimSpace())

	if  != nil {
		for ,  := range .ipMatchers {
			if .match(, , ) {
				return false
			}
		}
	}
	for ,  := range .domainMatchers {
		if .match(, , ) {
			return false
		}
	}
	return true
}

func ( *config) () {
	if ,  := parseProxy(.HTTPProxy);  == nil {
		.httpProxy = 
	}
	if ,  := parseProxy(.HTTPSProxy);  == nil {
		.httpsProxy = 
	}

	for ,  := range strings.Split(.NoProxy, ",") {
		 = strings.ToLower(strings.TrimSpace())
		if len() == 0 {
			continue
		}

		if  == "*" {
			.ipMatchers = []matcher{allMatch{}}
			.domainMatchers = []matcher{allMatch{}}
			return
		}
IPv4/CIDR, IPv6/CIDR
		if , ,  := net.ParseCIDR();  == nil {
			.ipMatchers = append(.ipMatchers, cidrMatch{cidr: })
			continue
		}
IPv4:port, [IPv6]:port
		, ,  := net.SplitHostPort()
		if  == nil {
There is no host part, likely the entry is malformed; ignore.
				continue
			}
			if [0] == '[' && [len()-1] == ']' {
				 = [1 : len()-1]
			}
		} else {
			 = 
IPv4, IPv6
		if  := net.ParseIP();  != nil {
			.ipMatchers = append(.ipMatchers, ipMatch{ip: , port: })
			continue
		}

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:port
		if strings.HasPrefix(, "*.") {
			 = [1:]
		}
		 := false
		if [0] != '.' {
			 = true
			 = "." + 
		}
		.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" suffix
func ( *url.URL) string {
	 := .Hostname()
	if ,  := idnaASCII();  == nil {
		 = 
	}
	 := .Port()
	if  == "" {
		 = portMap[.Scheme]
	}
	return net.JoinHostPort(, )
}
Given a string of the form "host", "host:port", or "[ipv6::address]:port", return true if the string includes a port.
func ( string) bool { return strings.LastIndex(, ":") > strings.LastIndex(, "]") }

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.
	if isASCII() {
		return , nil
	}
	return idna.Lookup.ToASCII()
}

func ( string) bool {
	for  := 0;  < len(); ++ {
		if [] >= utf8.RuneSelf {
			return false
		}
	}
	return true
}
matcher represents the matching rule for a given value in the NO_PROXY list
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 ( allMatch) (,  string,  net.IP) bool {
	return true
}

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 == 
	}
	return false
}

type domainMatch struct {
	host string
	port string

	matchHost bool
}

func ( domainMatch) (,  string,  net.IP) bool {
	if strings.HasSuffix(, .host) || (.matchHost &&  == .host[1:]) {
		return .port == "" || .port == 
	}
	return false