Skip to content

x/net/http2: call conn.Handshake before accessing ConnectionState #33304

Open
@sebsebmc

Description

@sebsebmc

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsFixThe path to resolution is known, but the work has not been done.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions