@ -9,11 +9,16 @@ import (
"context"
"context"
"fmt"
"fmt"
"log"
"log"
"net"
"net/http"
"net/http"
"net/url"
"net/url"
"os"
"os"
"runtime"
"strings"
"sync"
"sync"
"time"
"time"
"golang.org/x/net/http/httpproxy"
)
)
// InvalidateCache invalidates the package-level cache for ProxyFromEnvironment.
// InvalidateCache invalidates the package-level cache for ProxyFromEnvironment.
@ -27,9 +32,24 @@ func InvalidateCache() {
var (
var (
mu sync . Mutex
mu sync . Mutex
noProxyUntil time . Time // if non-zero, time at which ProxyFromEnvironment should check again
noProxyUntil time . Time // if non-zero, time at which ProxyFromEnvironment should check again
config * httpproxy . Config // used to create proxyFunc
proxyFunc func ( * url . URL ) ( * url . URL , error )
)
)
func getProxyFunc ( ) func ( * url . URL ) ( * url . URL , error ) {
// Create config/proxyFunc if it's not created
mu . Lock ( )
defer mu . Unlock ( )
if config == nil {
config = httpproxy . FromEnvironment ( )
}
if proxyFunc == nil {
proxyFunc = config . ProxyFunc ( )
}
return proxyFunc
}
// setNoProxyUntil stops calls to sysProxyEnv (if any) for the provided duration.
// setNoProxyUntil stops calls to sysProxyEnv (if any) for the provided duration.
func setNoProxyUntil ( d time . Duration ) {
func setNoProxyUntil ( d time . Duration ) {
mu . Lock ( )
mu . Lock ( )
@ -39,6 +59,59 @@ func setNoProxyUntil(d time.Duration) {
var _ = setNoProxyUntil // quiet staticcheck; Windows uses the above, more might later
var _ = setNoProxyUntil // quiet staticcheck; Windows uses the above, more might later
// SetSelfProxy configures this package to avoid proxying through any of the
// provided addresses– e.g. if they refer to proxies being run by this process.
func SetSelfProxy ( addrs ... string ) {
mu . Lock ( )
defer mu . Unlock ( )
// Ensure we have a valid config
if config == nil {
config = httpproxy . FromEnvironment ( )
}
normalizeHostPort := func ( s string ) string {
host , portStr , err := net . SplitHostPort ( s )
if err != nil {
return s
}
// Normalize the localhost IP into "localhost", to avoid IPv4/IPv6 confusion.
if host == "127.0.0.1" || host == "::1" {
return "localhost:" + portStr
}
// On Linux, all 127.0.0.1/8 IPs are also localhost.
if runtime . GOOS == "linux" && strings . HasPrefix ( host , "127.0.0." ) {
return "localhost:" + portStr
}
return s
}
normHTTP := normalizeHostPort ( config . HTTPProxy )
normHTTPS := normalizeHostPort ( config . HTTPSProxy )
// If any of our proxy variables point to one of the configured
// addresses, ignore them.
for _ , addr := range addrs {
normAddr := normalizeHostPort ( addr )
if normHTTP != "" && normHTTP == normAddr {
log . Printf ( "tshttpproxy: skipping HTTP_PROXY pointing to self: %q" , addr )
config . HTTPProxy = ""
normHTTP = ""
}
if normHTTPS != "" && normHTTPS == normAddr {
log . Printf ( "tshttpproxy: skipping HTTPS_PROXY pointing to self: %q" , addr )
config . HTTPSProxy = ""
normHTTPS = ""
}
}
// Invalidate to cause it to get re-created
proxyFunc = nil
}
// sysProxyFromEnv, if non-nil, specifies a platform-specific ProxyFromEnvironment
// sysProxyFromEnv, if non-nil, specifies a platform-specific ProxyFromEnvironment
// func to use if http.ProxyFromEnvironment doesn't return a proxy.
// func to use if http.ProxyFromEnvironment doesn't return a proxy.
// For example, WPAD PAC files on Windows.
// For example, WPAD PAC files on Windows.
@ -48,7 +121,8 @@ var sysProxyFromEnv func(*http.Request) (*url.URL, error)
// but additionally does OS-specific proxy lookups if the environment variables
// but additionally does OS-specific proxy lookups if the environment variables
// alone don't specify a proxy.
// alone don't specify a proxy.
func ProxyFromEnvironment ( req * http . Request ) ( * url . URL , error ) {
func ProxyFromEnvironment ( req * http . Request ) ( * url . URL , error ) {
u , err := http . ProxyFromEnvironment ( req )
localProxyFunc := getProxyFunc ( )
u , err := localProxyFunc ( req . URL )
if u != nil && err == nil {
if u != nil && err == nil {
return u , nil
return u , nil
}
}