tshttpproxy_test.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package tshttpproxy
  4. import (
  5. "net/http"
  6. "net/url"
  7. "os"
  8. "runtime"
  9. "strings"
  10. "testing"
  11. "time"
  12. "tailscale.com/util/must"
  13. )
  14. func TestGetAuthHeaderNoResult(t *testing.T) {
  15. const proxyURL = "http://127.0.0.1:38274"
  16. u, err := url.Parse(proxyURL)
  17. if err != nil {
  18. t.Fatalf("can't parse %q: %v", proxyURL, err)
  19. }
  20. got, err := GetAuthHeader(u)
  21. if err != nil {
  22. t.Fatalf("can't get auth header value: %v", err)
  23. }
  24. if runtime.GOOS == "windows" && strings.HasPrefix(got, "Negotiate") {
  25. t.Logf("didn't get empty result, but got acceptable Windows Negotiate header")
  26. return
  27. }
  28. if got != "" {
  29. t.Fatalf("GetAuthHeader(%q) = %q; want empty string", proxyURL, got)
  30. }
  31. }
  32. func TestGetAuthHeaderBasicAuth(t *testing.T) {
  33. const proxyURL = "http://user:[email protected]:38274"
  34. const want = "Basic dXNlcjpwYXNzd29yZA=="
  35. u, err := url.Parse(proxyURL)
  36. if err != nil {
  37. t.Fatalf("can't parse %q: %v", proxyURL, err)
  38. }
  39. got, err := GetAuthHeader(u)
  40. if err != nil {
  41. t.Fatalf("can't get auth header value: %v", err)
  42. }
  43. if got != want {
  44. t.Fatalf("GetAuthHeader(%q) = %q; want %q", proxyURL, got, want)
  45. }
  46. }
  47. func TestProxyFromEnvironment_setNoProxyUntil(t *testing.T) {
  48. const fakeProxyEnv = "10.1.2.3:456"
  49. const fakeProxyFull = "http://" + fakeProxyEnv
  50. defer os.Setenv("HTTPS_PROXY", os.Getenv("HTTPS_PROXY"))
  51. os.Setenv("HTTPS_PROXY", fakeProxyEnv)
  52. req := &http.Request{URL: must.Get(url.Parse("https://example.com/"))}
  53. for i := range 3 {
  54. switch i {
  55. case 1:
  56. setNoProxyUntil(time.Minute)
  57. case 2:
  58. setNoProxyUntil(0)
  59. }
  60. got, err := ProxyFromEnvironment(req)
  61. if err != nil {
  62. t.Fatalf("[%d] ProxyFromEnvironment: %v", i, err)
  63. }
  64. if got == nil || got.String() != fakeProxyFull {
  65. t.Errorf("[%d] Got proxy %v; want %v", i, got, fakeProxyFull)
  66. }
  67. }
  68. }
  69. func TestSetSelfProxy(t *testing.T) {
  70. // Ensure we clean everything up at the end of our test
  71. t.Cleanup(func() {
  72. config = nil
  73. proxyFunc = nil
  74. })
  75. testCases := []struct {
  76. name string
  77. env map[string]string
  78. self []string
  79. wantHTTP string
  80. wantHTTPS string
  81. }{
  82. {
  83. name: "no self proxy",
  84. env: map[string]string{
  85. "HTTP_PROXY": "127.0.0.1:1234",
  86. "HTTPS_PROXY": "127.0.0.1:1234",
  87. },
  88. self: nil,
  89. wantHTTP: "127.0.0.1:1234",
  90. wantHTTPS: "127.0.0.1:1234",
  91. },
  92. {
  93. name: "skip proxies",
  94. env: map[string]string{
  95. "HTTP_PROXY": "127.0.0.1:1234",
  96. "HTTPS_PROXY": "127.0.0.1:5678",
  97. },
  98. self: []string{"127.0.0.1:1234", "127.0.0.1:5678"},
  99. wantHTTP: "", // skipped
  100. wantHTTPS: "", // skipped
  101. },
  102. {
  103. name: "localhost normalization of env var",
  104. env: map[string]string{
  105. "HTTP_PROXY": "localhost:1234",
  106. "HTTPS_PROXY": "[::1]:5678",
  107. },
  108. self: []string{"127.0.0.1:1234", "127.0.0.1:5678"},
  109. wantHTTP: "", // skipped
  110. wantHTTPS: "", // skipped
  111. },
  112. {
  113. name: "localhost normalization of addr",
  114. env: map[string]string{
  115. "HTTP_PROXY": "127.0.0.1:1234",
  116. "HTTPS_PROXY": "127.0.0.1:1234",
  117. },
  118. self: []string{"[::1]:1234"},
  119. wantHTTP: "", // skipped
  120. wantHTTPS: "", // skipped
  121. },
  122. {
  123. name: "no ports",
  124. env: map[string]string{
  125. "HTTP_PROXY": "myproxy",
  126. "HTTPS_PROXY": "myproxy",
  127. },
  128. self: []string{"127.0.0.1:1234"},
  129. wantHTTP: "myproxy",
  130. wantHTTPS: "myproxy",
  131. },
  132. }
  133. for _, tt := range testCases {
  134. t.Run(tt.name, func(t *testing.T) {
  135. for k, v := range tt.env {
  136. oldEnv, found := os.LookupEnv(k)
  137. if found {
  138. t.Cleanup(func() {
  139. os.Setenv(k, oldEnv)
  140. })
  141. }
  142. os.Setenv(k, v)
  143. }
  144. // Reset computed variables
  145. config = nil
  146. proxyFunc = func(*url.URL) (*url.URL, error) {
  147. panic("should not be called")
  148. }
  149. SetSelfProxy(tt.self...)
  150. if got := config.HTTPProxy; got != tt.wantHTTP {
  151. t.Errorf("got HTTPProxy=%q; want %q", got, tt.wantHTTP)
  152. }
  153. if got := config.HTTPSProxy; got != tt.wantHTTPS {
  154. t.Errorf("got HTTPSProxy=%q; want %q", got, tt.wantHTTPS)
  155. }
  156. if proxyFunc != nil {
  157. t.Errorf("wanted nil proxyFunc")
  158. }
  159. // Verify that we do actually proxy through the
  160. // expected proxy, if we have one configured.
  161. pf := getProxyFunc()
  162. if tt.wantHTTP != "" {
  163. want := "http://" + tt.wantHTTP
  164. uu, _ := url.Parse("http://tailscale.com")
  165. dest, err := pf(uu)
  166. if err != nil {
  167. t.Error(err)
  168. } else if dest.String() != want {
  169. t.Errorf("got dest=%q; want %q", dest, want)
  170. }
  171. }
  172. if tt.wantHTTPS != "" {
  173. want := "http://" + tt.wantHTTPS
  174. uu, _ := url.Parse("https://tailscale.com")
  175. dest, err := pf(uu)
  176. if err != nil {
  177. t.Error(err)
  178. } else if dest.String() != want {
  179. t.Errorf("got dest=%q; want %q", dest, want)
  180. }
  181. }
  182. })
  183. }
  184. }