github.com/Andyfoo/golang/x/net@v0.0.0-20190901054642-57c1bf301704/proxy/proxy.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package proxy provides support for a variety of protocols to proxy network
     6  // data.
     7  package proxy // import "github.com/Andyfoo/golang/x/net/proxy"
     8  
     9  import (
    10  	"errors"
    11  	"net"
    12  	"net/url"
    13  	"os"
    14  	"sync"
    15  )
    16  
    17  // A Dialer is a means to establish a connection.
    18  // Custom dialers should also implement ContextDialer.
    19  type Dialer interface {
    20  	// Dial connects to the given address via the proxy.
    21  	Dial(network, addr string) (c net.Conn, err error)
    22  }
    23  
    24  // Auth contains authentication parameters that specific Dialers may require.
    25  type Auth struct {
    26  	User, Password string
    27  }
    28  
    29  // FromEnvironment returns the dialer specified by the proxy-related
    30  // variables in the environment and makes underlying connections
    31  // directly.
    32  func FromEnvironment() Dialer {
    33  	return FromEnvironmentUsing(Direct)
    34  }
    35  
    36  // FromEnvironmentUsing returns the dialer specify by the proxy-related
    37  // variables in the environment and makes underlying connections
    38  // using the provided forwarding Dialer (for instance, a *net.Dialer
    39  // with desired configuration).
    40  func FromEnvironmentUsing(forward Dialer) Dialer {
    41  	allProxy := allProxyEnv.Get()
    42  	if len(allProxy) == 0 {
    43  		return forward
    44  	}
    45  
    46  	proxyURL, err := url.Parse(allProxy)
    47  	if err != nil {
    48  		return forward
    49  	}
    50  	proxy, err := FromURL(proxyURL, forward)
    51  	if err != nil {
    52  		return forward
    53  	}
    54  
    55  	noProxy := noProxyEnv.Get()
    56  	if len(noProxy) == 0 {
    57  		return proxy
    58  	}
    59  
    60  	perHost := NewPerHost(proxy, forward)
    61  	perHost.AddFromString(noProxy)
    62  	return perHost
    63  }
    64  
    65  // proxySchemes is a map from URL schemes to a function that creates a Dialer
    66  // from a URL with such a scheme.
    67  var proxySchemes map[string]func(*url.URL, Dialer) (Dialer, error)
    68  
    69  // RegisterDialerType takes a URL scheme and a function to generate Dialers from
    70  // a URL with that scheme and a forwarding Dialer. Registered schemes are used
    71  // by FromURL.
    72  func RegisterDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error)) {
    73  	if proxySchemes == nil {
    74  		proxySchemes = make(map[string]func(*url.URL, Dialer) (Dialer, error))
    75  	}
    76  	proxySchemes[scheme] = f
    77  }
    78  
    79  // FromURL returns a Dialer given a URL specification and an underlying
    80  // Dialer for it to make network requests.
    81  func FromURL(u *url.URL, forward Dialer) (Dialer, error) {
    82  	var auth *Auth
    83  	if u.User != nil {
    84  		auth = new(Auth)
    85  		auth.User = u.User.Username()
    86  		if p, ok := u.User.Password(); ok {
    87  			auth.Password = p
    88  		}
    89  	}
    90  
    91  	switch u.Scheme {
    92  	case "socks5", "socks5h":
    93  		addr := u.Hostname()
    94  		port := u.Port()
    95  		if port == "" {
    96  			port = "1080"
    97  		}
    98  		return SOCKS5("tcp", net.JoinHostPort(addr, port), auth, forward)
    99  	}
   100  
   101  	// If the scheme doesn't match any of the built-in schemes, see if it
   102  	// was registered by another package.
   103  	if proxySchemes != nil {
   104  		if f, ok := proxySchemes[u.Scheme]; ok {
   105  			return f(u, forward)
   106  		}
   107  	}
   108  
   109  	return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
   110  }
   111  
   112  var (
   113  	allProxyEnv = &envOnce{
   114  		names: []string{"ALL_PROXY", "all_proxy"},
   115  	}
   116  	noProxyEnv = &envOnce{
   117  		names: []string{"NO_PROXY", "no_proxy"},
   118  	}
   119  )
   120  
   121  // envOnce looks up an environment variable (optionally by multiple
   122  // names) once. It mitigates expensive lookups on some platforms
   123  // (e.g. Windows).
   124  // (Borrowed from net/http/transport.go)
   125  type envOnce struct {
   126  	names []string
   127  	once  sync.Once
   128  	val   string
   129  }
   130  
   131  func (e *envOnce) Get() string {
   132  	e.once.Do(e.init)
   133  	return e.val
   134  }
   135  
   136  func (e *envOnce) init() {
   137  	for _, n := range e.names {
   138  		e.val = os.Getenv(n)
   139  		if e.val != "" {
   140  			return
   141  		}
   142  	}
   143  }
   144  
   145  // reset is used by tests
   146  func (e *envOnce) reset() {
   147  	e.once = sync.Once{}
   148  	e.val = ""
   149  }