...

Source file src/golang.org/x/net/http/httpproxy/proxy_test.go

Documentation: golang.org/x/net/http/httpproxy

     1  // Copyright 2017 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 httpproxy_test
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"fmt"
    11  	"net/url"
    12  	"os"
    13  	"strings"
    14  	"testing"
    15  
    16  	"golang.org/x/net/http/httpproxy"
    17  )
    18  
    19  // setHelper calls t.Helper() for Go 1.9+ (see go19_test.go) and does nothing otherwise.
    20  var setHelper = func(t *testing.T) {}
    21  
    22  type proxyForURLTest struct {
    23  	cfg     httpproxy.Config
    24  	req     string // URL to fetch; blank means "http://example.com"
    25  	want    string
    26  	wanterr error
    27  }
    28  
    29  func (t proxyForURLTest) String() string {
    30  	var buf bytes.Buffer
    31  	space := func() {
    32  		if buf.Len() > 0 {
    33  			buf.WriteByte(' ')
    34  		}
    35  	}
    36  	if t.cfg.HTTPProxy != "" {
    37  		fmt.Fprintf(&buf, "http_proxy=%q", t.cfg.HTTPProxy)
    38  	}
    39  	if t.cfg.HTTPSProxy != "" {
    40  		space()
    41  		fmt.Fprintf(&buf, "https_proxy=%q", t.cfg.HTTPSProxy)
    42  	}
    43  	if t.cfg.NoProxy != "" {
    44  		space()
    45  		fmt.Fprintf(&buf, "no_proxy=%q", t.cfg.NoProxy)
    46  	}
    47  	req := "http://example.com"
    48  	if t.req != "" {
    49  		req = t.req
    50  	}
    51  	space()
    52  	fmt.Fprintf(&buf, "req=%q", req)
    53  	return strings.TrimSpace(buf.String())
    54  }
    55  
    56  var proxyForURLTests = []proxyForURLTest{{
    57  	cfg: httpproxy.Config{
    58  		HTTPProxy: "127.0.0.1:8080",
    59  	},
    60  	want: "http://127.0.0.1:8080",
    61  }, {
    62  	cfg: httpproxy.Config{
    63  		HTTPProxy: "cache.corp.example.com:1234",
    64  	},
    65  	want: "http://cache.corp.example.com:1234",
    66  }, {
    67  	cfg: httpproxy.Config{
    68  		HTTPProxy: "cache.corp.example.com",
    69  	},
    70  	want: "http://cache.corp.example.com",
    71  }, {
    72  	cfg: httpproxy.Config{
    73  		HTTPProxy: "https://cache.corp.example.com",
    74  	},
    75  	want: "https://cache.corp.example.com",
    76  }, {
    77  	cfg: httpproxy.Config{
    78  		HTTPProxy: "http://127.0.0.1:8080",
    79  	},
    80  	want: "http://127.0.0.1:8080",
    81  }, {
    82  	cfg: httpproxy.Config{
    83  		HTTPProxy: "https://127.0.0.1:8080",
    84  	},
    85  	want: "https://127.0.0.1:8080",
    86  }, {
    87  	cfg: httpproxy.Config{
    88  		HTTPProxy: "socks5://127.0.0.1",
    89  	},
    90  	want: "socks5://127.0.0.1",
    91  }, {
    92  	// Don't use secure for http
    93  	cfg: httpproxy.Config{
    94  		HTTPProxy:  "http.proxy.tld",
    95  		HTTPSProxy: "secure.proxy.tld",
    96  	},
    97  	req:  "http://insecure.tld/",
    98  	want: "http://http.proxy.tld",
    99  }, {
   100  	// Use secure for https.
   101  	cfg: httpproxy.Config{
   102  		HTTPProxy:  "http.proxy.tld",
   103  		HTTPSProxy: "secure.proxy.tld",
   104  	},
   105  	req:  "https://secure.tld/",
   106  	want: "http://secure.proxy.tld",
   107  }, {
   108  	cfg: httpproxy.Config{
   109  		HTTPProxy:  "http.proxy.tld",
   110  		HTTPSProxy: "https://secure.proxy.tld",
   111  	},
   112  	req:  "https://secure.tld/",
   113  	want: "https://secure.proxy.tld",
   114  }, {
   115  	cfg: httpproxy.Config{
   116  		HTTPProxy: "http.proxy.tld",
   117  	},
   118  	req:  "https://secure.tld/",
   119  	want: "<nil>",
   120  }, {
   121  	cfg: httpproxy.Config{
   122  		HTTPProxy: "http.proxy.tld",
   123  	},
   124  	req:  "ftp://insecure.tld/",
   125  	want: "<nil>",
   126  }, {
   127  	// Issue 16405: don't use HTTP_PROXY in a CGI environment,
   128  	// where HTTP_PROXY can be attacker-controlled.
   129  	cfg: httpproxy.Config{
   130  		HTTPProxy: "http://10.1.2.3:8080",
   131  		CGI:       true,
   132  	},
   133  	want:    "<nil>",
   134  	wanterr: errors.New("refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy"),
   135  }, {
   136  	// HTTPS proxy is still used even in CGI environment.
   137  	// (perhaps dubious but it's the historical behaviour).
   138  	cfg: httpproxy.Config{
   139  		HTTPSProxy: "https://secure.proxy.tld",
   140  		CGI:        true,
   141  	},
   142  	req:  "https://secure.tld/",
   143  	want: "https://secure.proxy.tld",
   144  }, {
   145  	want: "<nil>",
   146  }, {
   147  	cfg: httpproxy.Config{
   148  		NoProxy:   "example.com",
   149  		HTTPProxy: "proxy",
   150  	},
   151  	req:  "http://example.com/",
   152  	want: "<nil>",
   153  }, {
   154  	cfg: httpproxy.Config{
   155  		NoProxy:   ".example.com",
   156  		HTTPProxy: "proxy",
   157  	},
   158  	req:  "http://example.com/",
   159  	want: "http://proxy",
   160  }, {
   161  	cfg: httpproxy.Config{
   162  		NoProxy:   "ample.com",
   163  		HTTPProxy: "proxy",
   164  	},
   165  	req:  "http://example.com/",
   166  	want: "http://proxy",
   167  }, {
   168  	cfg: httpproxy.Config{
   169  		NoProxy:   "example.com",
   170  		HTTPProxy: "proxy",
   171  	},
   172  	req:  "http://foo.example.com/",
   173  	want: "<nil>",
   174  }, {
   175  	cfg: httpproxy.Config{
   176  		NoProxy:   ".foo.com",
   177  		HTTPProxy: "proxy",
   178  	},
   179  	req:  "http://example.com/",
   180  	want: "http://proxy",
   181  }, {
   182  	cfg: httpproxy.Config{
   183  		NoProxy:   ".示例.com",
   184  		HTTPProxy: "proxy",
   185  	},
   186  	req:  "http://www.示例.com",
   187  	want: "<nil>",
   188  }, {
   189  	cfg: httpproxy.Config{
   190  		NoProxy:   "xn--fsq092h.com",
   191  		HTTPProxy: "proxy",
   192  	},
   193  	req:  "http://www.示例.com",
   194  	want: "<nil>",
   195  }, {
   196  	cfg: httpproxy.Config{
   197  		NoProxy:   "示例.com",
   198  		HTTPProxy: "proxy",
   199  	},
   200  	req:  "http://www.xn--fsq092h.com",
   201  	want: "<nil>",
   202  },
   203  }
   204  
   205  func testProxyForURL(t *testing.T, tt proxyForURLTest) {
   206  	setHelper(t)
   207  	reqURLStr := tt.req
   208  	if reqURLStr == "" {
   209  		reqURLStr = "http://example.com"
   210  	}
   211  	reqURL, err := url.Parse(reqURLStr)
   212  	if err != nil {
   213  		t.Errorf("invalid URL %q", reqURLStr)
   214  		return
   215  	}
   216  	cfg := tt.cfg
   217  	proxyForURL := cfg.ProxyFunc()
   218  	url, err := proxyForURL(reqURL)
   219  	if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e {
   220  		t.Errorf("%v: got error = %q, want %q", tt, g, e)
   221  		return
   222  	}
   223  	if got := fmt.Sprintf("%s", url); got != tt.want {
   224  		t.Errorf("%v: got URL = %q, want %q", tt, url, tt.want)
   225  	}
   226  
   227  	// Check that changing the Config doesn't change the results
   228  	// of the functuon.
   229  	cfg = httpproxy.Config{}
   230  	url, err = proxyForURL(reqURL)
   231  	if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e {
   232  		t.Errorf("(after mutating config) %v: got error = %q, want %q", tt, g, e)
   233  		return
   234  	}
   235  	if got := fmt.Sprintf("%s", url); got != tt.want {
   236  		t.Errorf("(after mutating config) %v: got URL = %q, want %q", tt, url, tt.want)
   237  	}
   238  }
   239  
   240  func TestProxyForURL(t *testing.T) {
   241  	for _, tt := range proxyForURLTests {
   242  		testProxyForURL(t, tt)
   243  	}
   244  }
   245  
   246  func TestFromEnvironment(t *testing.T) {
   247  	os.Setenv("HTTP_PROXY", "httpproxy")
   248  	os.Setenv("HTTPS_PROXY", "httpsproxy")
   249  	os.Setenv("NO_PROXY", "noproxy")
   250  	os.Setenv("REQUEST_METHOD", "")
   251  	got := httpproxy.FromEnvironment()
   252  	want := httpproxy.Config{
   253  		HTTPProxy:  "httpproxy",
   254  		HTTPSProxy: "httpsproxy",
   255  		NoProxy:    "noproxy",
   256  	}
   257  	if *got != want {
   258  		t.Errorf("unexpected proxy config, got %#v want %#v", got, want)
   259  	}
   260  }
   261  
   262  func TestFromEnvironmentWithRequestMethod(t *testing.T) {
   263  	os.Setenv("HTTP_PROXY", "httpproxy")
   264  	os.Setenv("HTTPS_PROXY", "httpsproxy")
   265  	os.Setenv("NO_PROXY", "noproxy")
   266  	os.Setenv("REQUEST_METHOD", "PUT")
   267  	got := httpproxy.FromEnvironment()
   268  	want := httpproxy.Config{
   269  		HTTPProxy:  "httpproxy",
   270  		HTTPSProxy: "httpsproxy",
   271  		NoProxy:    "noproxy",
   272  		CGI:        true,
   273  	}
   274  	if *got != want {
   275  		t.Errorf("unexpected proxy config, got %#v want %#v", got, want)
   276  	}
   277  }
   278  
   279  func TestFromEnvironmentLowerCase(t *testing.T) {
   280  	os.Setenv("http_proxy", "httpproxy")
   281  	os.Setenv("https_proxy", "httpsproxy")
   282  	os.Setenv("no_proxy", "noproxy")
   283  	os.Setenv("REQUEST_METHOD", "")
   284  	got := httpproxy.FromEnvironment()
   285  	want := httpproxy.Config{
   286  		HTTPProxy:  "httpproxy",
   287  		HTTPSProxy: "httpsproxy",
   288  		NoProxy:    "noproxy",
   289  	}
   290  	if *got != want {
   291  		t.Errorf("unexpected proxy config, got %#v want %#v", got, want)
   292  	}
   293  }
   294  
   295  var UseProxyTests = []struct {
   296  	host  string
   297  	match bool
   298  }{
   299  	// Never proxy localhost:
   300  	{"localhost", false},
   301  	{"127.0.0.1", false},
   302  	{"127.0.0.2", false},
   303  	{"[::1]", false},
   304  	{"[::2]", true}, // not a loopback address
   305  
   306  	{"192.168.1.1", false},                // matches exact IPv4
   307  	{"192.168.1.2", true},                 // ports do not match
   308  	{"192.168.1.3", false},                // matches exact IPv4:port
   309  	{"192.168.1.4", true},                 // no match
   310  	{"10.0.0.2", false},                   // matches IPv4/CIDR
   311  	{"[2001:db8::52:0:1]", false},         // matches exact IPv6
   312  	{"[2001:db8::52:0:2]", true},          // no match
   313  	{"[2001:db8::52:0:3]", false},         // matches exact [IPv6]:port
   314  	{"[2002:db8:a::123]", false},          // matches IPv6/CIDR
   315  	{"[fe80::424b:c8be:1643:a1b6]", true}, // no match
   316  
   317  	{"barbaz.net", true},          // does not match as .barbaz.net
   318  	{"www.barbaz.net", false},     // does match as .barbaz.net
   319  	{"foobar.com", false},         // does match as foobar.com
   320  	{"www.foobar.com", false},     // match because NO_PROXY includes "foobar.com"
   321  	{"foofoobar.com", true},       // not match as a part of foobar.com
   322  	{"baz.com", true},             // not match as a part of barbaz.com
   323  	{"localhost.net", true},       // not match as suffix of address
   324  	{"local.localhost", true},     // not match as prefix as address
   325  	{"barbarbaz.net", true},       // not match, wrong domain
   326  	{"wildcard.io", true},         // does not match as *.wildcard.io
   327  	{"nested.wildcard.io", false}, // match as *.wildcard.io
   328  	{"awildcard.io", true},        // not a match because of '*'
   329  }
   330  
   331  var noProxy = "foobar.com, .barbaz.net, *.wildcard.io, 192.168.1.1, 192.168.1.2:81, 192.168.1.3:80, 10.0.0.0/30, 2001:db8::52:0:1, [2001:db8::52:0:2]:443, [2001:db8::52:0:3]:80, 2002:db8:a::45/64"
   332  
   333  func TestUseProxy(t *testing.T) {
   334  	cfg := &httpproxy.Config{
   335  		NoProxy: noProxy,
   336  	}
   337  	for _, test := range UseProxyTests {
   338  		if httpproxy.ExportUseProxy(cfg, test.host+":80") != test.match {
   339  			t.Errorf("useProxy(%v) = %v, want %v", test.host, !test.match, test.match)
   340  		}
   341  	}
   342  }
   343  
   344  func TestInvalidNoProxy(t *testing.T) {
   345  	cfg := &httpproxy.Config{
   346  		NoProxy: ":1",
   347  	}
   348  	ok := httpproxy.ExportUseProxy(cfg, "example.com:80") // should not panic
   349  	if !ok {
   350  		t.Errorf("useProxy unexpected return; got false; want true")
   351  	}
   352  }
   353  
   354  func TestAllNoProxy(t *testing.T) {
   355  	cfg := &httpproxy.Config{
   356  		NoProxy: "*",
   357  	}
   358  	for _, test := range UseProxyTests {
   359  		if httpproxy.ExportUseProxy(cfg, test.host+":80") != false {
   360  			t.Errorf("useProxy(%v) = true, want false", test.host)
   361  		}
   362  	}
   363  }
   364  
   365  func BenchmarkProxyForURL(b *testing.B) {
   366  	cfg := &httpproxy.Config{
   367  		HTTPProxy:  "http://proxy.example.org",
   368  		HTTPSProxy: "https://proxy.example.org",
   369  		NoProxy:    noProxy,
   370  	}
   371  	for _, test := range UseProxyTests {
   372  		u, err := url.Parse("https://" + test.host + ":80")
   373  		if err != nil {
   374  			b.Fatalf("parsed failed: %s", test.host)
   375  		}
   376  		proxyFunc := cfg.ProxyFunc()
   377  		b.Run(test.host, func(b *testing.B) {
   378  			for n := 0; n < b.N; n++ {
   379  				if au, e := proxyFunc(u); e != nil && test.match == (au != nil) {
   380  					b.Errorf("useProxy(%v) = %v, want %v", test.host, !test.match, test.match)
   381  				}
   382  			}
   383  		})
   384  	}
   385  }
   386  

View as plain text