Description
What version of Go are you using (go version
)?
$ go version go1.12.7 windows/amd64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env set GOARCH=amd64 set GOBIN= set GOCACHE=C:\Users\schlopeki\AppData\Local\go-build set GOEXE=.exe set GOFLAGS= set GOHOSTARCH=amd64 set GOHOSTOS=windows set GOOS=windows set GOPATH=C:\Users\schlopeki\go set GOPROXY= set GORACE= set GOROOT=C:\Go set GOTMPDIR= set GOTOOLDIR=C:\Go\pkg\tool\windows_amd64 set GCCGO=gccgo set CC=gcc set CXX=g++ set CGO_ENABLED=1 set GOMOD=C:\Users\schlopeki\Projects\doh\go.mod set CGO_CFLAGS=-g -O2 set CGO_CPPFLAGS= set CGO_CXXFLAGS=-g -O2 set CGO_FFLAGS=-g -O2 set CGO_LDFLAGS=-g -O2 set PKG_CONFIG=pkg-config set GOGCCFLAGS=-m64 -mthreads -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\Users\SCHLOP~1\AppData\Local\Temp\go-build767447546=/tmp/go-build -gno-record-gcc-switches
What did you do?
I attempted to make a simple http2 only client/server but the server rejects all connections as having "INADEQUATE_SECURITY, TLS version too low". When inspecting the TLS version connection claims to have a TLS version of 0 but in Wireshark I see that a TLS 1.2 negotiation has already started.
If I manually perform the TLS handshake the http2.Server.ServeConn no longer hangs up the connection.
Here is the minimal server:
package main
import (
"crypto/tls"
"log"
"net/http"
"golang.org/x/net/http2"
)
func main() {
cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
panic("Cannot load cert")
}
tlsCfg := &tls.Config{
Certificates: []tls.Certificate{cert},
NextProtos: []string{http2.NextProtoTLS},
}
srv := http2.Server{}
srvOpt := &http2.ServeConnOpts{
Handler: http.HandlerFunc(handler),
BaseConfig: &http.Server{TLSConfig: tlsCfg},
}
listener, err := tls.Listen("tcp", "127.0.0.1:8942", tlsCfg)
if err != nil {
panic("Cant listen")
}
for {
conn, err := listener.Accept()
if err != nil {
panic("connection error")
}
// Uncommenting the next 2 lines make the server work
// tlsConn := conn.(*tls.Conn)
// tlsConn.Handshake()
go func() { srv.ServeConn(conn, srvOpt) }()
}
}
func handler(wrt http.ResponseWriter, req *http.Request) {
if req.ProtoMajor != 2 {
wrt.WriteHeader(500)
flusher, _ := wrt.(http.Flusher)
flusher.Flush()
log.Printf("Not HTTP2")
return
}
wrt.WriteHeader(200)
flusher, _ := wrt.(http.Flusher)
flusher.Flush()
}
Modern versions of Chrome/Firefox should be able to test this server. Testing server.crt and server.key need to be created and I used a host file to point test.local at the loopback address.
What did you expect to see?
The connection would have already completed the TLS handshake once listen.Accept returned it for use, or http2.Server.ServerConn would be able to see that the TLS version is at least negotiated.
What did you see instead?
When passing the connection directly from Accept to the http2.Server.ServeConn, the TLS state of the connection is not set. This results in ServeConn seeing a TLS version of 0 and therefore it sends GOAWAY and kills the connection.