Description
Go version
go version go1.24.2 darwin/amd64
Output of go env
in your module/workspace:
AR='ar'
CC='clang'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='clang++'
GCCGO='gccgo'
GO111MODULE='on'
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/Users/ipetrenko/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/ipetrenko/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/ml/f6gwv9sn1s91hbj0fzqkswy40000gn/T/go-build2838209554=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='amd64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/Users/ipetrenko/test/client/go.mod'
GOMODCACHE='/Users/ipetrenko/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/ipetrenko/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/Users/ipetrenko/go/pkg/mod/golang.org/[email protected]'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/ipetrenko/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/Users/ipetrenko/go/pkg/mod/golang.org/[email protected]/pkg/tool/darwin_amd64'
GOVCS=''
GOVERSION='go1.24.2'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
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 its 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.
What did you see happen?
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 leads to some changes to the tls.Config struct, which breaks the client1
for some reason.
This behaviour is unexpected, because tls.Config documentation promotes sharing of the config object.
What did you expect to see?
I expect all requests to succeed, and test case not fail.