Skip to content

Commit 249c85d

Browse files
author
Bryan C. Mills
committed
net/http: avoid writing to Transport.ProxyConnectHeader
Previously, we accidentally wrote the Proxy-Authorization header for the initial CONNECT request to the shared ProxyConnectHeader map when it was non-nil. Fixes #36431 Change-Id: I5cb414f391dddf8c23d85427eb6973f14c949025 Reviewed-on: https://go-review.googlesource.com/c/go/+/213638 Run-TryBot: Bryan C. Mills <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 98418c9 commit 249c85d

2 files changed

Lines changed: 42 additions & 3 deletions

File tree

src/net/http/transport.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,15 +1559,16 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (pconn *pers
15591559
if hdr == nil {
15601560
hdr = make(Header)
15611561
}
1562+
if pa := cm.proxyAuth(); pa != "" {
1563+
hdr = hdr.Clone()
1564+
hdr.Set("Proxy-Authorization", pa)
1565+
}
15621566
connectReq := &Request{
15631567
Method: "CONNECT",
15641568
URL: &url.URL{Opaque: cm.targetAddr},
15651569
Host: cm.targetAddr,
15661570
Header: hdr,
15671571
}
1568-
if pa := cm.proxyAuth(); pa != "" {
1569-
connectReq.Header.Set("Proxy-Authorization", pa)
1570-
}
15711572

15721573
// If there's no done channel (no deadline or cancellation
15731574
// from the caller possible), at least set some (long)

src/net/http/transport_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,6 +1550,44 @@ func TestTransportDialPreservesNetOpProxyError(t *testing.T) {
15501550
}
15511551
}
15521552

1553+
// Issue 36431: calls to RoundTrip should not mutate t.ProxyConnectHeader.
1554+
//
1555+
// (A bug caused dialConn to instead write the per-request Proxy-Authorization
1556+
// header through to the shared Header instance, introducing a data race.)
1557+
func TestTransportProxyDialDoesNotMutateProxyConnectHeader(t *testing.T) {
1558+
setParallel(t)
1559+
defer afterTest(t)
1560+
1561+
proxy := httptest.NewTLSServer(NotFoundHandler())
1562+
defer proxy.Close()
1563+
c := proxy.Client()
1564+
1565+
tr := c.Transport.(*Transport)
1566+
tr.Proxy = func(*Request) (*url.URL, error) {
1567+
u, _ := url.Parse(proxy.URL)
1568+
u.User = url.UserPassword("aladdin", "opensesame")
1569+
return u, nil
1570+
}
1571+
h := tr.ProxyConnectHeader
1572+
if h == nil {
1573+
h = make(Header)
1574+
}
1575+
tr.ProxyConnectHeader = h.Clone()
1576+
1577+
req, err := NewRequest("GET", "https://golang.fake.tld/", nil)
1578+
if err != nil {
1579+
t.Fatal(err)
1580+
}
1581+
_, err = c.Do(req)
1582+
if err == nil {
1583+
t.Errorf("unexpected Get success")
1584+
}
1585+
1586+
if !reflect.DeepEqual(tr.ProxyConnectHeader, h) {
1587+
t.Errorf("tr.ProxyConnectHeader = %v; want %v", tr.ProxyConnectHeader, h)
1588+
}
1589+
}
1590+
15531591
// TestTransportGzipRecursive sends a gzip quine and checks that the
15541592
// client gets the same value back. This is more cute than anything,
15551593
// but checks that we don't recurse forever, and checks that

0 commit comments

Comments
 (0)