Source file src/pkg/vendor/golang.org/x/net/http/httpproxy/proxy.go
1
2
3
4
5
6
7
8
9
10
11 package httpproxy
12
13 import (
14 "errors"
15 "fmt"
16 "net"
17 "net/url"
18 "os"
19 "strings"
20 "unicode/utf8"
21
22 "golang.org/x/net/idna"
23 )
24
25
26
27 type Config struct {
28
29
30
31
32 HTTPProxy string
33
34
35
36
37 HTTPSProxy string
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 NoProxy string
53
54
55
56
57
58
59
60 CGI bool
61 }
62
63
64 type config struct {
65
66 Config
67
68
69 httpsProxy *url.URL
70
71
72 httpProxy *url.URL
73
74
75
76 ipMatchers []matcher
77
78
79
80 domainMatchers []matcher
81 }
82
83
84
85
86
87
88
89
90
91 func FromEnvironment() *Config {
92 return &Config{
93 HTTPProxy: getEnvAny("HTTP_PROXY", "http_proxy"),
94 HTTPSProxy: getEnvAny("HTTPS_PROXY", "https_proxy"),
95 NoProxy: getEnvAny("NO_PROXY", "no_proxy"),
96 CGI: os.Getenv("REQUEST_METHOD") != "",
97 }
98 }
99
100 func getEnvAny(names ...string) string {
101 for _, n := range names {
102 if val := os.Getenv(n); val != "" {
103 return val
104 }
105 }
106 return ""
107 }
108
109
110
111
112
113
114
115
116
117
118
119 func (cfg *Config) ProxyFunc() func(reqURL *url.URL) (*url.URL, error) {
120
121 cfg1 := &config{
122 Config: *cfg,
123 }
124 cfg1.init()
125 return cfg1.proxyForURL
126 }
127
128 func (cfg *config) proxyForURL(reqURL *url.URL) (*url.URL, error) {
129 var proxy *url.URL
130 if reqURL.Scheme == "https" {
131 proxy = cfg.httpsProxy
132 }
133 if proxy == nil {
134 proxy = cfg.httpProxy
135 if proxy != nil && cfg.CGI {
136 return nil, errors.New("refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy")
137 }
138 }
139 if proxy == nil {
140 return nil, nil
141 }
142 if !cfg.useProxy(canonicalAddr(reqURL)) {
143 return nil, nil
144 }
145
146 return proxy, nil
147 }
148
149 func parseProxy(proxy string) (*url.URL, error) {
150 if proxy == "" {
151 return nil, nil
152 }
153
154 proxyURL, err := url.Parse(proxy)
155 if err != nil ||
156 (proxyURL.Scheme != "http" &&
157 proxyURL.Scheme != "https" &&
158 proxyURL.Scheme != "socks5") {
159
160
161
162 if proxyURL, err := url.Parse("http://" + proxy); err == nil {
163 return proxyURL, nil
164 }
165 }
166 if err != nil {
167 return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err)
168 }
169 return proxyURL, nil
170 }
171
172
173
174
175 func (cfg *config) useProxy(addr string) bool {
176 if len(addr) == 0 {
177 return true
178 }
179 host, port, err := net.SplitHostPort(addr)
180 if err != nil {
181 return false
182 }
183 if host == "localhost" {
184 return false
185 }
186 ip := net.ParseIP(host)
187 if ip != nil {
188 if ip.IsLoopback() {
189 return false
190 }
191 }
192
193 addr = strings.ToLower(strings.TrimSpace(host))
194
195 if ip != nil {
196 for _, m := range cfg.ipMatchers {
197 if m.match(addr, port, ip) {
198 return false
199 }
200 }
201 }
202 for _, m := range cfg.domainMatchers {
203 if m.match(addr, port, ip) {
204 return false
205 }
206 }
207 return true
208 }
209
210 func (c *config) init() {
211 if parsed, err := parseProxy(c.HTTPProxy); err == nil {
212 c.httpProxy = parsed
213 }
214 if parsed, err := parseProxy(c.HTTPSProxy); err == nil {
215 c.httpsProxy = parsed
216 }
217
218 for _, p := range strings.Split(c.NoProxy, ",") {
219 p = strings.ToLower(strings.TrimSpace(p))
220 if len(p) == 0 {
221 continue
222 }
223
224 if p == "*" {
225 c.ipMatchers = []matcher{allMatch{}}
226 c.domainMatchers = []matcher{allMatch{}}
227 return
228 }
229
230
231 if _, pnet, err := net.ParseCIDR(p); err == nil {
232 c.ipMatchers = append(c.ipMatchers, cidrMatch{cidr: pnet})
233 continue
234 }
235
236
237 phost, pport, err := net.SplitHostPort(p)
238 if err == nil {
239 if len(phost) == 0 {
240
241 continue
242 }
243 if phost[0] == '[' && phost[len(phost)-1] == ']' {
244 phost = phost[1 : len(phost)-1]
245 }
246 } else {
247 phost = p
248 }
249
250 if pip := net.ParseIP(phost); pip != nil {
251 c.ipMatchers = append(c.ipMatchers, ipMatch{ip: pip, port: pport})
252 continue
253 }
254
255 if len(phost) == 0 {
256
257 continue
258 }
259
260
261
262
263
264 if strings.HasPrefix(phost, "*.") {
265 phost = phost[1:]
266 }
267 matchHost := false
268 if phost[0] != '.' {
269 matchHost = true
270 phost = "." + phost
271 }
272 c.domainMatchers = append(c.domainMatchers, domainMatch{host: phost, port: pport, matchHost: matchHost})
273 }
274 }
275
276 var portMap = map[string]string{
277 "http": "80",
278 "https": "443",
279 "socks5": "1080",
280 }
281
282
283 func canonicalAddr(url *url.URL) string {
284 addr := url.Hostname()
285 if v, err := idnaASCII(addr); err == nil {
286 addr = v
287 }
288 port := url.Port()
289 if port == "" {
290 port = portMap[url.Scheme]
291 }
292 return net.JoinHostPort(addr, port)
293 }
294
295
296
297 func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
298
299 func idnaASCII(v string) (string, error) {
300
301
302
303
304
305
306
307
308
309 if isASCII(v) {
310 return v, nil
311 }
312 return idna.Lookup.ToASCII(v)
313 }
314
315 func isASCII(s string) bool {
316 for i := 0; i < len(s); i++ {
317 if s[i] >= utf8.RuneSelf {
318 return false
319 }
320 }
321 return true
322 }
323
324
325 type matcher interface {
326
327
328 match(host, port string, ip net.IP) bool
329 }
330
331
332 type allMatch struct{}
333
334 func (a allMatch) match(host, port string, ip net.IP) bool {
335 return true
336 }
337
338 type cidrMatch struct {
339 cidr *net.IPNet
340 }
341
342 func (m cidrMatch) match(host, port string, ip net.IP) bool {
343 return m.cidr.Contains(ip)
344 }
345
346 type ipMatch struct {
347 ip net.IP
348 port string
349 }
350
351 func (m ipMatch) match(host, port string, ip net.IP) bool {
352 if m.ip.Equal(ip) {
353 return m.port == "" || m.port == port
354 }
355 return false
356 }
357
358 type domainMatch struct {
359 host string
360 port string
361
362 matchHost bool
363 }
364
365 func (m domainMatch) match(host, port string, ip net.IP) bool {
366 if strings.HasSuffix(host, m.host) || (m.matchHost && host == m.host[1:]) {
367 return m.port == "" || m.port == port
368 }
369 return false
370 }
371
View as plain text