你正在使用的Go版本是什么(go version
)?
go版本:go1.10.2 linux/amd64
这个问题在最新版本的发布中是否会重现?
是的
你做了什么?
运行以下程序
package main
import (
"context"
"fmt"
"net"
"net/http"
"net/http/httptrace"
)
func main() {
dialFunc := func(ctx context.Context, network string, addr string) (net.Conn, error) {
// This would be where there's a net.Dial to an external lookup service
// but just pretend that this actually matters for the purposes of this bug
// report
n, err := (&net.Dialer{}).DialContext(ctx, "tcp", "www.golang.org:80")
if err != nil {
panic(err)
}
n.Close()
// assume that 1.1.1.1 is an IP returned from the lookup service
// This IP was purely chosen because it's an IP that I know runs a
// HTTP server
return (&net.Dialer{}).DialContext(ctx, network, "1.1.1.1:80")
}
transport := &http.Transport{
DialContext: dialFunc,
}
req, err := http.NewRequest("GET", "http://example.org/", nil)
if err != nil {
panic(err)
}
ct := &httptrace.ClientTrace{
DNSStart: func(info httptrace.DNSStartInfo) {
fmt.Println("DNSStart:", info.Host)
},
}
ctx := httptrace.WithClientTrace(context.Background(), ct)
req = req.WithContext(ctx)
resp, err := transport.RoundTrip(req)
if err != nil {
panic(err)
}
resp.Body.Close()
}
你期望看到什么?
stdout上没有打印任何内容
你实际上看到了什么?DNSStart: www.golang.org
被打印在stdout上
此外,ConnectStart和ConnectDone被调用了两次,一次是为了解析服务,另一次是为了实际的HTTP连接。这也会让你非常恼火,一旦你的解析服务可能正在使用HTTP本身,就会导致更多的困惑。
通常情况下,我会在解析函数运行时将ClientTrace存储起来,并独立地调用DNSStart和DNSDone函数,但是一旦将ClientTrace放入上下文中,就没有方法可以将其删除。
我对解决这个问题相当有信心,但这种交互方式是意料之外的,如果你们在使用ClientTraces的库时没有预料到会被覆盖的Dial函数,可能会引起混乱。
8条答案
按热度按时间avwztpqn1#
@bradfitz
jdgnovmf2#
我并不完全相信这是一个bug。Transport.DialFunc是用于Transport的。如果你有一些无关的拨号操作,使用一个无关的上下文?
sshcrbum3#
对于错误报告,这个拨号没有连接,但是在我编写的程序中,我们使用网络名称解析从主机名到IP。在这种情况下,我想通过上下文启用取消操作,而不会得到令人困惑的httptrace回调。
piah890a4#
关于:
hsvhsicv5#
或者让
WithClientTrace
执行一个空操作*ClientTrace
以从上下文中删除它。axr492tv6#
这可以工作,因为nil已经被保留了:
想要发送一个CL,包括测试和文档吗?
qyswt5oh7#
可以做到,可能会在明天寄出去。
r6vfmomb8#
我遇到了这个问题,发现这个仍然打开着,所以我别无选择,只能实施一个黑客: