tshttpproxy.go 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. // Copyright (c) 2020 Tailscale Inc & 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 tshttpproxy contains Tailscale additions to httpproxy not available
  5. // in golang.org/x/net/http/httpproxy. Notably, it aims to support Windows better.
  6. package tshttpproxy
  7. import (
  8. "net/http"
  9. "net/url"
  10. "os"
  11. "sync"
  12. "time"
  13. )
  14. // InvalidateCache invalidates the package-level cache for ProxyFromEnvironment.
  15. //
  16. // It's intended to be called on network link/routing table changes.
  17. func InvalidateCache() {
  18. mu.Lock()
  19. defer mu.Unlock()
  20. noProxyUntil = time.Time{}
  21. }
  22. var (
  23. mu sync.Mutex
  24. noProxyUntil time.Time // if non-zero, time at which ProxyFromEnvironment should check again
  25. )
  26. func setNoProxyUntil(d time.Duration) {
  27. mu.Lock()
  28. defer mu.Unlock()
  29. noProxyUntil = time.Now().Add(d)
  30. }
  31. var _ = setNoProxyUntil // quiet staticcheck; Windows uses the above, more might later
  32. // sysProxyFromEnv, if non-nil, specifies a platform-specific ProxyFromEnvironment
  33. // func to use if http.ProxyFromEnvironment doesn't return a proxy.
  34. // For example, WPAD PAC files on Windows.
  35. var sysProxyFromEnv func(*http.Request) (*url.URL, error)
  36. func ProxyFromEnvironment(req *http.Request) (*url.URL, error) {
  37. mu.Lock()
  38. noProxyTime := noProxyUntil
  39. mu.Unlock()
  40. if time.Now().Before(noProxyTime) {
  41. return nil, nil
  42. }
  43. u, err := http.ProxyFromEnvironment(req)
  44. if u != nil && err == nil {
  45. return u, nil
  46. }
  47. if sysProxyFromEnv != nil {
  48. u, err := sysProxyFromEnv(req)
  49. if u != nil && err == nil {
  50. return u, nil
  51. }
  52. }
  53. return nil, err
  54. }
  55. var sysAuthHeader func(*url.URL) (string, error)
  56. // GetAuthHeader returns the Authorization header value to send to proxy u.
  57. func GetAuthHeader(u *url.URL) (string, error) {
  58. if fake := os.Getenv("TS_DEBUG_FAKE_PROXY_AUTH"); fake != "" {
  59. return fake, nil
  60. }
  61. if sysAuthHeader != nil {
  62. return sysAuthHeader(u)
  63. }
  64. return "", nil
  65. }
  66. var condSetTransportGetProxyConnectHeader func(*http.Transport)
  67. // SetTarnsportGetProxyConnectHeader sets the provided Transport's
  68. // GetProxyConnectHeader field, if the current build of Go supports
  69. // it.
  70. //
  71. // See https://github.com/golang/go/issues/41048.
  72. func SetTransportGetProxyConnectHeader(tr *http.Transport) {
  73. if f := condSetTransportGetProxyConnectHeader; f != nil {
  74. f(tr)
  75. }
  76. }