Commit c5af2aa0 authored by Bryan C. Mills's avatar Bryan C. Mills

[release-branch.go1.12] 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.

Updates #36431
Fixes #36433

Change-Id: I5cb414f391dddf8c23d85427eb6973f14c949025
Reviewed-on: https://go-review.googlesource.com/c/go/+/213638
Run-TryBot: Bryan C. Mills <bcmills@google.com>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
(cherry picked from commit 249c85d3)
Reviewed-on: https://go-review.googlesource.com/c/go/+/213677
parent e42221dc
...@@ -1309,15 +1309,16 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistCon ...@@ -1309,15 +1309,16 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistCon
if hdr == nil { if hdr == nil {
hdr = make(Header) hdr = make(Header)
} }
if pa := cm.proxyAuth(); pa != "" {
hdr = hdr.clone()
hdr.Set("Proxy-Authorization", pa)
}
connectReq := &Request{ connectReq := &Request{
Method: "CONNECT", Method: "CONNECT",
URL: &url.URL{Opaque: cm.targetAddr}, URL: &url.URL{Opaque: cm.targetAddr},
Host: cm.targetAddr, Host: cm.targetAddr,
Header: hdr, Header: hdr,
} }
if pa := cm.proxyAuth(); pa != "" {
connectReq.Header.Set("Proxy-Authorization", pa)
}
connectReq.Write(conn) connectReq.Write(conn)
// Read response. // Read response.
......
...@@ -1371,6 +1371,47 @@ func TestTransportDialPreservesNetOpProxyError(t *testing.T) { ...@@ -1371,6 +1371,47 @@ func TestTransportDialPreservesNetOpProxyError(t *testing.T) {
} }
} }
// 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 // TestTransportGzipRecursive sends a gzip quine and checks that the
// client gets the same value back. This is more cute than anything, // client gets the same value back. This is more cute than anything,
// but checks that we don't recurse forever, and checks that // but checks that we don't recurse forever, and checks that
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment