Unexpected behaviour of net.http package

Please take a look and run the test snippet below.

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

func TestClient(t *testing.T) {
	config := tls.Config{InsecureSkipVerify: true}

	transport1 := &http.Transport{
		TLSClientConfig: &config,
	}
	client1 := &http.Client{Transport: transport1}
	_, err1 := client1.Get("https://www.google.com")
	if err1 != nil {
		t.Error(err1)
	}

	transport2 := &http.Transport{
		ForceAttemptHTTP2: true,
		TLSClientConfig:   &config,
	}
	client2 := &http.Client{Transport: transport2}
	_, err2 := client2.Get("https://www.google.com")
	if err2 != nil {
		t.Error(err2)
	}

	_, err3 := client1.Get("https://www.google.com")
	if err3 != nil {
		t.Error(err3)
	}
}

I created a singe tls.Config object that is shared in two different http.Transport objects. According to the following comment from the crypto/tls/common.go file, config explicitly allows reuse:

// A Config structure is used to configure a TLS client or server.
// After one has been passed to a TLS function it must not be
// modified. A Config may be reused; the tls package will also not
// modify it.
type Config struct {

One of the transport objects has ForceAttemptHTTP2 field set to true. I use those two http.Transport objects to create two http.Client objects and perform a Get request to www.google.com.

First, and second requests succeed. The third request attempt _, err3 := client1.Get("https://www.google.com") errors out and causes the test to fail. It appears that using the client2 to perform a Get request breaks the client1 for some reason.

This behaviour seems wrong to me. Thoughts?

try closing the response body between requests

For the records, this has been reported as an issue, and the thing is that the statement “the tls package will also not modify it” is true but doesn’t say anything about other packages. It turned out that net/http does modify a tls.Config.

Ye, that was me who reported it. net/http package should not modify user supplied tls.Config too. Especially when there are no proper deep copy methods, and no explicit warnings in the documentation about this. And when there is a established contract in the tls package that it will not modify it.

1 Like