go x/net/http2: bogus greeting when providing TLSClientConfig

jv4diomz  于 9个月前  发布在  Go
关注(0)|答案(9)|浏览(98)

你使用的Go版本是什么( go version )?

go版本 go1.9beta2 darwin/amd64

你正在使用的操作系统和处理器架构是什么( go env )?

  1. GOARCH="amd64"
  2. GOBIN=""
  3. GOEXE=""
  4. GOHOSTARCH="amd64"
  5. GOHOSTOS="darwin"
  6. GOOS="darwin"
  7. GOPATH="/Users/jackson/"
  8. GORACE=""
  9. GOROOT="/Users/jackson/sdk/go1.9beta2"
  10. GOTOOLDIR="/Users/jackson/sdk/go1.9beta2/pkg/tool/darwin_amd64"
  11. GCCGO="gccgo"
  12. CC="clang"
  13. 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"
  14. CXX="clang++"
  15. CGO_ENABLED="1"
  16. CGO_CFLAGS="-g -O2"
  17. CGO_CPPFLAGS=""
  18. CGO_CXXFLAGS="-g -O2"
  19. CGO_FFLAGS="-g -O2"
  20. CGO_LDFLAGS="-g -O2"
  21. PKG_CONFIG="pkg-config

你做了什么?

下载 https://gist.github.com/jbowens/3e1f7443d6cb1bf8071f4aea2397c4b1
运行 main.go
go1.9beta2 run main.go

你应该看到什么?

程序应该在没有恐慌的情况下退出。

你看到了什么?

  1. 2017/08/07 18:17:01 http2: server: error reading preface from client [::1]:59163: bogus greeting "POST / HTTP/1.1\r\nHost: l"
  2. 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。

pdkcd3nj

pdkcd3nj1#

@jbowens,为了避免使用http2,https://golang.org/pkg/net/http文档中提到

设置Transport.TLSNextProto,所以在你的gist中请设置

  1. config.NextProtos = []string{"h1"}

,这样应该会使用HTTP/1。这是否会影响你的bug报告?

svgewumm

svgewumm2#

@odeke-em 感谢,但我认为它没有。我可以通过显式配置协议(包括http2或不包括)来避免这个错误。我只是报告了这个配置导致客户端协商http/2,但然后使用http1进行请求的事实。

41zrol4v

41zrol4v3#

是的,谢谢你让我知道。我会请来大人物/cc @bradfitz@tombergan。

xriantvc

xriantvc4#

行为肯定是不一致的。http.Transport说:https://golang.org/pkg/net/http/#Transport
注意"可能不会被启用"。正如你指出的,当指定TLCClientConfig时,代码不会启用HTTP/2。

  1. // TLSClientConfig specifies the TLS configuration to use with
  2. // tls.Client.
  3. // If nil, the default configuration is used.
  4. // If non-nil, HTTP/2 support may not be enabled by default.
  5. TLSClientConfig *tls.Config

无论如何都不会做出保证,如果指定了TLSConfig,它将很高兴地使用HTTP/2,只要配置中不包含禁止的密码。
https://golang.org/pkg/net/http/#Server
我猜H2服务器是按照这种方式编写的,以允许调用者指定证书,而H2客户端提供证书的情况较少见。我认为Transport应该表现得像服务器一样,以便在需要时允许客户端提供证书。

khbbv19g

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。

2w3rbyxf

2w3rbyxf6#

Happy New Year @tombergan! 这里的近况怎么样?

8zzbczxx

8zzbczxx7#

这里还有一个数据竞争问题,因为运行ListenAndServeTLS的goroutine与主goroutine的http Client同时初始化*tls.Config.NextProtos。
你可以通过在上面的main.go代码中添加以下几行来观察这个问题:

  1. time.Sleep(500 * time.Millisecond)
  2. log.Printf("TLS config NextProto = %q", config.NextProtos)

... 在goroutine之后,客户端初始化之前。注解掉500ms的睡眠,有时NextProtos会为空。使用-race运行显示:

  1. ==================
  2. WARNING: DATA RACE
  3. Write at 0x00c4200844e0 by goroutine 6:
  4. net/http.http2ConfigureServer()
  5. /home/bradfitz/go/src/net/http/h2_bundle.go:3953 +0x580
  6. net/http.(*Server).onceSetNextProtoDefaults()
  7. /home/bradfitz/go/src/net/http/server.go:3062 +0x13e
  8. net/http.(*Server).(net/http.onceSetNextProtoDefaults)-fm()
  9. /home/bradfitz/go/src/net/http/server.go:3026 +0x41
  10. sync.(*Once).Do()
  11. /home/bradfitz/go/src/sync/once.go:44 +0xe1
  12. net/http.(*Server).setupHTTP2_ServeTLS()
  13. /home/bradfitz/go/src/net/http/server.go:3026 +0x81
  14. net/http.(*Server).ServeTLS()
  15. /home/bradfitz/go/src/net/http/server.go:2806 +0x53
  16. net/http.(*Server).ListenAndServeTLS()
  17. /home/bradfitz/go/src/net/http/server.go:3019 +0x139
  18. main.main.func2()
  19. /home/bradfitz/x.go:50 +0x49
  20. Previous read at 0x00c4200844e0 by main goroutine:
  21. runtime.convT2Eslice()
  22. /home/bradfitz/go/src/runtime/iface.go:365 +0x0
  23. main.main()
  24. /home/bradfitz/x.go:57 +0x699
  25. Goroutine 6 (running) created at:
  26. main.main()
  27. /home/bradfitz/x.go:49 +0x66d
  28. ==================

为了解决这个问题,我认为我们需要停止修改调用者的提供的*Server,至少通过它的公共字段。我们可能需要添加一个额外的私有字段(很可能由现有的私有互斥锁保护),并让http2.ConfigureServer设置该字段,如果有的话,设置为导出配置的克隆。但是由于它在不同的包中,我们需要一个导出的setter方法?(粗俗。)或者我们需要做一些只在捆绑的x/net/http2-in-std版本中起作用的小把戏,这可能是答案。
然后,每当net/http服务器需要其配置时,它都会通过一个私有的访问器方法获取它,该方法优先选择未导出的TLS配置字段而不是导出的字段。
无关紧要的是,我们还可以在HTTP/1传输中更智能地检查其TLS连接的状态,以及是否已经协商了ALPN "h2",要么提前用有用的东西("你配置错误了。")失败,要么不遗余力地实际上说HTTP/2,如果那不是太多的工作。
无论如何,对于即将发布的Go 1.10来说,这里有太多要做的事了。抱歉。
至少有很多解决方法。

展开查看全部
zzoitvuj

zzoitvuj8#

/cc @FiloSottile

nhaq1z21

nhaq1z219#

在客户端有临时的解决方法吗?

相关问题