Commit 054b33e6 authored by Shi Han Ng's avatar Shi Han Ng Committed by Brad Fitzpatrick

proxy: add support for ALL_PROXY and NO_PROXY

Fixes golang/go#13456

Change-Id: I0b938f824c47b29ac2026eff83e61c2f227a6cc1
Reviewed-on: https://go-review.googlesource.com/47530
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 570fa1c9
......@@ -11,6 +11,7 @@ import (
"net"
"net/url"
"os"
"sync"
)
// A Dialer is a means to establish a connection.
......@@ -27,7 +28,7 @@ type Auth struct {
// FromEnvironment returns the dialer specified by the proxy related variables in
// the environment.
func FromEnvironment() Dialer {
allProxy := os.Getenv("all_proxy")
allProxy := allProxyEnv.Get()
if len(allProxy) == 0 {
return Direct
}
......@@ -41,7 +42,7 @@ func FromEnvironment() Dialer {
return Direct
}
noProxy := os.Getenv("no_proxy")
noProxy := noProxyEnv.Get()
if len(noProxy) == 0 {
return proxy
}
......@@ -92,3 +93,42 @@ func FromURL(u *url.URL, forward Dialer) (Dialer, error) {
return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
}
var (
allProxyEnv = &envOnce{
names: []string{"ALL_PROXY", "all_proxy"},
}
noProxyEnv = &envOnce{
names: []string{"NO_PROXY", "no_proxy"},
}
)
// envOnce looks up an environment variable (optionally by multiple
// names) once. It mitigates expensive lookups on some platforms
// (e.g. Windows).
// (Borrowed from net/http/transport.go)
type envOnce struct {
names []string
once sync.Once
val string
}
func (e *envOnce) Get() string {
e.once.Do(e.init)
return e.val
}
func (e *envOnce) init() {
for _, n := range e.names {
e.val = os.Getenv(n)
if e.val != "" {
return
}
}
}
// reset is used by tests
func (e *envOnce) reset() {
e.once = sync.Once{}
e.val = ""
}
......@@ -5,14 +5,73 @@
package proxy
import (
"bytes"
"fmt"
"io"
"net"
"net/url"
"os"
"strconv"
"strings"
"sync"
"testing"
)
type proxyFromEnvTest struct {
allProxyEnv string
noProxyEnv string
wantTypeOf Dialer
}
func (t proxyFromEnvTest) String() string {
var buf bytes.Buffer
space := func() {
if buf.Len() > 0 {
buf.WriteByte(' ')
}
}
if t.allProxyEnv != "" {
fmt.Fprintf(&buf, "all_proxy=%q", t.allProxyEnv)
}
if t.noProxyEnv != "" {
space()
fmt.Fprintf(&buf, "no_proxy=%q", t.noProxyEnv)
}
return strings.TrimSpace(buf.String())
}
func TestFromEnvironment(t *testing.T) {
ResetProxyEnv()
type dummyDialer struct {
direct
}
RegisterDialerType("irc", func(_ *url.URL, _ Dialer) (Dialer, error) {
return dummyDialer{}, nil
})
proxyFromEnvTests := []proxyFromEnvTest{
{allProxyEnv: "127.0.0.1:8080", noProxyEnv: "localhost, 127.0.0.1", wantTypeOf: direct{}},
{allProxyEnv: "ftp://example.com:8000", noProxyEnv: "localhost, 127.0.0.1", wantTypeOf: direct{}},
{allProxyEnv: "socks5://example.com:8080", noProxyEnv: "localhost, 127.0.0.1", wantTypeOf: &PerHost{}},
{allProxyEnv: "irc://example.com:8000", wantTypeOf: dummyDialer{}},
{noProxyEnv: "localhost, 127.0.0.1", wantTypeOf: direct{}},
{wantTypeOf: direct{}},
}
for _, tt := range proxyFromEnvTests {
os.Setenv("ALL_PROXY", tt.allProxyEnv)
os.Setenv("NO_PROXY", tt.noProxyEnv)
ResetCachedEnvironment()
d := FromEnvironment()
if got, want := fmt.Sprintf("%T", d), fmt.Sprintf("%T", tt.wantTypeOf); got != want {
t.Errorf("%v: got type = %T, want %T", tt, d, tt.wantTypeOf)
}
}
}
func TestFromURL(t *testing.T) {
endSystem, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
......@@ -140,3 +199,17 @@ func socks5Gateway(t *testing.T, gateway, endSystem net.Listener, typ byte, wg *
return
}
}
func ResetProxyEnv() {
for _, env := range []*envOnce{allProxyEnv, noProxyEnv} {
for _, v := range env.names {
os.Setenv(v, "")
}
}
ResetCachedEnvironment()
}
func ResetCachedEnvironment() {
allProxyEnv.reset()
noProxyEnv.reset()
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment