k3s/vendor/github.com/lxc/lxd/shared/proxy.go
2019-11-10 04:05:59 +00:00

163 lines
3.3 KiB
Go

package shared
import (
"fmt"
"net"
"net/http"
"net/url"
"os"
"strings"
"sync"
)
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"},
}
)
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
}
}
}
// This is basically the same as golang's ProxyFromEnvironment, except it
// doesn't fall back to http_proxy when https_proxy isn't around, which is
// incorrect behavior. It still respects HTTP_PROXY, HTTPS_PROXY, and NO_PROXY.
func ProxyFromEnvironment(req *http.Request) (*url.URL, error) {
return ProxyFromConfig("", "", "")(req)
}
func ProxyFromConfig(httpsProxy string, httpProxy string, noProxy string) func(req *http.Request) (*url.URL, error) {
return func(req *http.Request) (*url.URL, error) {
var proxy, port string
var err error
switch req.URL.Scheme {
case "https":
proxy = httpsProxy
if proxy == "" {
proxy = httpsProxyEnv.Get()
}
port = ":443"
case "http":
proxy = httpProxy
if proxy == "" {
proxy = httpProxyEnv.Get()
}
port = ":80"
default:
return nil, fmt.Errorf("unknown scheme %s", req.URL.Scheme)
}
if proxy == "" {
return nil, nil
}
addr := req.URL.Host
if !hasPort(addr) {
addr = addr + port
}
use, err := useProxy(addr, noProxy)
if err != nil {
return nil, err
}
if !use {
return nil, nil
}
proxyURL, err := url.Parse(proxy)
if err != nil || !strings.HasPrefix(proxyURL.Scheme, "http") {
// 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
}
}
func hasPort(s string) bool {
return strings.LastIndex(s, ":") > strings.LastIndex(s, "]")
}
func useProxy(addr string, noProxy string) (bool, error) {
if noProxy == "" {
noProxy = noProxyEnv.Get()
}
if len(addr) == 0 {
return true, nil
}
host, _, err := net.SplitHostPort(addr)
if err != nil {
return false, nil
}
if host == "localhost" {
return false, nil
}
if ip := net.ParseIP(host); ip != nil {
if ip.IsLoopback() {
return false, nil
}
}
if noProxy == "*" {
return false, nil
}
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, nil
}
if p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:]) {
// noProxy ".foo.com" matches "bar.foo.com" or "foo.com"
return false, nil
}
if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' {
// noProxy "foo.com" matches "bar.foo.com"
return false, nil
}
}
return true, nil
}