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

     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  	"github.com/Andyfoo/golang/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  	// Issue 16405: don't use HTTP_PROXY in a CGI environment,
   116  	// where HTTP_PROXY can be attacker-controlled.
   117  	cfg: httpproxy.Config{
   118  		HTTPProxy: "http://10.1.2.3:8080",
   119  		CGI:       true,
   120  	},
   121  	want:    "<nil>",
   122  	wanterr: errors.New("refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy"),
   123  }, {
   124  	// HTTPS proxy is still used even in CGI environment.
   125  	// (perhaps dubious but it's the historical behaviour).
   126  	cfg: httpproxy.Config{
   127  		HTTPSProxy: "https://secure.proxy.tld",
   128  		CGI:        true,
   129  	},
   130  	req:  "https://secure.tld/",
   131  	want: "https://secure.proxy.tld",
   132  }, {
   133  	want: "<nil>",
   134  }, {
   135  	cfg: httpproxy.Config{
   136  		NoProxy:   "example.com",
   137  		HTTPProxy: "proxy",
   138  	},
   139  	req:  "http://example.com/",
   140  	want: "<nil>",
   141  }, {
   142  	cfg: httpproxy.Config{
   143  		NoProxy:   ".example.com",
   144  		HTTPProxy: "proxy",
   145  	},
   146  	req:  "http://example.com/",
   147  	want: "http://proxy",
   148  }, {
   149  	cfg: httpproxy.Config{
   150  		NoProxy:   "ample.com",
   151  		HTTPProxy: "proxy",
   152  	},
   153  	req:  "http://example.com/",
   154  	want: "http://proxy",
   155  }, {
   156  	cfg: httpproxy.Config{
   157  		NoProxy:   "example.com",
   158  		HTTPProxy: "proxy",
   159  	},
   160  	req:  "http://foo.example.com/",
   161  	want: "<nil>",
   162  }, {
   163  	cfg: httpproxy.Config{
   164  		NoProxy:   ".foo.com",
   165  		HTTPProxy: "proxy",
   166  	},
   167  	req:  "http://example.com/",
   168  	want: "http://proxy",
   169  }}
   170  
   171  func testProxyForURL(t *testing.T, tt proxyForURLTest) {
   172  	setHelper(t)
   173  	reqURLStr := tt.req
   174  	if reqURLStr == "" {
   175  		reqURLStr = "http://example.com"
   176  	}
   177  	reqURL, err := url.Parse(reqURLStr)
   178  	if err != nil {
   179  		t.Errorf("invalid URL %q", reqURLStr)
   180  		return
   181  	}
   182  	cfg := tt.cfg
   183  	proxyForURL := cfg.ProxyFunc()
   184  	url, err := proxyForURL(reqURL)
   185  	if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e {
   186  		t.Errorf("%v: got error = %q, want %q", tt, g, e)
   187  		return
   188  	}
   189  	if got := fmt.Sprintf("%s", url); got != tt.want {
   190  		t.Errorf("%v: got URL = %q, want %q", tt, url, tt.want)
   191  	}
   192  
   193  	// Check that changing the Config doesn't change the results
   194  	// of the functuon.
   195  	cfg = httpproxy.Config{}
   196  	url, err = proxyForURL(reqURL)
   197  	if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e {
   198  		t.Errorf("(after mutating config) %v: got error = %q, want %q", tt, g, e)
   199  		return
   200  	}
   201  	if got := fmt.Sprintf("%s", url); got != tt.want {
   202  		t.Errorf("(after mutating config) %v: got URL = %q, want %q", tt, url, tt.want)
   203  	}
   204  }
   205  
   206  func TestProxyForURL(t *testing.T) {
   207  	for _, tt := range proxyForURLTests {
   208  		testProxyForURL(t, tt)
   209  	}
   210  }
   211  
   212  func TestFromEnvironment(t *testing.T) {
   213  	os.Setenv("HTTP_PROXY", "httpproxy")
   214  	os.Setenv("HTTPS_PROXY", "httpsproxy")
   215  	os.Setenv("NO_PROXY", "noproxy")
   216  	os.Setenv("REQUEST_METHOD", "")
   217  	got := httpproxy.FromEnvironment()
   218  	want := httpproxy.Config{
   219  		HTTPProxy:  "httpproxy",
   220  		HTTPSProxy: "httpsproxy",
   221  		NoProxy:    "noproxy",
   222  	}
   223  	if *got != want {
   224  		t.Errorf("unexpected proxy config, got %#v want %#v", got, want)
   225  	}
   226  }
   227  
   228  func TestFromEnvironmentWithRequestMethod(t *testing.T) {
   229  	os.Setenv("HTTP_PROXY", "httpproxy")
   230  	os.Setenv("HTTPS_PROXY", "httpsproxy")
   231  	os.Setenv("NO_PROXY", "noproxy")
   232  	os.Setenv("REQUEST_METHOD", "PUT")
   233  	got := httpproxy.FromEnvironment()
   234  	want := httpproxy.Config{
   235  		HTTPProxy:  "httpproxy",
   236  		HTTPSProxy: "httpsproxy",
   237  		NoProxy:    "noproxy",
   238  		CGI:        true,
   239  	}
   240  	if *got != want {
   241  		t.Errorf("unexpected proxy config, got %#v want %#v", got, want)
   242  	}
   243  }
   244  
   245  func TestFromEnvironmentLowerCase(t *testing.T) {
   246  	os.Setenv("http_proxy", "httpproxy")
   247  	os.Setenv("https_proxy", "httpsproxy")
   248  	os.Setenv("no_proxy", "noproxy")
   249  	os.Setenv("REQUEST_METHOD", "")
   250  	got := httpproxy.FromEnvironment()
   251  	want := httpproxy.Config{
   252  		HTTPProxy:  "httpproxy",
   253  		HTTPSProxy: "httpsproxy",
   254  		NoProxy:    "noproxy",
   255  	}
   256  	if *got != want {
   257  		t.Errorf("unexpected proxy config, got %#v want %#v", got, want)
   258  	}
   259  }
   260  
   261  var UseProxyTests = []struct {
   262  	host  string
   263  	match bool
   264  }{
   265  	// Never proxy localhost:
   266  	{"localhost", false},
   267  	{"127.0.0.1", false},
   268  	{"127.0.0.2", false},
   269  	{"[::1]", false},
   270  	{"[::2]", true}, // not a loopback address
   271  
   272  	{"192.168.1.1", false},                // matches exact IPv4
   273  	{"192.168.1.2", true},                 // ports do not match
   274  	{"192.168.1.3", false},                // matches exact IPv4:port
   275  	{"192.168.1.4", true},                 // no match
   276  	{"10.0.0.2", false},                   // matches IPv4/CIDR
   277  	{"[2001:db8::52:0:1]", false},         // matches exact IPv6
   278  	{"[2001:db8::52:0:2]", true},          // no match
   279  	{"[2001:db8::52:0:3]", false},         // matches exact [IPv6]:port
   280  	{"[2002:db8:a::123]", false},          // matches IPv6/CIDR
   281  	{"[fe80::424b:c8be:1643:a1b6]", true}, // no match
   282  
   283  	{"barbaz.net", true},          // does not match as .barbaz.net
   284  	{"www.barbaz.net", false},     // does match as .barbaz.net
   285  	{"foobar.com", false},         // does match as foobar.com
   286  	{"www.foobar.com", false},     // match because NO_PROXY includes "foobar.com"
   287  	{"foofoobar.com", true},       // not match as a part of foobar.com
   288  	{"baz.com", true},             // not match as a part of barbaz.com
   289  	{"localhost.net", true},       // not match as suffix of address
   290  	{"local.localhost", true},     // not match as prefix as address
   291  	{"barbarbaz.net", true},       // not match, wrong domain
   292  	{"wildcard.io", true},         // does not match as *.wildcard.io
   293  	{"nested.wildcard.io", false}, // match as *.wildcard.io
   294  	{"awildcard.io", true},        // not a match because of '*'
   295  }
   296  
   297  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"
   298  
   299  func TestUseProxy(t *testing.T) {
   300  	cfg := &httpproxy.Config{
   301  		NoProxy: noProxy,
   302  	}
   303  	for _, test := range UseProxyTests {
   304  		if httpproxy.ExportUseProxy(cfg, test.host+":80") != test.match {
   305  			t.Errorf("useProxy(%v) = %v, want %v", test.host, !test.match, test.match)
   306  		}
   307  	}
   308  }
   309  
   310  func TestInvalidNoProxy(t *testing.T) {
   311  	cfg := &httpproxy.Config{
   312  		NoProxy: ":1",
   313  	}
   314  	ok := httpproxy.ExportUseProxy(cfg, "example.com:80") // should not panic
   315  	if !ok {
   316  		t.Errorf("useProxy unexpected return; got false; want true")
   317  	}
   318  }
   319  
   320  func TestAllNoProxy(t *testing.T) {
   321  	cfg := &httpproxy.Config{
   322  		NoProxy: "*",
   323  	}
   324  	for _, test := range UseProxyTests {
   325  		if httpproxy.ExportUseProxy(cfg, test.host+":80") != false {
   326  			t.Errorf("useProxy(%v) = true, want false", test.host)
   327  		}
   328  	}
   329  }
   330  
   331  func BenchmarkProxyForURL(b *testing.B) {
   332  	cfg := &httpproxy.Config{
   333  		HTTPProxy:  "http://proxy.example.org",
   334  		HTTPSProxy: "https://proxy.example.org",
   335  		NoProxy:    noProxy,
   336  	}
   337  	for _, test := range UseProxyTests {
   338  		u, err := url.Parse("https://" + test.host + ":80")
   339  		if err != nil {
   340  			b.Fatalf("parsed failed: %s", test.host)
   341  		}
   342  		proxyFunc := cfg.ProxyFunc()
   343  		b.Run(test.host, func(b *testing.B) {
   344  			for n := 0; n < b.N; n++ {
   345  				if au, e := proxyFunc(u); e != nil && test.match == (au != nil) {
   346  					b.Errorf("useProxy(%v) = %v, want %v", test.host, !test.match, test.match)
   347  				}
   348  			}
   349  		})
   350  	}
   351  }