mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-26 11:35:35 +00:00
fb392e34b5
There was a mechanism in tshttpproxy to note that a Windows proxy lookup failed and to stop hitting it so often. But that turns out to fire a lot (no PAC file configured at all results in a proxy lookup), so after the first proxy lookup, we were enabling the "omg something's wrong, stop looking up proxies" bit for awhile, which was then also preventing the normal Go environment-based proxy lookups from working. This at least fixes environment-based proxies. Plenty of other Windows-specific proxy work remains (using WinHttpGetIEProxyConfigForCurrentUser instead of just PAC files, ignoring certain types of errors, etc), but this should fix the regression reported in #4811. Updates #4811 Change-Id: I665e1891897d58e290163bda5ca51a22a017c5f9 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
106 lines
2.8 KiB
Go
106 lines
2.8 KiB
Go
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package tshttpproxy contains Tailscale additions to httpproxy not available
|
|
// in golang.org/x/net/http/httpproxy. Notably, it aims to support Windows better.
|
|
package tshttpproxy
|
|
|
|
import (
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// InvalidateCache invalidates the package-level cache for ProxyFromEnvironment.
|
|
//
|
|
// It's intended to be called on network link/routing table changes.
|
|
func InvalidateCache() {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
noProxyUntil = time.Time{}
|
|
}
|
|
|
|
var (
|
|
mu sync.Mutex
|
|
noProxyUntil time.Time // if non-zero, time at which ProxyFromEnvironment should check again
|
|
)
|
|
|
|
// setNoProxyUntil stops calls to sysProxyEnv (if any) for the provided duration.
|
|
func setNoProxyUntil(d time.Duration) {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
noProxyUntil = time.Now().Add(d)
|
|
}
|
|
|
|
var _ = setNoProxyUntil // quiet staticcheck; Windows uses the above, more might later
|
|
|
|
// sysProxyFromEnv, if non-nil, specifies a platform-specific ProxyFromEnvironment
|
|
// func to use if http.ProxyFromEnvironment doesn't return a proxy.
|
|
// For example, WPAD PAC files on Windows.
|
|
var sysProxyFromEnv func(*http.Request) (*url.URL, error)
|
|
|
|
// ProxyFromEnvironment is like the standard library's http.ProxyFromEnvironment
|
|
// but additionally does OS-specific proxy lookups if the environment variables
|
|
// alone don't specify a proxy.
|
|
func ProxyFromEnvironment(req *http.Request) (*url.URL, error) {
|
|
u, err := http.ProxyFromEnvironment(req)
|
|
if u != nil && err == nil {
|
|
return u, nil
|
|
}
|
|
|
|
mu.Lock()
|
|
noProxyTime := noProxyUntil
|
|
mu.Unlock()
|
|
if time.Now().Before(noProxyTime) {
|
|
return nil, nil
|
|
}
|
|
|
|
if sysProxyFromEnv != nil {
|
|
u, err := sysProxyFromEnv(req)
|
|
if u != nil && err == nil {
|
|
return u, nil
|
|
}
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
var sysAuthHeader func(*url.URL) (string, error)
|
|
|
|
// GetAuthHeader returns the Authorization header value to send to proxy u.
|
|
func GetAuthHeader(u *url.URL) (string, error) {
|
|
if fake := os.Getenv("TS_DEBUG_FAKE_PROXY_AUTH"); fake != "" {
|
|
return fake, nil
|
|
}
|
|
if user := u.User.Username(); user != "" {
|
|
pass, ok := u.User.Password()
|
|
if !ok {
|
|
return "", nil
|
|
}
|
|
|
|
req := &http.Request{Header: make(http.Header)}
|
|
req.SetBasicAuth(user, pass)
|
|
return req.Header.Get("Authorization"), nil
|
|
}
|
|
if sysAuthHeader != nil {
|
|
return sysAuthHeader(u)
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
var condSetTransportGetProxyConnectHeader func(*http.Transport)
|
|
|
|
// SetTransportGetProxyConnectHeader sets the provided Transport's
|
|
// GetProxyConnectHeader field, if the current build of Go supports
|
|
// it.
|
|
//
|
|
// See https://github.com/golang/go/issues/41048.
|
|
func SetTransportGetProxyConnectHeader(tr *http.Transport) {
|
|
if f := condSetTransportGetProxyConnectHeader; f != nil {
|
|
f(tr)
|
|
}
|
|
}
|