aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/net
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2020-04-03 15:01:34 -0700
committerIan Lance Taylor <iant@golang.org>2020-04-03 15:01:34 -0700
commit00eb71c43c74cc5143b60d470450c3981037ed3c (patch)
tree66833e46153e7869903229adb39ebb1a86c86169 /libgo/go/net
parent213caedb0104ed919b67b3446a53f06054d62fec (diff)
parentff229375721d1763a18ec76403aa1215b2932fb3 (diff)
downloadgcc-00eb71c43c74cc5143b60d470450c3981037ed3c.zip
gcc-00eb71c43c74cc5143b60d470450c3981037ed3c.tar.gz
gcc-00eb71c43c74cc5143b60d470450c3981037ed3c.tar.bz2
Merge from trunk revision ff229375721d1763a18ec76403aa1215b2932fb3
Diffstat (limited to 'libgo/go/net')
-rw-r--r--libgo/go/net/dial_test.go2
-rw-r--r--libgo/go/net/dnsclient_unix_test.go2
-rw-r--r--libgo/go/net/http/cgi/integration_test.go (renamed from libgo/go/net/http/cgi/matryoshka_test.go)0
-rw-r--r--libgo/go/net/http/client.go17
-rw-r--r--libgo/go/net/http/httputil/reverseproxy.go8
-rw-r--r--libgo/go/net/http/omithttp2.go4
-rw-r--r--libgo/go/net/http/request.go12
-rw-r--r--libgo/go/net/http/transfer.go125
-rw-r--r--libgo/go/net/http/transfer_test.go284
-rw-r--r--libgo/go/net/http/transport.go27
-rw-r--r--libgo/go/net/http/transport_test.go70
-rw-r--r--libgo/go/net/lookup_test.go13
-rw-r--r--libgo/go/net/net.go1
13 files changed, 143 insertions, 422 deletions
diff --git a/libgo/go/net/dial_test.go b/libgo/go/net/dial_test.go
index ae40079..493cdfc 100644
--- a/libgo/go/net/dial_test.go
+++ b/libgo/go/net/dial_test.go
@@ -174,7 +174,7 @@ func dialClosedPort(t *testing.T) (actual, expected time.Duration) {
}
addr := l.Addr().String()
l.Close()
- // On OpenBSD, interference from TestSelfConnect is mysteriously
+ // On OpenBSD, interference from TestTCPSelfConnect is mysteriously
// causing the first attempt to hang for a few seconds, so we throw
// away the first result and keep the second.
for i := 1; ; i++ {
diff --git a/libgo/go/net/dnsclient_unix_test.go b/libgo/go/net/dnsclient_unix_test.go
index 6d72817..e8f81e8 100644
--- a/libgo/go/net/dnsclient_unix_test.go
+++ b/libgo/go/net/dnsclient_unix_test.go
@@ -173,7 +173,7 @@ func TestAvoidDNSName(t *testing.T) {
// Without stuff before onion/local, they're fine to
// use DNS. With a search path,
- // "onion.vegegtables.com" can use DNS. Without a
+ // "onion.vegetables.com" can use DNS. Without a
// search path (or with a trailing dot), the queries
// are just kinda useless, but don't reveal anything
// private.
diff --git a/libgo/go/net/http/cgi/matryoshka_test.go b/libgo/go/net/http/cgi/integration_test.go
index 32d59c0..32d59c0 100644
--- a/libgo/go/net/http/cgi/matryoshka_test.go
+++ b/libgo/go/net/http/cgi/integration_test.go
diff --git a/libgo/go/net/http/client.go b/libgo/go/net/http/client.go
index 6a8c59a..a496f1c 100644
--- a/libgo/go/net/http/client.go
+++ b/libgo/go/net/http/client.go
@@ -288,10 +288,17 @@ func timeBeforeContextDeadline(t time.Time, ctx context.Context) bool {
// knownRoundTripperImpl reports whether rt is a RoundTripper that's
// maintained by the Go team and known to implement the latest
-// optional semantics (notably contexts).
-func knownRoundTripperImpl(rt RoundTripper) bool {
- switch rt.(type) {
- case *Transport, *http2Transport:
+// optional semantics (notably contexts). The Request is used
+// to check whether this particular request is using an alternate protocol,
+// in which case we need to check the RoundTripper for that protocol.
+func knownRoundTripperImpl(rt RoundTripper, req *Request) bool {
+ switch t := rt.(type) {
+ case *Transport:
+ if altRT := t.alternateRoundTripper(req); altRT != nil {
+ return knownRoundTripperImpl(altRT, req)
+ }
+ return true
+ case *http2Transport, http2noDialH2RoundTripper:
return true
}
// There's a very minor chance of a false positive with this.
@@ -319,7 +326,7 @@ func setRequestCancel(req *Request, rt RoundTripper, deadline time.Time) (stopTi
if deadline.IsZero() {
return nop, alwaysFalse
}
- knownTransport := knownRoundTripperImpl(rt)
+ knownTransport := knownRoundTripperImpl(rt, req)
oldCtx := req.Context()
if req.Cancel == nil && knownTransport {
diff --git a/libgo/go/net/http/httputil/reverseproxy.go b/libgo/go/net/http/httputil/reverseproxy.go
index e8f7df2..4d6a085 100644
--- a/libgo/go/net/http/httputil/reverseproxy.go
+++ b/libgo/go/net/http/httputil/reverseproxy.go
@@ -24,6 +24,14 @@ import (
// ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the
// client.
+//
+// ReverseProxy automatically sets the client IP as the value of the
+// X-Forwarded-For header.
+// If an X-Forwarded-For header already exists, the client IP is
+// appended to the existing values.
+// To prevent IP spoofing, be sure to delete any pre-existing
+// X-Forwarded-For header coming from the client or
+// an untrusted proxy.
type ReverseProxy struct {
// Director must be a function which modifies
// the request into a new request to be sent
diff --git a/libgo/go/net/http/omithttp2.go b/libgo/go/net/http/omithttp2.go
index a0b33e9..307d93a 100644
--- a/libgo/go/net/http/omithttp2.go
+++ b/libgo/go/net/http/omithttp2.go
@@ -36,6 +36,10 @@ type http2erringRoundTripper struct{}
func (http2erringRoundTripper) RoundTrip(*Request) (*Response, error) { panic(noHTTP2) }
+type http2noDialH2RoundTripper struct{}
+
+func (http2noDialH2RoundTripper) RoundTrip(*Request) (*Response, error) { panic(noHTTP2) }
+
type http2noDialClientConnPool struct {
http2clientConnPool http2clientConnPool
}
diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go
index 8dd9fe1..88fa093 100644
--- a/libgo/go/net/http/request.go
+++ b/libgo/go/net/http/request.go
@@ -1223,17 +1223,17 @@ func parsePostForm(r *Request) (vs url.Values, err error) {
// For all requests, ParseForm parses the raw query from the URL and updates
// r.Form.
//
-// For POST, PUT, and PATCH requests, it also parses the request body as a form
-// and puts the results into both r.PostForm and r.Form. Request body parameters
-// take precedence over URL query string values in r.Form.
+// For POST, PUT, and PATCH requests, it also reads the request body, parses it
+// as a form and puts the results into both r.PostForm and r.Form. Request body
+// parameters take precedence over URL query string values in r.Form.
+//
+// If the request Body's size has not already been limited by MaxBytesReader,
+// the size is capped at 10MB.
//
// For other HTTP methods, or when the Content-Type is not
// application/x-www-form-urlencoded, the request Body is not read, and
// r.PostForm is initialized to a non-nil, empty value.
//
-// If the request Body's size has not already been limited by MaxBytesReader,
-// the size is capped at 10MB.
-//
// ParseMultipartForm calls ParseForm automatically.
// ParseForm is idempotent.
func (r *Request) ParseForm() error {
diff --git a/libgo/go/net/http/transfer.go b/libgo/go/net/http/transfer.go
index 1d6a987..2e01a07 100644
--- a/libgo/go/net/http/transfer.go
+++ b/libgo/go/net/http/transfer.go
@@ -7,7 +7,6 @@ package http
import (
"bufio"
"bytes"
- "compress/gzip"
"errors"
"fmt"
"io"
@@ -467,34 +466,6 @@ func suppressedHeaders(status int) []string {
return nil
}
-// proxyingReadCloser is a composite type that accepts and proxies
-// io.Read and io.Close calls to its respective Reader and Closer.
-//
-// It is composed of:
-// a) a top-level reader e.g. the result of decompression
-// b) a symbolic Closer e.g. the result of decompression, the
-// original body and the connection itself.
-type proxyingReadCloser struct {
- io.Reader
- io.Closer
-}
-
-// multiCloser implements io.Closer and allows a bunch of io.Closer values
-// to all be closed once.
-// Example usage is with proxyingReadCloser if we are decompressing a response
-// body on the fly and would like to close both *gzip.Reader and underlying body.
-type multiCloser []io.Closer
-
-func (mc multiCloser) Close() error {
- var err error
- for _, c := range mc {
- if err1 := c.Close(); err1 != nil && err == nil {
- err = err1
- }
- }
- return err
-}
-
// msg is *Request or *Response.
func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
t := &transferReader{RequestMethod: "GET"}
@@ -572,7 +543,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
// Prepare body reader. ContentLength < 0 means chunked encoding
// or close connection when finished, since multipart is not supported yet
switch {
- case chunked(t.TransferEncoding) || implicitlyChunked(t.TransferEncoding):
+ case chunked(t.TransferEncoding):
if noResponseBodyExpected(t.RequestMethod) || !bodyAllowedForStatus(t.StatusCode) {
t.Body = NoBody
} else {
@@ -593,21 +564,6 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
}
}
- // Finally if "gzip" was one of the requested transfer-encodings,
- // we'll unzip the concatenated body/payload of the request.
- // TODO: As we support more transfer-encodings, extract
- // this code and apply the un-codings in reverse.
- if t.Body != NoBody && gzipped(t.TransferEncoding) {
- zr, err := gzip.NewReader(t.Body)
- if err != nil {
- return fmt.Errorf("http: failed to gunzip body: %v", err)
- }
- t.Body = &proxyingReadCloser{
- Reader: zr,
- Closer: multiCloser{zr, t.Body},
- }
- }
-
// Unify output
switch rr := msg.(type) {
case *Request:
@@ -627,41 +583,8 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
return nil
}
-// Checks whether chunked is the last part of the encodings stack
-func chunked(te []string) bool { return len(te) > 0 && te[len(te)-1] == "chunked" }
-
-// implicitlyChunked is a helper to check for implicity of chunked, because
-// RFC 7230 Section 3.3.1 says that the sender MUST apply chunked as the final
-// payload body to ensure that the message is framed for both the request
-// and the body. Since "identity" is incompatible with any other transformational
-// encoding cannot co-exist, the presence of "identity" will cause implicitlyChunked
-// to return false.
-func implicitlyChunked(te []string) bool {
- if len(te) == 0 { // No transfer-encodings passed in, so not implicitly chunked.
- return false
- }
- for _, tei := range te {
- if tei == "identity" {
- return false
- }
- }
- return true
-}
-
-func isGzipTransferEncoding(tei string) bool {
- // RFC 7230 4.2.3 requests that "x-gzip" SHOULD be considered the same as "gzip".
- return tei == "gzip" || tei == "x-gzip"
-}
-
-// Checks where either of "gzip" or "x-gzip" are contained in transfer encodings.
-func gzipped(te []string) bool {
- for _, tei := range te {
- if isGzipTransferEncoding(tei) {
- return true
- }
- }
- return false
-}
+// Checks whether chunked is part of the encodings stack
+func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" }
// Checks whether the encoding is explicitly "identity".
func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" }
@@ -697,47 +620,25 @@ func (t *transferReader) fixTransferEncoding() error {
encodings := strings.Split(raw[0], ",")
te := make([]string, 0, len(encodings))
-
- // When adding new encodings, please maintain the invariant:
- // if chunked encoding is present, it must always
- // come last and it must be applied only once.
- // See RFC 7230 Section 3.3.1 Transfer-Encoding.
- for i, encoding := range encodings {
+ // TODO: Even though we only support "identity" and "chunked"
+ // encodings, the loop below is designed with foresight. One
+ // invariant that must be maintained is that, if present,
+ // chunked encoding must always come first.
+ for _, encoding := range encodings {
encoding = strings.ToLower(strings.TrimSpace(encoding))
-
+ // "identity" encoding is not recorded
if encoding == "identity" {
- // "identity" should not be mixed with other transfer-encodings/compressions
- // because it means "no compression, no transformation".
- if len(encodings) != 1 {
- return &badStringError{`"identity" when present must be the only transfer encoding`, strings.Join(encodings, ",")}
- }
- // "identity" is not recorded.
break
}
-
- switch {
- case encoding == "chunked":
- // "chunked" MUST ALWAYS be the last
- // encoding as per the loop invariant.
- // That is:
- // Invalid: [chunked, gzip]
- // Valid: [gzip, chunked]
- if i+1 != len(encodings) {
- return &badStringError{"chunked must be applied only once, as the last encoding", strings.Join(encodings, ",")}
- }
- // Supported otherwise.
-
- case isGzipTransferEncoding(encoding):
- // Supported
-
- default:
+ if encoding != "chunked" {
return &unsupportedTEError{fmt.Sprintf("unsupported transfer encoding: %q", encoding)}
}
-
te = te[0 : len(te)+1]
te[len(te)-1] = encoding
}
-
+ if len(te) > 1 {
+ return &badStringError{"too many transfer encodings", strings.Join(te, ",")}
+ }
if len(te) > 0 {
// RFC 7230 3.3.2 says "A sender MUST NOT send a
// Content-Length header field in any message that
diff --git a/libgo/go/net/http/transfer_test.go b/libgo/go/net/http/transfer_test.go
index a8ce2d3..65009ee 100644
--- a/libgo/go/net/http/transfer_test.go
+++ b/libgo/go/net/http/transfer_test.go
@@ -7,7 +7,6 @@ package http
import (
"bufio"
"bytes"
- "compress/gzip"
"crypto/rand"
"fmt"
"io"
@@ -62,6 +61,7 @@ func TestFinalChunkedBodyReadEOF(t *testing.T) {
buf := make([]byte, len(want))
n, err := res.Body.Read(buf)
if n != len(want) || err != io.EOF {
+ t.Logf("body = %#v", res.Body)
t.Errorf("Read = %v, %v; want %d, EOF", n, err, len(want))
}
if string(buf) != want {
@@ -290,7 +290,7 @@ func TestFixTransferEncoding(t *testing.T) {
},
{
hdr: Header{"Transfer-Encoding": {"chunked, chunked", "identity", "chunked"}},
- wantErr: &badStringError{"chunked must be applied only once, as the last encoding", "chunked, chunked"},
+ wantErr: &badStringError{"too many transfer encodings", "chunked,chunked"},
},
{
hdr: Header{"Transfer-Encoding": {"chunked"}},
@@ -310,283 +310,3 @@ func TestFixTransferEncoding(t *testing.T) {
}
}
}
-
-func gzipIt(s string) string {
- buf := new(bytes.Buffer)
- gw := gzip.NewWriter(buf)
- gw.Write([]byte(s))
- gw.Close()
- return buf.String()
-}
-
-func TestUnitTestProxyingReadCloserClosesBody(t *testing.T) {
- var checker closeChecker
- buf := new(bytes.Buffer)
- buf.WriteString("Hello, Gophers!")
- prc := &proxyingReadCloser{
- Reader: buf,
- Closer: &checker,
- }
- prc.Close()
-
- read, err := ioutil.ReadAll(prc)
- if err != nil {
- t.Fatalf("Read error: %v", err)
- }
- if g, w := string(read), "Hello, Gophers!"; g != w {
- t.Errorf("Read mismatch: got %q want %q", g, w)
- }
-
- if checker.closed != true {
- t.Fatal("closeChecker.Close was never invoked")
- }
-}
-
-func TestGzipTransferEncoding_request(t *testing.T) {
- helloWorldGzipped := gzipIt("Hello, World!")
-
- tests := []struct {
- payload string
- wantErr string
- wantBody string
- }{
-
- {
- // The case of "chunked" properly applied as the last encoding
- // and a gzipped request payload that is streamed in 3 parts.
- payload: `POST / HTTP/1.1
-Host: golang.org
-Transfer-Encoding: gzip, chunked
-Content-Type: text/html; charset=UTF-8
-
-` + fmt.Sprintf("%02x\r\n%s\r\n%02x\r\n%s\r\n%02x\r\n%s\r\n0\r\n\r\n",
- 3, helloWorldGzipped[:3],
- 5, helloWorldGzipped[3:8],
- len(helloWorldGzipped)-8, helloWorldGzipped[8:]),
- wantBody: `Hello, World!`,
- },
-
- {
- // The request specifies "Transfer-Encoding: chunked" so its body must be left untouched.
- payload: `PUT / HTTP/1.1
-Host: golang.org
-Transfer-Encoding: chunked
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-` + fmt.Sprintf("%0x\r\n%s\r\n0\r\n\r\n", len(helloWorldGzipped), helloWorldGzipped),
- // We want that payload as it was sent.
- wantBody: helloWorldGzipped,
- },
-
- {
- // Valid request, the body doesn't have "Transfer-Encoding: chunked" but implicitly encoded
- // for chunking as per the advisory from RFC 7230 3.3.1 which advises for cases where.
- payload: `POST / HTTP/1.1
-Host: localhost
-Transfer-Encoding: gzip
-Content-Type: text/html; charset=UTF-8
-
-` + fmt.Sprintf("%0x\r\n%s\r\n0\r\n\r\n", len(helloWorldGzipped), helloWorldGzipped),
- wantBody: `Hello, World!`,
- },
-
- {
- // Invalid request, the body isn't chunked nor is the connection terminated immediately
- // hence invalid as per the advisory from RFC 7230 3.3.1 which advises for cases where
- // a Transfer-Encoding that isn't finally chunked is provided.
- payload: `PUT / HTTP/1.1
-Host: golang.org
-Transfer-Encoding: gzip
-Content-Length: 0
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-`,
- wantErr: `EOF`,
- },
-
- {
- // The case of chunked applied before another encoding.
- payload: `PUT / HTTP/1.1
-Location: golang.org
-Transfer-Encoding: chunked, gzip
-Content-Length: 0
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-`,
- wantErr: `chunked must be applied only once, as the last encoding "chunked, gzip"`,
- },
-
- {
- // The case of chunked properly applied as the
- // last encoding BUT with a bad "Content-Length".
- payload: `POST / HTTP/1.1
-Host: golang.org
-Transfer-Encoding: gzip, chunked
-Content-Length: 10
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-` + "0\r\n\r\n",
- wantErr: "EOF",
- },
- }
-
- for i, tt := range tests {
- req, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.payload)))
- if tt.wantErr != "" {
- if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
- t.Errorf("test %d. Error mismatch\nGot: %v\nWant: %s", i, err, tt.wantErr)
- }
- continue
- }
-
- if err != nil {
- t.Errorf("test %d. Unexpected ReadRequest error: %v\nPayload:\n%s", i, err, tt.payload)
- continue
- }
-
- got, err := ioutil.ReadAll(req.Body)
- req.Body.Close()
- if err != nil {
- t.Errorf("test %d. Failed to read response body: %v", i, err)
- }
- if g, w := string(got), tt.wantBody; g != w {
- t.Errorf("test %d. Request body mimsatch\nGot:\n%s\n\nWant:\n%s", i, g, w)
- }
- }
-}
-
-func TestGzipTransferEncoding_response(t *testing.T) {
- helloWorldGzipped := gzipIt("Hello, World!")
-
- tests := []struct {
- payload string
- wantErr string
- wantBody string
- }{
-
- {
- // The case of "chunked" properly applied as the last encoding
- // and a gzipped payload that is streamed in 3 parts.
- payload: `HTTP/1.1 302 Found
-Location: https://golang.org/
-Transfer-Encoding: gzip, chunked
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-` + fmt.Sprintf("%02x\r\n%s\r\n%02x\r\n%s\r\n%02x\r\n%s\r\n0\r\n\r\n",
- 3, helloWorldGzipped[:3],
- 5, helloWorldGzipped[3:8],
- len(helloWorldGzipped)-8, helloWorldGzipped[8:]),
- wantBody: `Hello, World!`,
- },
-
- {
- // The response specifies "Transfer-Encoding: chunked" so response body must be left untouched.
- payload: `HTTP/1.1 302 Found
-Location: https://golang.org/
-Transfer-Encoding: chunked
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-` + fmt.Sprintf("%0x\r\n%s\r\n0\r\n\r\n", len(helloWorldGzipped), helloWorldGzipped),
- // We want that payload as it was sent.
- wantBody: helloWorldGzipped,
- },
-
- {
- // Valid response, the body doesn't have "Transfer-Encoding: chunked" but implicitly encoded
- // for chunking as per the advisory from RFC 7230 3.3.1 which advises for cases where.
- payload: `HTTP/1.1 302 Found
-Location: https://golang.org/
-Transfer-Encoding: gzip
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-` + fmt.Sprintf("%0x\r\n%s\r\n0\r\n\r\n", len(helloWorldGzipped), helloWorldGzipped),
- wantBody: `Hello, World!`,
- },
-
- {
- // Invalid response, the body isn't chunked nor is the connection terminated immediately
- // hence invalid as per the advisory from RFC 7230 3.3.1 which advises for cases where
- // a Transfer-Encoding that isn't finally chunked is provided.
- payload: `HTTP/1.1 302 Found
-Location: https://golang.org/
-Transfer-Encoding: gzip
-Content-Length: 0
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-`,
- wantErr: `EOF`,
- },
-
- {
- // The case of chunked applied before another encoding.
- payload: `HTTP/1.1 302 Found
-Location: https://golang.org/
-Transfer-Encoding: chunked, gzip
-Content-Length: 0
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-`,
- wantErr: `chunked must be applied only once, as the last encoding "chunked, gzip"`,
- },
-
- {
- // The case of chunked properly applied as the
- // last encoding BUT with a bad "Content-Length".
- payload: `HTTP/1.1 302 Found
-Location: https://golang.org/
-Transfer-Encoding: gzip, chunked
-Content-Length: 10
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-` + "0\r\n\r\n",
- wantErr: "EOF",
- },
-
- {
- // Including "identity" more than once.
- payload: `HTTP/1.1 200 OK
-Location: https://golang.org/
-Transfer-Encoding: identity, identity
-Content-Length: 0
-Connection: close
-Content-Type: text/html; charset=UTF-8
-
-` + "0\r\n\r\n",
- wantErr: `"identity" when present must be the only transfer encoding "identity, identity"`,
- },
- }
-
- for i, tt := range tests {
- res, err := ReadResponse(bufio.NewReader(strings.NewReader(tt.payload)), nil)
- if tt.wantErr != "" {
- if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
- t.Errorf("test %d. Error mismatch\nGot: %v\nWant: %s", i, err, tt.wantErr)
- }
- continue
- }
-
- if err != nil {
- t.Errorf("test %d. Unexpected ReadResponse error: %v\nPayload:\n%s", i, err, tt.payload)
- continue
- }
-
- got, err := ioutil.ReadAll(res.Body)
- res.Body.Close()
- if err != nil {
- t.Errorf("test %d. Failed to read response body: %v", i, err)
- }
- if g, w := string(got), tt.wantBody; g != w {
- t.Errorf("test %d. Response body mimsatch\nGot:\n%s\n\nWant:\n%s", i, g, w)
- }
- }
-}
diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go
index 64d8510..d0bfdb4 100644
--- a/libgo/go/net/http/transport.go
+++ b/libgo/go/net/http/transport.go
@@ -469,6 +469,17 @@ func (t *Transport) useRegisteredProtocol(req *Request) bool {
return true
}
+// alternateRoundTripper returns the alternate RoundTripper to use
+// for this request if the Request's URL scheme requires one,
+// or nil for the normal case of using the Transport.
+func (t *Transport) alternateRoundTripper(req *Request) RoundTripper {
+ if !t.useRegisteredProtocol(req) {
+ return nil
+ }
+ altProto, _ := t.altProto.Load().(map[string]RoundTripper)
+ return altProto[req.URL.Scheme]
+}
+
// roundTrip implements a RoundTripper over HTTP.
func (t *Transport) roundTrip(req *Request) (*Response, error) {
t.nextProtoOnce.Do(t.onceSetNextProtoDefaults)
@@ -500,12 +511,9 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) {
}
}
- if t.useRegisteredProtocol(req) {
- altProto, _ := t.altProto.Load().(map[string]RoundTripper)
- if altRT := altProto[scheme]; altRT != nil {
- if resp, err := altRT.RoundTrip(req); err != ErrSkipAltProtocol {
- return resp, err
- }
+ if altRT := t.alternateRoundTripper(req); altRT != nil {
+ if resp, err := altRT.RoundTrip(req); err != ErrSkipAltProtocol {
+ return resp, err
}
}
if !isHTTP {
@@ -1559,15 +1567,16 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (pconn *pers
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)
- }
// If there's no done channel (no deadline or cancellation
// from the caller possible), at least set some (long)
diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go
index 2256813..1e0334d 100644
--- a/libgo/go/net/http/transport_test.go
+++ b/libgo/go/net/http/transport_test.go
@@ -1550,6 +1550,44 @@ 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 = h.Clone()
+
+ 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
@@ -6109,3 +6147,35 @@ func TestTransportDecrementConnWhenIdleConnRemoved(t *testing.T) {
t.Errorf("error occurred: %v", err)
}
}
+
+// Issue 36820
+// Test that we use the older backward compatible cancellation protocol
+// when a RoundTripper is registered via RegisterProtocol.
+func TestAltProtoCancellation(t *testing.T) {
+ defer afterTest(t)
+ tr := &Transport{}
+ c := &Client{
+ Transport: tr,
+ Timeout: time.Millisecond,
+ }
+ tr.RegisterProtocol("timeout", timeoutProto{})
+ _, err := c.Get("timeout://bar.com/path")
+ if err == nil {
+ t.Error("request unexpectedly succeeded")
+ } else if !strings.Contains(err.Error(), timeoutProtoErr.Error()) {
+ t.Errorf("got error %q, does not contain expected string %q", err, timeoutProtoErr)
+ }
+}
+
+var timeoutProtoErr = errors.New("canceled as expected")
+
+type timeoutProto struct{}
+
+func (timeoutProto) RoundTrip(req *Request) (*Response, error) {
+ select {
+ case <-req.Cancel:
+ return nil, timeoutProtoErr
+ case <-time.After(5 * time.Second):
+ return nil, errors.New("request was not canceled")
+ }
+}
diff --git a/libgo/go/net/lookup_test.go b/libgo/go/net/lookup_test.go
index 8a41510..2bc5592 100644
--- a/libgo/go/net/lookup_test.go
+++ b/libgo/go/net/lookup_test.go
@@ -998,12 +998,16 @@ func TestConcurrentPreferGoResolversDial(t *testing.T) {
defer wg.Done()
_, err := r.LookupIPAddr(context.Background(), "google.com")
if err != nil {
- t.Fatalf("lookup failed for resolver %d: %q", index, err)
+ t.Errorf("lookup failed for resolver %d: %q", index, err)
}
}(resolver.Resolver, i)
}
wg.Wait()
+ if t.Failed() {
+ t.FailNow()
+ }
+
for i, resolver := range resolvers {
if !resolver.dialed {
t.Errorf("custom resolver %d not dialed during lookup", i)
@@ -1175,12 +1179,9 @@ func TestWithUnexpiredValuesPreserved(t *testing.T) {
}
}
-// Issue 31586: don't crash on null byte in name
+// Issue 31597: don't panic on null byte in name
func TestLookupNullByte(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
testenv.SkipFlakyNet(t)
- _, err := LookupHost("foo\x00bar") // used to crash on Windows
- if err == nil {
- t.Errorf("unexpected success")
- }
+ LookupHost("foo\x00bar") // check that it doesn't panic; it used to on Windows
}
diff --git a/libgo/go/net/net.go b/libgo/go/net/net.go
index 38c6b99..1d7e5e7 100644
--- a/libgo/go/net/net.go
+++ b/libgo/go/net/net.go
@@ -452,6 +452,7 @@ type OpError struct {
Addr Addr
// Err is the error that occurred during the operation.
+ // The Error method panics if the error is nil.
Err error
}