Go忽略HTTP_PROXY环境变量。

huangapple go评论85阅读模式
英文:

Go ignores HTTP_PROXY environment variable

问题

我在互联网上看到,Go 会读取 HTTP_PROXY 环境变量并为默认客户端设置代理。然而,对我来说这并不起作用,我不知道为什么。

我使用的是 Ubuntu 20.04,Go 版本是 1.16,所以我升级到了 1.17,但问题仍然存在。

我有下面这个程序,并在终端中执行:HTTP_PROXY="http://localhost:8000" go run req.go
我发现第一个 Println 打印出了正确的值,但代理并没有被使用。

func main() {
	fmt.Println(os.Getenv("HTTP_PROXY"))
	client := &http.Client{}
	resp, err := client.Get("http://localhost:8090/vm/1")
	if err != nil {
		log.Fatal(err)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(body))
}

如果我修改代码,显式地设置代理,它就能正常工作。

u, err := url.Parse("http://localhost:8000")
if err != nil {
	log.Fatal(err)
}
client := &http.Client{
	Transport: &http.Transport{Proxy: http.ProxyURL(u)},
}
英文:

I read all over the internet, that Go reads HTTP_PROXY environment variable and set proxy for default client. However, it is not working for me and I don't know why.

I'm on Ubuntu 20.04, Go was 1.16, so I upgraded to 1.17 but it still the same.

I have the program below and I execute this in terminal: HTTP_PROXY="http://localhost:8000" go run req.go
I see that first Println prints out correct value, but proxy is not used.

func main() {
	fmt.Println(os.Getenv("HTTP_PROXY"))
	client := &http.Client{}
	resp, err := client.Get("http://localhost:8090/vm/1")
	if err != nil {
		log.Fatal(err)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(body))
}

If I modify the code, and set proxy explicitly, it works.

u, err := url.Parse("http://localhost:8000")
if err != nil {
	log.Fatal(err)
}
client := &http.Client{
	Transport: &http.Transport{Proxy: http.ProxyURL(u)},
}

答案1

得分: 4

在查看源代码后,我找到了主要原因和一个解决方法。这是一种被注释掉的行为,但非常深入。源代码中的 opensource.google/x/net/http/httpproxy/proxy.go 第118行第181行 是负责的确切 if 语句。

问题是,当请求的URL为 localhost127.x.x.x 时,HTTP_PROXY 被忽略。但你可以将自定义的URI添加到 /etc/hostsC:\Windows\System32\drivers\etc\hosts 中。

127.0.0.1    localserver.loc

然后,所有的请求都必须发送到 localserver.loc:8090 而不是 localhost:8090。这样就能正常工作了。

英文:

After digging in the source code I have found the main reason and also a workaround. It is commented behavior, but quite deep. Source opensource.google/x/net/http/httpproxy/proxy.go line 118 and on line 181 is exact if which is responsible.

The issue is, that HTTP_PROXY is ignored when the request has URL localhost or 127.x.x.x. But you can easily add custom URI to /etc/hosts or C:\Windows\System32\drivers\etc\hosts.

127.0.0.1    localserver.loc

Then all requests must go to localserver.loc:8090 not localhost:8090. And it will work like a charm.

答案2

得分: 1

我假设你设置了一个名为"NO_PROXY"的变量,其值为"localhost",这个值会被DefaultTransport所遵循。

当使用http.ProxyURL()来显式设置代理时,无论NO_PROXY的值如何,都会使用这个代理。

英文:

I assume that you have a NO_PROXY variable set to "localhost", which is respected by the DefaultTransport.

When explicitely setting a Proxy with http.ProxyURL(), this proxy is used regardless of NO_PROXY.

答案3

得分: 1

对于其他遇到此问题的人,请确保您没有使用空结构初始化传输。这是DefaultTransport的代码(请注意Proxy的值):

var DefaultTransport RoundTripper = &Transport{
	Proxy: ProxyFromEnvironment,
	DialContext: defaultTransportDialContext(&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}),
	ForceAttemptHTTP2:     true,
	MaxIdleConns:          100,
	IdleConnTimeout:       90 * time.Second,
	TLSHandshakeTimeout:   10 * time.Second,
	ExpectContinueTimeout: 1 * time.Second,
}

因此,如果您像这样编写代码:

if client.Transport == nil {
	client.Transport = &http.Transport{}
}

代理将不会被使用。对我来说,修复方法是:

if client.Transport == nil {
	client.Transport = http.DefaultTransport.(*http.Transport).Clone()
}
英文:

For anyone else stumbling on this, be sure that you didn't initialize transport with an empty struct. Here's the DefaultTransport (note the Proxy value):

var DefaultTransport RoundTripper = &Transport{
	Proxy: ProxyFromEnvironment,
	DialContext: defaultTransportDialContext(&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}),
	ForceAttemptHTTP2:     true,
	MaxIdleConns:          100,
	IdleConnTimeout:       90 * time.Second,
	TLSHandshakeTimeout:   10 * time.Second,
	ExpectContinueTimeout: 1 * time.Second,
}

So if you were to say something like:

if client.Transport == nil {
	client.Transport = &http.Transport{}
}

the proxy wouldn't be used. The fix for me was:

if client.Transport == nil {
	client.Transport = http.DefaultTransport.(*http.Transport).Clone()
}

huangapple
  • 本文由 发表于 2021年11月1日 03:32:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/69789948.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定