Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net/http: disabling HTTP/2 as per documentation does not work #39302

Open
rittneje opened this issue May 28, 2020 · 4 comments
Open

net/http: disabling HTTP/2 as per documentation does not work #39302

rittneje opened this issue May 28, 2020 · 4 comments

Comments

@rittneje
Copy link

@rittneje rittneje commented May 28, 2020

What version of Go are you using (go version)?

$ go version
go version go1.13.10 darwin/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
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/tmp/.gocache"
GOENV="/Users/jrittner/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/jrittner/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/Users/jrittner/go1.13.10"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/Users/jrittner/go1.13.10/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="0"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/qz/jbyzqww512dfd00_vfwk74l4zy5gvy/T/go-build718180347=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

I set up an http.Transport with a non-nil, empty map for TLSNextProto in order to disable HTTP/2, as per the documentation.

https://golang.org/pkg/net/http/

Programs that must disable HTTP/2 can do so by setting Transport.TLSNextProto (for clients) or Server.TLSNextProto (for servers) to a non-nil, empty map.

 transport := http.DefaultTransport.(*http.Transport).Clone()
 transport.TLSNextProto = make(map[string]func(authority string, c *tls.Conn) http.RoundTripper)
 httpClient := &http.Client{
   Transport: transport,
 }
 resp, err := httpClient.Get("https://google.com")
 if err != nil {
   panic(err)
 }
 defer resp.Body.Close()
 fmt.Println("Response status:", resp.Status)

What did you expect to see?

I expected it to do HTTP/1.1 over TLS.

What did you see instead?

panic: Get https://google.com: net/http: HTTP/1.x transport connection broken: malformed HTTP response "\x00\x00\x12\x04\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00d\x00\x04\x00\x10\x00\x00\x00\x06\x00\x00@\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x0f\x00\x01\x00\x00\x1e\a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01http2_handshake_failed"
@andybons
Copy link
Member

@andybons andybons commented May 28, 2020

@andybons andybons added this to the Unplanned milestone May 28, 2020
@odeke-em
Copy link
Member

@odeke-em odeke-em commented May 29, 2020

Thank you for this report @rittneje!

It does work as advertised, except that you are using a http.DefaultTransport.Clone() which clones only the exported fields of that Transport and yet http.DefaultTransport is initialized with some internal variables. The problem you are encountering is an inconsistency in what's expected internally and the TLS handling function is returned as nil which causes that error.

You can ensure that this works by creating a fresh transport

package main

import (
	"crypto/tls"
	"fmt"
	"net/http"
)

func main() {
	httpClient := &http.Client{
		Transport: &http.Transport{
			TLSNextProto: make(map[string]func(authority string, c *tls.Conn) http.RoundTripper),
		},
	}
	resp, err := httpClient.Get("https://google.com/")
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	fmt.Println("Response status:", resp.Status)
}

or alternatively if you want to entirely disable HTTP/2, you can set in your environment

GODEBUG=http2client=0

I am going to send a CL showing clear examples for what I've talked about and that'll perhaps serve as a reference for disabling HTTP/2.

@gopherbot
Copy link

@gopherbot gopherbot commented May 29, 2020

Change https://golang.org/cl/235523 mentions this issue: net/http: add examples for disabling HTTP/2 in Transport

@odeke-em
Copy link
Member

@odeke-em odeke-em commented May 29, 2020

I forgot to mention, that if you also want to use a cloned http.DefaultTransport with HTTP/1, you'll need to ensure:
a) ForceAttemptHTTP2 = false
b) TLSClientConfig = new(tls.Config)

package main

import (
	"crypto/tls"
	"fmt"
	"net/http"
)

func main() {
	tr := http.DefaultTransport.(*http.Transport).Clone()
	tr.ForceAttemptHTTP2 = false
	tr.TLSNextProto = make(map[string]func(authority string, c *tls.Conn) http.RoundTripper)
	tr.TLSClientConfig = &tls.Config{}
	httpClient := &http.Client{
		Transport: tr,
	}
	resp, err := httpClient.Get("https://google.com/")
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	fmt.Println("Response status:", resp.Status)
}

which will directly reflect in your programs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
4 participants
You can’t perform that action at this time.