你使用的Go版本是什么( go version
)?
go版本 go1.9beta2 darwin/amd64
你正在使用的操作系统和处理器架构是什么( go env
)?
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/jackson/"
GORACE=""
GOROOT="/Users/jackson/sdk/go1.9beta2"
GOTOOLDIR="/Users/jackson/sdk/go1.9beta2/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/df/tll2ynl125z01jxszx2fq2nw0000gn/T/go-build293310413=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config
你做了什么?
下载 https://gist.github.com/jbowens/3e1f7443d6cb1bf8071f4aea2397c4b1
运行 main.go
go1.9beta2 run main.go
你应该看到什么?
程序应该在没有恐慌的情况下退出。
你看到了什么?
2017/08/07 18:17:01 http2: server: error reading preface from client [::1]:59163: bogus greeting "POST / HTTP/1.1\r\nHost: l"
panic: Post https://localhost:8080/: net/http: HTTP/1.x transport connection broken: malformed HTTP response "\x00\x00\x18\x04\x00\x00\x00\x00\x00\x00\x05\x00\x10\x00\x00\x00\x03\x00\x00\x00\xfa\x00\x06\x00\x10\x01@\x00\x04\x00\x10\x00\x00"
从我的理解来看,TLSClientConfig的存在应该会导致客户端不使用http2,但它似乎仍然协商使用http2。
9条答案
按热度按时间pdkcd3nj1#
@jbowens,为了避免使用http2,https://golang.org/pkg/net/http文档中提到
设置
Transport.TLSNextProto
,所以在你的gist中请设置,这样应该会使用HTTP/1。这是否会影响你的bug报告?
svgewumm2#
@odeke-em 感谢,但我认为它没有。我可以通过显式配置协议(包括http2或不包括)来避免这个错误。我只是报告了这个配置导致客户端协商http/2,但然后使用http1进行请求的事实。
41zrol4v3#
是的,谢谢你让我知道。我会请来大人物/cc @bradfitz@tombergan。
xriantvc4#
行为肯定是不一致的。
http.Transport
说:https://golang.org/pkg/net/http/#Transport注意"可能不会被启用"。正如你指出的,当指定TLCClientConfig时,代码不会启用HTTP/2。
无论如何都不会做出保证,如果指定了TLSConfig,它将很高兴地使用HTTP/2,只要配置中不包含禁止的密码。
https://golang.org/pkg/net/http/#Server
我猜H2服务器是按照这种方式编写的,以允许调用者指定证书,而H2客户端提供证书的情况较少见。我认为Transport应该表现得像服务器一样,以便在需要时允许客户端提供证书。
khbbv19g5#
在这里添加一点背景信息:这是由于同时使用相同的
tls.Config
创建服务器和客户端所导致的。当服务器开始提供服务时,它正确地检测到它确实可以提供HTTP2。因为它已经检测到它可以提供HTTP2,所以它将
"h2"
添加到其TLSConfig
中的NextProtos
列表中(https://github.com/golang/go/blob/master/src/net/http/h2_bundle.go#L3949)。注意,它首先克隆了
TLSConfig
,因此我认为意图是可以使用相同的TLSConfig
初始化多个服务器和客户端。但它只进行了浅克隆(参见 https://github.com/golang/go/blob/master/src/crypto/tls/common.go#L545)。浅克隆意味着NextProtos
列表在多个TLSConfig
对象之间共享。因此,当服务器决定它可以提供
h2
时,它还会将h2
添加到客户端的TLSConfig
中。因此,TLS协商与服务器进行的HTTP2连接进行协商。然而,Transport
决定不使用HTTP2,因为它看到已经设置了一个 TLSConfig(https://github.com/golang/go/blob/master/src/net/http/transport.go#L227)。客户端中的这两个协议级别在这里存在分歧——TLS已经与HTTP2协商,而 HTTP2是下一个协议,但
Transport
决定它不能使用HTTP2。2w3rbyxf6#
Happy New Year @tombergan! 这里的近况怎么样?
8zzbczxx7#
这里还有一个数据竞争问题,因为运行ListenAndServeTLS的goroutine与主goroutine的http Client同时初始化*tls.Config.NextProtos。
你可以通过在上面的main.go代码中添加以下几行来观察这个问题:
... 在goroutine之后,客户端初始化之前。注解掉500ms的睡眠,有时NextProtos会为空。使用-race运行显示:
为了解决这个问题,我认为我们需要停止修改调用者的提供的*Server,至少通过它的公共字段。我们可能需要添加一个额外的私有字段(很可能由现有的私有互斥锁保护),并让http2.ConfigureServer设置该字段,如果有的话,设置为导出配置的克隆。但是由于它在不同的包中,我们需要一个导出的setter方法?(粗俗。)或者我们需要做一些只在捆绑的x/net/http2-in-std版本中起作用的小把戏,这可能是答案。
然后,每当net/http服务器需要其配置时,它都会通过一个私有的访问器方法获取它,该方法优先选择未导出的TLS配置字段而不是导出的字段。
无关紧要的是,我们还可以在HTTP/1传输中更智能地检查其TLS连接的状态,以及是否已经协商了ALPN "h2",要么提前用有用的东西("你配置错误了。")失败,要么不遗余力地实际上说HTTP/2,如果那不是太多的工作。
无论如何,对于即将发布的Go 1.10来说,这里有太多要做的事了。抱歉。
至少有很多解决方法。
zzoitvuj8#
/cc @FiloSottile
nhaq1z219#
在客户端有临时的解决方法吗?