// 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 ) 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) func ProxyFromEnvironment(req *http.Request) (*url.URL, error) { mu.Lock() noProxyTime := noProxyUntil mu.Unlock() if time.Now().Before(noProxyTime) { return nil, nil } u, err := http.ProxyFromEnvironment(req) if u != nil && err == nil { return u, 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 sysAuthHeader != nil { return sysAuthHeader(u) } 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 } return "", nil } var condSetTransportGetProxyConnectHeader func(*http.Transport) // SetTarnsportGetProxyConnectHeader 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) } }