proxy_test.go 9.2 KB

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