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