proxy.go raw

   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 "golang.org/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  }
 150