src/net/http/transport.go | 7 ++++--- src/net/http/transport_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ diff --git a/src/net/http/transport.go b/src/net/http/transport.go index e946760963c5b9877c4193de9ad6e7c035b6e1ca..df41cbd7743d8d884befd3eef71d6545c782846e 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -1309,14 +1309,15 @@ hdr := t.ProxyConnectHeader if hdr == nil { hdr = make(Header) } + if pa := cm.proxyAuth(); pa != "" { + hdr = hdr.clone() + hdr.Set("Proxy-Authorization", pa) + } connectReq := &Request{ Method: "CONNECT", URL: &url.URL{Opaque: cm.targetAddr}, Host: cm.targetAddr, Header: hdr, - } - if pa := cm.proxyAuth(); pa != "" { - connectReq.Header.Set("Proxy-Authorization", pa) } connectReq.Write(conn) diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 5e5438a708b48aef3e3a720d4c93a02b64ff079f..a2f95911d938585acb3b3a034b8f51e5605d8774 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -1371,6 +1371,47 @@ t.Errorf("Got error %#v; want %#v", oe, want) } } +// Issue 36431: calls to RoundTrip should not mutate t.ProxyConnectHeader. +// +// (A bug caused dialConn to instead write the per-request Proxy-Authorization +// header through to the shared Header instance, introducing a data race.) +func TestTransportProxyDialDoesNotMutateProxyConnectHeader(t *testing.T) { + setParallel(t) + defer afterTest(t) + + proxy := httptest.NewTLSServer(NotFoundHandler()) + defer proxy.Close() + c := proxy.Client() + + tr := c.Transport.(*Transport) + tr.Proxy = func(*Request) (*url.URL, error) { + u, _ := url.Parse(proxy.URL) + u.User = url.UserPassword("aladdin", "opensesame") + return u, nil + } + h := tr.ProxyConnectHeader + if h == nil { + h = make(Header) + } + tr.ProxyConnectHeader = make(Header, len(h)) + for k, vals := range h { + tr.ProxyConnectHeader[k] = append([]string(nil), vals...) + } + + req, err := NewRequest("GET", "https://golang.fake.tld/", nil) + if err != nil { + t.Fatal(err) + } + _, err = c.Do(req) + if err == nil { + t.Errorf("unexpected Get success") + } + + if !reflect.DeepEqual(tr.ProxyConnectHeader, h) { + t.Errorf("tr.ProxyConnectHeader = %v; want %v", tr.ProxyConnectHeader, h) + } +} + // TestTransportGzipRecursive sends a gzip quine and checks that the // client gets the same value back. This is more cute than anything, // but checks that we don't recurse forever, and checks that