summaryrefslogtreecommitdiff
path: root/libgo/go/net
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2018-01-17 14:20:29 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2018-01-17 14:20:29 +0000
commitc6d6367f848cfd8381aba41e035c5e7e873667c5 (patch)
treea218e98243463fc27f5053b4444e2544c63cd57a /libgo/go/net
parent9bff0086915f544fa648ea81131f035cb9ce79a4 (diff)
libgo: update to Go1.10beta2 release
Reviewed-on: https://go-review.googlesource.com/87897 From-SVN: r256794
Diffstat (limited to 'libgo/go/net')
-rw-r--r--libgo/go/net/cgo_unix_test.go6
-rw-r--r--libgo/go/net/dial_test.go16
-rw-r--r--libgo/go/net/dnsclient_unix.go2
-rw-r--r--libgo/go/net/dnsclient_unix_test.go41
-rw-r--r--libgo/go/net/fd_unix.go4
-rw-r--r--libgo/go/net/http/clientserver_test.go56
-rw-r--r--libgo/go/net/http/fs_test.go2
-rw-r--r--libgo/go/net/http/h2_bundle.go26
-rw-r--r--libgo/go/net/http/httputil/reverseproxy.go21
-rw-r--r--libgo/go/net/http/httputil/reverseproxy_test.go29
-rw-r--r--libgo/go/net/http/request.go4
-rw-r--r--libgo/go/net/http/response.go7
-rw-r--r--libgo/go/net/http/serve_test.go128
-rw-r--r--libgo/go/net/http/server.go61
-rw-r--r--libgo/go/net/http/transport.go18
-rw-r--r--libgo/go/net/http/transport_internal_test.go22
-rw-r--r--libgo/go/net/ipsock_posix.go4
-rw-r--r--libgo/go/net/lookup.go12
-rw-r--r--libgo/go/net/lookup_test.go28
-rw-r--r--libgo/go/net/net.go2
-rw-r--r--libgo/go/net/protoconn_test.go15
-rw-r--r--libgo/go/net/udpsock_test.go10
-rw-r--r--libgo/go/net/url/url.go33
-rw-r--r--libgo/go/net/url/url_test.go7
24 files changed, 440 insertions, 114 deletions
diff --git a/libgo/go/net/cgo_unix_test.go b/libgo/go/net/cgo_unix_test.go
index e579198e09a..49da9936d3f 100644
--- a/libgo/go/net/cgo_unix_test.go
+++ b/libgo/go/net/cgo_unix_test.go
@@ -13,6 +13,7 @@ import (
)
func TestCgoLookupIP(t *testing.T) {
+ defer dnsWaitGroup.Wait()
ctx := context.Background()
_, err, ok := cgoLookupIP(ctx, "localhost")
if !ok {
@@ -24,6 +25,7 @@ func TestCgoLookupIP(t *testing.T) {
}
func TestCgoLookupIPWithCancel(t *testing.T) {
+ defer dnsWaitGroup.Wait()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
_, err, ok := cgoLookupIP(ctx, "localhost")
@@ -36,6 +38,7 @@ func TestCgoLookupIPWithCancel(t *testing.T) {
}
func TestCgoLookupPort(t *testing.T) {
+ defer dnsWaitGroup.Wait()
ctx := context.Background()
_, err, ok := cgoLookupPort(ctx, "tcp", "smtp")
if !ok {
@@ -47,6 +50,7 @@ func TestCgoLookupPort(t *testing.T) {
}
func TestCgoLookupPortWithCancel(t *testing.T) {
+ defer dnsWaitGroup.Wait()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
_, err, ok := cgoLookupPort(ctx, "tcp", "smtp")
@@ -59,6 +63,7 @@ func TestCgoLookupPortWithCancel(t *testing.T) {
}
func TestCgoLookupPTR(t *testing.T) {
+ defer dnsWaitGroup.Wait()
ctx := context.Background()
_, err, ok := cgoLookupPTR(ctx, "127.0.0.1")
if !ok {
@@ -70,6 +75,7 @@ func TestCgoLookupPTR(t *testing.T) {
}
func TestCgoLookupPTRWithCancel(t *testing.T) {
+ defer dnsWaitGroup.Wait()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
_, err, ok := cgoLookupPTR(ctx, "127.0.0.1")
diff --git a/libgo/go/net/dial_test.go b/libgo/go/net/dial_test.go
index 13fa9faacb5..b5f1dc9e98b 100644
--- a/libgo/go/net/dial_test.go
+++ b/libgo/go/net/dial_test.go
@@ -10,6 +10,7 @@ import (
"internal/poll"
"internal/testenv"
"io"
+ "os"
"runtime"
"sync"
"testing"
@@ -85,11 +86,6 @@ func TestDialerDualStackFDLeak(t *testing.T) {
t.Skip("both IPv4 and IPv6 are required")
}
- closedPortDelay, expectClosedPortDelay := dialClosedPort()
- if closedPortDelay > expectClosedPortDelay {
- t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay)
- }
-
before := sw.Sockets()
origTestHookLookupIP := testHookLookupIP
defer func() { testHookLookupIP = origTestHookLookupIP }()
@@ -115,7 +111,7 @@ func TestDialerDualStackFDLeak(t *testing.T) {
const N = 10
var wg sync.WaitGroup
wg.Add(N)
- d := &Dialer{DualStack: true, Timeout: 100*time.Millisecond + closedPortDelay}
+ d := &Dialer{DualStack: true, Timeout: 5 * time.Second}
for i := 0; i < N; i++ {
go func() {
defer wg.Done()
@@ -639,7 +635,13 @@ func TestDialerLocalAddr(t *testing.T) {
}
c, err := d.Dial(tt.network, addr)
if err == nil && tt.error != nil || err != nil && tt.error == nil {
- t.Errorf("%s %v->%s: got %v; want %v", tt.network, tt.laddr, tt.raddr, err, tt.error)
+ // On Darwin this occasionally times out.
+ // We don't know why. Issue #22019.
+ if runtime.GOOS == "darwin" && tt.error == nil && os.IsTimeout(err) {
+ t.Logf("ignoring timeout error on Darwin; see https://golang.org/issue/22019")
+ } else {
+ t.Errorf("%s %v->%s: got %v; want %v", tt.network, tt.laddr, tt.raddr, err, tt.error)
+ }
}
if err != nil {
if perr := parseDialError(err); perr != nil {
diff --git a/libgo/go/net/dnsclient_unix.go b/libgo/go/net/dnsclient_unix.go
index ff6a4f69dcd..73a507e506b 100644
--- a/libgo/go/net/dnsclient_unix.go
+++ b/libgo/go/net/dnsclient_unix.go
@@ -479,7 +479,9 @@ func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order
var lastErr error
for _, fqdn := range conf.nameList(name) {
for _, qtype := range qtypes {
+ dnsWaitGroup.Add(1)
go func(qtype uint16) {
+ defer dnsWaitGroup.Done()
cname, rrs, err := r.tryOneName(ctx, conf, fqdn, qtype)
lane <- racer{cname, rrs, err}
}(qtype)
diff --git a/libgo/go/net/dnsclient_unix_test.go b/libgo/go/net/dnsclient_unix_test.go
index 94811c96a6b..82a6c6572c2 100644
--- a/libgo/go/net/dnsclient_unix_test.go
+++ b/libgo/go/net/dnsclient_unix_test.go
@@ -203,6 +203,7 @@ var fakeDNSServerSuccessful = fakeDNSServer{func(_, _ string, q *dnsMsg, _ time.
// Issue 13705: don't try to resolve onion addresses, etc
func TestLookupTorOnion(t *testing.T) {
+ defer dnsWaitGroup.Wait()
r := Resolver{PreferGo: true, Dial: fakeDNSServerSuccessful.DialContext}
addrs, err := r.LookupIPAddr(context.Background(), "foo.onion")
if err != nil {
@@ -300,6 +301,8 @@ var updateResolvConfTests = []struct {
}
func TestUpdateResolvConf(t *testing.T) {
+ defer dnsWaitGroup.Wait()
+
r := Resolver{PreferGo: true, Dial: fakeDNSServerSuccessful.DialContext}
conf, err := newResolvConfTest()
@@ -455,6 +458,8 @@ var goLookupIPWithResolverConfigTests = []struct {
}
func TestGoLookupIPWithResolverConfig(t *testing.T) {
+ defer dnsWaitGroup.Wait()
+
fake := fakeDNSServer{func(n, s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
switch s {
case "[2001:4860:4860::8888]:53", "8.8.8.8:53":
@@ -547,6 +552,8 @@ func TestGoLookupIPWithResolverConfig(t *testing.T) {
// Test that goLookupIPOrder falls back to the host file when no DNS servers are available.
func TestGoLookupIPOrderFallbackToFile(t *testing.T) {
+ defer dnsWaitGroup.Wait()
+
fake := fakeDNSServer{func(n, s string, q *dnsMsg, tm time.Time) (*dnsMsg, error) {
r := &dnsMsg{
dnsMsgHdr: dnsMsgHdr{
@@ -603,6 +610,8 @@ func TestGoLookupIPOrderFallbackToFile(t *testing.T) {
// querying the original name instead of an error encountered
// querying a generated name.
func TestErrorForOriginalNameWhenSearching(t *testing.T) {
+ defer dnsWaitGroup.Wait()
+
const fqdn = "doesnotexist.domain"
conf, err := newResolvConfTest()
@@ -657,6 +666,8 @@ func TestErrorForOriginalNameWhenSearching(t *testing.T) {
// Issue 15434. If a name server gives a lame referral, continue to the next.
func TestIgnoreLameReferrals(t *testing.T) {
+ defer dnsWaitGroup.Wait()
+
conf, err := newResolvConfTest()
if err != nil {
t.Fatal(err)
@@ -889,6 +900,8 @@ func TestIgnoreDNSForgeries(t *testing.T) {
// Issue 16865. If a name server times out, continue to the next.
func TestRetryTimeout(t *testing.T) {
+ defer dnsWaitGroup.Wait()
+
conf, err := newResolvConfTest()
if err != nil {
t.Fatal(err)
@@ -945,6 +958,8 @@ func TestRotate(t *testing.T) {
}
func testRotate(t *testing.T, rotate bool, nameservers, wantServers []string) {
+ defer dnsWaitGroup.Wait()
+
conf, err := newResolvConfTest()
if err != nil {
t.Fatal(err)
@@ -1008,6 +1023,8 @@ func mockTXTResponse(q *dnsMsg) *dnsMsg {
// Issue 17448. With StrictErrors enabled, temporary errors should make
// LookupIP fail rather than return a partial result.
func TestStrictErrorsLookupIP(t *testing.T) {
+ defer dnsWaitGroup.Wait()
+
conf, err := newResolvConfTest()
if err != nil {
t.Fatal(err)
@@ -1256,6 +1273,8 @@ func TestStrictErrorsLookupIP(t *testing.T) {
// Issue 17448. With StrictErrors enabled, temporary errors should make
// LookupTXT stop walking the search list.
func TestStrictErrorsLookupTXT(t *testing.T) {
+ defer dnsWaitGroup.Wait()
+
conf, err := newResolvConfTest()
if err != nil {
t.Fatal(err)
@@ -1312,3 +1331,25 @@ func TestStrictErrorsLookupTXT(t *testing.T) {
}
}
}
+
+// Test for a race between uninstalling the test hooks and closing a
+// socket connection. This used to fail when testing with -race.
+func TestDNSGoroutineRace(t *testing.T) {
+ defer dnsWaitGroup.Wait()
+
+ fake := fakeDNSServer{func(n, s string, q *dnsMsg, t time.Time) (*dnsMsg, error) {
+ time.Sleep(10 * time.Microsecond)
+ return nil, poll.ErrTimeout
+ }}
+ r := Resolver{PreferGo: true, Dial: fake.DialContext}
+
+ // The timeout here is less than the timeout used by the server,
+ // so the goroutine started to query the (fake) server will hang
+ // around after this test is done if we don't call dnsWaitGroup.Wait.
+ ctx, cancel := context.WithTimeout(context.Background(), 2*time.Microsecond)
+ defer cancel()
+ _, err := r.LookupIPAddr(ctx, "where.are.they.now")
+ if err == nil {
+ t.Fatal("fake DNS lookup unexpectedly succeeded")
+ }
+}
diff --git a/libgo/go/net/fd_unix.go b/libgo/go/net/fd_unix.go
index e5afd1ae0ae..95d5e4fd0f6 100644
--- a/libgo/go/net/fd_unix.go
+++ b/libgo/go/net/fd_unix.go
@@ -173,7 +173,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (rsa sysc
return rsa, nil
}
default:
- return nil, os.NewSyscallError("getsockopt", err)
+ return nil, os.NewSyscallError("connect", err)
}
runtime.KeepAlive(fd)
}
@@ -321,7 +321,7 @@ func (fd *netFD) dup() (f *os.File, err error) {
// This also puts the old fd into blocking mode, meaning that
// I/O will block the thread instead of letting us use the epoll server.
// Everything will still work, just with more threads.
- if err = syscall.SetNonblock(ns, false); err != nil {
+ if err = fd.pfd.SetBlocking(); err != nil {
return nil, os.NewSyscallError("setnonblock", err)
}
diff --git a/libgo/go/net/http/clientserver_test.go b/libgo/go/net/http/clientserver_test.go
index c8d9fab8b7a..8f2e5749542 100644
--- a/libgo/go/net/http/clientserver_test.go
+++ b/libgo/go/net/http/clientserver_test.go
@@ -1428,3 +1428,59 @@ func testWriteHeader0(t *testing.T, h2 bool) {
t.Error("expected panic in handler")
}
}
+
+// Issue 23010: don't be super strict checking WriteHeader's code if
+// it's not even valid to call WriteHeader then anyway.
+func TestWriteHeaderNoCodeCheck_h1(t *testing.T) { testWriteHeaderAfterWrite(t, h1Mode, false) }
+func TestWriteHeaderNoCodeCheck_h1hijack(t *testing.T) { testWriteHeaderAfterWrite(t, h1Mode, true) }
+func TestWriteHeaderNoCodeCheck_h2(t *testing.T) { testWriteHeaderAfterWrite(t, h2Mode, false) }
+func testWriteHeaderAfterWrite(t *testing.T, h2, hijack bool) {
+ setParallel(t)
+ defer afterTest(t)
+
+ var errorLog lockedBytesBuffer
+ cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
+ if hijack {
+ conn, _, _ := w.(Hijacker).Hijack()
+ defer conn.Close()
+ conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 6\r\n\r\nfoo"))
+ w.WriteHeader(0) // verify this doesn't panic if there's already output; Issue 23010
+ conn.Write([]byte("bar"))
+ return
+ }
+ io.WriteString(w, "foo")
+ w.(Flusher).Flush()
+ w.WriteHeader(0) // verify this doesn't panic if there's already output; Issue 23010
+ io.WriteString(w, "bar")
+ }), func(ts *httptest.Server) {
+ ts.Config.ErrorLog = log.New(&errorLog, "", 0)
+ })
+ defer cst.close()
+ res, err := cst.c.Get(cst.ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer res.Body.Close()
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got, want := string(body), "foobar"; got != want {
+ t.Errorf("got = %q; want %q", got, want)
+ }
+
+ // Also check the stderr output:
+ if h2 {
+ // TODO: also emit this log message for HTTP/2?
+ // We historically haven't, so don't check.
+ return
+ }
+ gotLog := strings.TrimSpace(errorLog.String())
+ wantLog := "http: multiple response.WriteHeader calls"
+ if hijack {
+ wantLog = "http: response.WriteHeader on hijacked connection"
+ }
+ if gotLog != wantLog {
+ t.Errorf("stderr output = %q; want %q", gotLog, wantLog)
+ }
+}
diff --git a/libgo/go/net/http/fs_test.go b/libgo/go/net/http/fs_test.go
index fb8f9fe4c5c..de772f9c28a 100644
--- a/libgo/go/net/http/fs_test.go
+++ b/libgo/go/net/http/fs_test.go
@@ -1140,7 +1140,7 @@ func TestLinuxSendfile(t *testing.T) {
Post(fmt.Sprintf("http://%s/quit", ln.Addr()), "", nil)
child.Wait()
- rx := regexp.MustCompile(`sendfile(64)?\(\d+,\s*\d+,\s*NULL,\s*\d+`)
+ rx := regexp.MustCompile(`sendfile(64)?\(`)
out := buf.String()
if !rx.MatchString(out) {
t.Errorf("no sendfile system call found in:\n%s", out)
diff --git a/libgo/go/net/http/h2_bundle.go b/libgo/go/net/http/h2_bundle.go
index f5a95084d24..3671875ddd1 100644
--- a/libgo/go/net/http/h2_bundle.go
+++ b/libgo/go/net/http/h2_bundle.go
@@ -989,7 +989,7 @@ type http2noDialH2RoundTripper struct{ t *http2Transport }
func (rt http2noDialH2RoundTripper) RoundTrip(req *Request) (*Response, error) {
res, err := rt.t.RoundTrip(req)
- if err == http2ErrNoCachedConn {
+ if http2isNoCachedConnError(err) {
return nil, ErrSkipAltProtocol
}
return res, err
@@ -6204,7 +6204,6 @@ func http2checkWriteHeaderCode(code int) {
}
func (w *http2responseWriter) WriteHeader(code int) {
- http2checkWriteHeaderCode(code)
rws := w.rws
if rws == nil {
panic("WriteHeader called after Handler finished")
@@ -6214,6 +6213,7 @@ func (w *http2responseWriter) WriteHeader(code int) {
func (rws *http2responseWriterState) writeHeader(code int) {
if !rws.wroteHeader {
+ http2checkWriteHeaderCode(code)
rws.wroteHeader = true
rws.status = code
if len(rws.handlerHeader) > 0 {
@@ -6856,7 +6856,27 @@ func (sew http2stickyErrWriter) Write(p []byte) (n int, err error) {
return
}
-var http2ErrNoCachedConn = errors.New("http2: no cached connection was available")
+// noCachedConnError is the concrete type of ErrNoCachedConn, needs to be detected
+// by net/http regardless of whether it's its bundled version (in h2_bundle.go with a rewritten type name)
+// or from a user's x/net/http2. As such, as it has a unique method name (IsHTTP2NoCachedConnError) that
+// net/http sniffs for via func isNoCachedConnError.
+type http2noCachedConnError struct{}
+
+func (http2noCachedConnError) IsHTTP2NoCachedConnError() {}
+
+func (http2noCachedConnError) Error() string { return "http2: no cached connection was available" }
+
+// isNoCachedConnError reports whether err is of type noCachedConnError
+// or its equivalent renamed type in net/http2's h2_bundle.go. Both types
+// may coexist in the same running program.
+func http2isNoCachedConnError(err error) bool {
+ _, ok := err.(interface {
+ IsHTTP2NoCachedConnError()
+ })
+ return ok
+}
+
+var http2ErrNoCachedConn error = http2noCachedConnError{}
// RoundTripOpt are options for the Transport.RoundTripOpt method.
type http2RoundTripOpt struct {
diff --git a/libgo/go/net/http/httputil/reverseproxy.go b/libgo/go/net/http/httputil/reverseproxy.go
index aa22d5a2fdd..b96bb21019b 100644
--- a/libgo/go/net/http/httputil/reverseproxy.go
+++ b/libgo/go/net/http/httputil/reverseproxy.go
@@ -191,11 +191,10 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
res, err := transport.RoundTrip(outreq)
- if res == nil {
- res = &http.Response{
- StatusCode: http.StatusBadGateway,
- Body: http.NoBody,
- }
+ if err != nil {
+ p.logf("http: proxy error: %v", err)
+ rw.WriteHeader(http.StatusBadGateway)
+ return
}
removeConnectionHeaders(res.Header)
@@ -205,16 +204,12 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
if p.ModifyResponse != nil {
- if err != nil {
+ if err := p.ModifyResponse(res); err != nil {
p.logf("http: proxy error: %v", err)
+ rw.WriteHeader(http.StatusBadGateway)
+ res.Body.Close()
+ return
}
- err = p.ModifyResponse(res)
- }
- if err != nil {
- p.logf("http: proxy error: %v", err)
- rw.WriteHeader(http.StatusBadGateway)
- res.Body.Close()
- return
}
copyHeader(rw.Header(), res.Header)
diff --git a/libgo/go/net/http/httputil/reverseproxy_test.go b/libgo/go/net/http/httputil/reverseproxy_test.go
index 822828e5c0d..2232042d3ed 100644
--- a/libgo/go/net/http/httputil/reverseproxy_test.go
+++ b/libgo/go/net/http/httputil/reverseproxy_test.go
@@ -631,35 +631,6 @@ func TestReverseProxyModifyResponse(t *testing.T) {
}
}
-// Issue 21255. Test ModifyResponse when an error from transport.RoundTrip
-// occurs, and that the proxy returns StatusOK.
-func TestReverseProxyModifyResponse_OnError(t *testing.T) {
- // Always returns an error
- errBackend := httptest.NewUnstartedServer(nil)
- errBackend.Config.ErrorLog = log.New(ioutil.Discard, "", 0) // quiet for tests
- defer errBackend.Close()
-
- rpURL, _ := url.Parse(errBackend.URL)
- rproxy := NewSingleHostReverseProxy(rpURL)
- rproxy.ModifyResponse = func(resp *http.Response) error {
- // Will be set for a non-nil error
- resp.StatusCode = http.StatusOK
- return nil
- }
-
- frontend := httptest.NewServer(rproxy)
- defer frontend.Close()
-
- resp, err := http.Get(frontend.URL)
- if err != nil {
- t.Fatalf("failed to reach proxy: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Errorf("err != nil: got res.StatusCode %d; expected %d", resp.StatusCode, http.StatusOK)
- }
- resp.Body.Close()
-}
-
// Issue 16659: log errors from short read
func TestReverseProxy_CopyBuffer(t *testing.T) {
backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go
index 870af85e04a..c9642e55c29 100644
--- a/libgo/go/net/http/request.go
+++ b/libgo/go/net/http/request.go
@@ -98,6 +98,10 @@ var reqWriteExcludeHeader = map[string]bool{
type Request struct {
// Method specifies the HTTP method (GET, POST, PUT, etc.).
// For client requests an empty string means GET.
+ //
+ // Go's HTTP client does not support sending a request with
+ // the CONNECT method. See the documentation on Transport for
+ // details.
Method string
// URL specifies either the URI being requested (for server
diff --git a/libgo/go/net/http/response.go b/libgo/go/net/http/response.go
index 4c614bfab0b..a91efcffbac 100644
--- a/libgo/go/net/http/response.go
+++ b/libgo/go/net/http/response.go
@@ -57,10 +57,9 @@ type Response struct {
// The http Client and Transport guarantee that Body is always
// non-nil, even on responses without a body or responses with
// a zero-length body. It is the caller's responsibility to
- // close Body. The default HTTP client's Transport does not
- // attempt to reuse HTTP/1.0 or HTTP/1.1 TCP connections
- // ("keep-alive") unless the Body is read to completion and is
- // closed.
+ // close Body. The default HTTP client's Transport may not
+ // reuse HTTP/1.x "keep-alive" TCP connections if the Body is
+ // not read to completion and closed.
//
// The Body is automatically dechunked if the server replied
// with a "chunked" Transfer-Encoding.
diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go
index 1ffa4115009..9cbfe872afe 100644
--- a/libgo/go/net/http/serve_test.go
+++ b/libgo/go/net/http/serve_test.go
@@ -523,6 +523,64 @@ func TestServeWithSlashRedirectKeepsQueryString(t *testing.T) {
}
}
+func TestServeWithSlashRedirectForHostPatterns(t *testing.T) {
+ setParallel(t)
+ defer afterTest(t)
+
+ mux := NewServeMux()
+ mux.Handle("example.com/pkg/foo/", stringHandler("example.com/pkg/foo/"))
+ mux.Handle("example.com/pkg/bar", stringHandler("example.com/pkg/bar"))
+ mux.Handle("example.com/pkg/bar/", stringHandler("example.com/pkg/bar/"))
+ mux.Handle("example.com:3000/pkg/connect/", stringHandler("example.com:3000/pkg/connect/"))
+ mux.Handle("example.com:9000/", stringHandler("example.com:9000/"))
+ mux.Handle("/pkg/baz/", stringHandler("/pkg/baz/"))
+
+ tests := []struct {
+ method string
+ url string
+ code int
+ loc string
+ want string
+ }{
+ {"GET", "http://example.com/", 404, "", ""},
+ {"GET", "http://example.com/pkg/foo", 301, "/pkg/foo/", ""},
+ {"GET", "http://example.com/pkg/bar", 200, "", "example.com/pkg/bar"},
+ {"GET", "http://example.com/pkg/bar/", 200, "", "example.com/pkg/bar/"},
+ {"GET", "http://example.com/pkg/baz", 301, "/pkg/baz/", ""},
+ {"GET", "http://example.com:3000/pkg/foo", 301, "/pkg/foo/", ""},
+ {"CONNECT", "http://example.com/", 404, "", ""},
+ {"CONNECT", "http://example.com:3000/", 404, "", ""},
+ {"CONNECT", "http://example.com:9000/", 200, "", "example.com:9000/"},
+ {"CONNECT", "http://example.com/pkg/foo", 301, "/pkg/foo/", ""},
+ {"CONNECT", "http://example.com:3000/pkg/foo", 404, "", ""},
+ {"CONNECT", "http://example.com:3000/pkg/baz", 301, "/pkg/baz/", ""},
+ {"CONNECT", "http://example.com:3000/pkg/connect", 301, "/pkg/connect/", ""},
+ }
+
+ ts := httptest.NewServer(mux)
+ defer ts.Close()
+
+ for i, tt := range tests {
+ req, _ := NewRequest(tt.method, tt.url, nil)
+ w := httptest.NewRecorder()
+ mux.ServeHTTP(w, req)
+
+ if got, want := w.Code, tt.code; got != want {
+ t.Errorf("#%d: Status = %d; want = %d", i, got, want)
+ }
+
+ if tt.code == 301 {
+ if got, want := w.HeaderMap.Get("Location"), tt.loc; got != want {
+ t.Errorf("#%d: Location = %q; want = %q", i, got, want)
+ }
+ } else {
+ if got, want := w.HeaderMap.Get("Result"), tt.want; got != want {
+ t.Errorf("#%d: Result = %q; want = %q", i, got, want)
+ }
+ }
+ }
+}
+
func BenchmarkServeMux(b *testing.B) {
type test struct {
@@ -5478,32 +5536,54 @@ func testServerKeepAlivesEnabled(t *testing.T, h2 bool) {
func TestServerCancelsReadTimeoutWhenIdle(t *testing.T) {
setParallel(t)
defer afterTest(t)
- const timeout = 250 * time.Millisecond
- ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
- select {
- case <-time.After(2 * timeout):
- fmt.Fprint(w, "ok")
- case <-r.Context().Done():
- fmt.Fprint(w, r.Context().Err())
- }
- }))
- ts.Config.ReadTimeout = timeout
- ts.Start()
- defer ts.Close()
+ runTimeSensitiveTest(t, []time.Duration{
+ 10 * time.Millisecond,
+ 50 * time.Millisecond,
+ 250 * time.Millisecond,
+ time.Second,
+ 2 * time.Second,
+ }, func(t *testing.T, timeout time.Duration) error {
+ ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ select {
+ case <-time.After(2 * timeout):
+ fmt.Fprint(w, "ok")
+ case <-r.Context().Done():
+ fmt.Fprint(w, r.Context().Err())
+ }
+ }))
+ ts.Config.ReadTimeout = timeout
+ ts.Start()
+ defer ts.Close()
- c := ts.Client()
+ c := ts.Client()
- res, err := c.Get(ts.URL)
- if err != nil {
- t.Fatal(err)
- }
- slurp, err := ioutil.ReadAll(res.Body)
- res.Body.Close()
- if err != nil {
- t.Fatal(err)
- }
- if string(slurp) != "ok" {
- t.Fatalf("Got: %q, want ok", slurp)
+ res, err := c.Get(ts.URL)
+ if err != nil {
+ return fmt.Errorf("Get: %v", err)
+ }
+ slurp, err := ioutil.ReadAll(res.Body)
+ res.Body.Close()
+ if err != nil {
+ return fmt.Errorf("Body ReadAll: %v", err)
+ }
+ if string(slurp) != "ok" {
+ return fmt.Errorf("got: %q, want ok", slurp)
+ }
+ return nil
+ })
+}
+
+// runTimeSensitiveTest runs test with the provided durations until one passes.
+// If they all fail, t.Fatal is called with the last one's duration and error value.
+func runTimeSensitiveTest(t *testing.T, durations []time.Duration, test func(t *testing.T, d time.Duration) error) {
+ for i, d := range durations {
+ err := test(t, d)
+ if err == nil {
+ return
+ }
+ if i == len(durations)-1 {
+ t.Fatalf("failed with duration %v: %v", d, err)
+ }
}
}
diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go
index 3fa66601649..57e1b5dacb3 100644
--- a/libgo/go/net/http/server.go
+++ b/libgo/go/net/http/server.go
@@ -132,12 +132,20 @@ type ResponseWriter interface {
// possible to maximize compatibility.
Write([]byte) (int, error)
- // WriteHeader sends an HTTP response header with status code.
+ // WriteHeader sends an HTTP response header with the provided
+ // status code.
+ //
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
// Thus explicit calls to WriteHeader are mainly used to
// send error codes.
- WriteHeader(int)
+ //
+ // The provided code must be a valid HTTP 1xx-5xx status code.
+ // Only one header may be written. Go does not currently
+ // support sending user-defined 1xx informational headers,
+ // with the exception of 100-continue response header that the
+ // Server sends automatically when the Request.Body is read.
+ WriteHeader(statusCode int)
}
// The Flusher interface is implemented by ResponseWriters that allow
@@ -1064,7 +1072,6 @@ func checkWriteHeaderCode(code int) {
}
func (w *response) WriteHeader(code int) {
- checkWriteHeaderCode(code)
if w.conn.hijacked() {
w.conn.server.logf("http: response.WriteHeader on hijacked connection")
return
@@ -1073,6 +1080,7 @@ func (w *response) WriteHeader(code int) {
w.conn.server.logf("http: multiple response.WriteHeader calls")
return
}
+ checkWriteHeaderCode(code)
w.wroteHeader = true
w.status = code
@@ -2212,8 +2220,8 @@ func (mux *ServeMux) match(path string) (h Handler, pattern string) {
// This occurs when a handler for path + "/" was already registered, but
// not for path itself. If the path needs appending to, it creates a new
// URL, setting the path to u.Path + "/" and returning true to indicate so.
-func (mux *ServeMux) redirectToPathSlash(path string, u *url.URL) (*url.URL, bool) {
- if !mux.shouldRedirect(path) {
+func (mux *ServeMux) redirectToPathSlash(host, path string, u *url.URL) (*url.URL, bool) {
+ if !mux.shouldRedirect(host, path) {
return u, false
}
path = path + "/"
@@ -2221,16 +2229,29 @@ func (mux *ServeMux) redirectToPathSlash(path string, u *url.URL) (*url.URL, boo
return u, true
}
-// shouldRedirect reports whether the given path should be redirected to
+// shouldRedirect reports whether the given path and host should be redirected to
// path+"/". This should happen if a handler is registered for path+"/" but
// not path -- see comments at ServeMux.
-func (mux *ServeMux) shouldRedirect(path string) bool {
- if _, exist := mux.m[path]; exist {
- return false
+func (mux *ServeMux) shouldRedirect(host, path string) bool {
+ p := []string{path, host + path}
+
+ for _, c := range p {
+ if _, exist := mux.m[c]; exist {
+ return false
+ }
}
+
n := len(path)
- _, exist := mux.m[path+"/"]
- return n > 0 && path[n-1] != '/' && exist
+ if n == 0 {
+ return false
+ }
+ for _, c := range p {
+ if _, exist := mux.m[c+"/"]; exist {
+ return path[n-1] != '/'
+ }
+ }
+
+ return false
}
// Handler returns the handler to use for the given request,
@@ -2255,7 +2276,7 @@ func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
// If r.URL.Path is /tree and its handler is not registered,
// the /tree -> /tree/ redirect applies to CONNECT requests
// but the path canonicalization does not.
- if u, ok := mux.redirectToPathSlash(r.URL.Path, r.URL); ok {
+ if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
@@ -2269,7 +2290,7 @@ func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
// If the given path is /tree and its handler is not registered,
// redirect for /tree/.
- if u, ok := mux.redirectToPathSlash(path, r.URL); ok {
+ if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
@@ -2386,9 +2407,17 @@ func ServeTLS(l net.Listener, handler Handler, certFile, keyFile string) error {
// A Server defines parameters for running an HTTP server.
// The zero value for Server is a valid configuration.
type Server struct {
- Addr string // TCP address to listen on, ":http" if empty
- Handler Handler // handler to invoke, http.DefaultServeMux if nil
- TLSConfig *tls.Config // optional TLS config, used by ServeTLS and ListenAndServeTLS
+ Addr string // TCP address to listen on, ":http" if empty
+ Handler Handler // handler to invoke, http.DefaultServeMux if nil
+
+ // TLSConfig optionally provides a TLS configuration for use
+ // by ServeTLS and ListenAndServeTLS. Note that this value is
+ // cloned by ServeTLS and ListenAndServeTLS, so it's not
+ // possible to modify the configuration with methods like
+ // tls.Config.SetSessionTicketKeys. To use
+ // SetSessionTicketKeys, use Server.Serve with a TLS Listener
+ // instead.
+ TLSConfig *tls.Config
// ReadTimeout is the maximum duration for reading the entire
// request, including the body.
diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go
index 45e3fd2eba7..7ef8f0147bd 100644
--- a/libgo/go/net/http/transport.go
+++ b/libgo/go/net/http/transport.go
@@ -73,6 +73,15 @@ const DefaultMaxIdleConnsPerHost = 2
// and how the Transport is configured. The DefaultTransport supports HTTP/2.
// To explicitly enable HTTP/2 on a transport, use golang.org/x/net/http2
// and call ConfigureTransport. See the package docs for more about HTTP/2.
+//
+// The Transport will send CONNECT requests to a proxy for its own use
+// when processing HTTPS requests, but Transport should generally not
+// be used to send a CONNECT request. That is, the Request passed to
+// the RoundTrip method should not have a Method of "CONNECT", as Go's
+// HTTP/1.x implementation does not support full-duplex request bodies
+// being written while the response body is streamed. Go's HTTP/2
+// implementation does support full duplex, but many CONNECT proxies speak
+// HTTP/1.x.
type Transport struct {
idleMu sync.Mutex
wantIdle bool // user has requested to close all idle conns
@@ -443,7 +452,7 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) {
// HTTP request on a new connection. The non-nil input error is the
// error from roundTrip.
func (pc *persistConn) shouldRetryRequest(req *Request, err error) bool {
- if err == http2ErrNoCachedConn {
+ if http2isNoCachedConnError(err) {
// Issue 16582: if the user started a bunch of
// requests at once, they can all pick the same conn
// and violate the server's max concurrent streams.
@@ -646,9 +655,14 @@ var (
errTooManyIdleHost = errors.New("http: putIdleConn: too many idle connections for host")
errCloseIdleConns = errors.New("http: CloseIdleConnections called")
errReadLoopExiting = errors.New("http: persistConn.readLoop exiting")
- errServerClosedIdle = errors.New("http: server closed idle connection")
errIdleConnTimeout = errors.New("http: idle connection timeout")
errNotCachingH2Conn = errors.New("http: not caching alternate protocol's connections")
+
+ // errServerClosedIdle is not seen by users for idempotent requests, but may be
+ // seen by a user if the server shuts down an idle connection and sends its FIN
+ // in flight with already-written POST body bytes from the client.
+ // See https://github.com/golang/go/issues/19943#issuecomment-355607646
+ errServerClosedIdle = errors.New("http: server closed idle connection")
)
// transportReadFromServerError is used by Transport.readLoop when the
diff --git a/libgo/go/net/http/transport_internal_test.go b/libgo/go/net/http/transport_internal_test.go
index 594bf6e2c83..a5f29c97a90 100644
--- a/libgo/go/net/http/transport_internal_test.go
+++ b/libgo/go/net/http/transport_internal_test.go
@@ -96,6 +96,12 @@ func dummyRequestWithBodyNoGetBody(method string) *Request {
return req
}
+// issue22091Error acts like a golang.org/x/net/http2.ErrNoCachedConn.
+type issue22091Error struct{}
+
+func (issue22091Error) IsHTTP2NoCachedConnError() {}
+func (issue22091Error) Error() string { return "issue22091Error" }
+
func TestTransportShouldRetryRequest(t *testing.T) {
tests := []struct {
pc *persistConn
@@ -123,36 +129,42 @@ func TestTransportShouldRetryRequest(t *testing.T) {
want: true,
},
3: {
+ pc: nil,
+ req: nil,
+ err: issue22091Error{}, // like an external http2ErrNoCachedConn
+ want: true,
+ },
+ 4: {
pc: &persistConn{reused: true},
req: dummyRequest("POST"),
err: errMissingHost,
want: false,
},
- 4: {
+ 5: {
pc: &persistConn{reused: true},
req: dummyRequest("POST"),
err: transportReadFromServerError{},
want: false,
},
- 5: {
+ 6: {
pc: &persistConn{reused: true},
req: dummyRequest("GET"),
err: transportReadFromServerError{},
want: true,
},
- 6: {
+ 7: {
pc: &persistConn{reused: true},
req: dummyRequest("GET"),
err: errServerClosedIdle,
want: true,
},
- 7: {
+ 8: {
pc: &persistConn{reused: true},
req: dummyRequestWithBody("POST"),
err: nothingWrittenError{},
want: true,
},
- 8: {
+ 9: {
pc: &persistConn{reused: true},
req: dummyRequestWithBodyNoGetBody("POST"),
err: nothingWrittenError{},
diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go
index 4b4363a47a1..9cff96006ce 100644
--- a/libgo/go/net/ipsock_posix.go
+++ b/libgo/go/net/ipsock_posix.go
@@ -24,7 +24,7 @@ import (
// general. Unfortunately, we need to run on kernels built without
// IPv6 support too. So probe the kernel to figure it out.
func (p *ipStackCapabilities) probe() {
- s, err := socketFunc(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
+ s, err := sysSocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
switch err {
case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT:
case nil:
@@ -48,7 +48,7 @@ func (p *ipStackCapabilities) probe() {
probes = probes[:1]
}
for i := range probes {
- s, err := socketFunc(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
+ s, err := sysSocket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
if err != nil {
continue
}
diff --git a/libgo/go/net/lookup.go b/libgo/go/net/lookup.go
index c9f327050af..85e472932fc 100644
--- a/libgo/go/net/lookup.go
+++ b/libgo/go/net/lookup.go
@@ -8,6 +8,7 @@ import (
"context"
"internal/nettrace"
"internal/singleflight"
+ "sync"
)
// protocols contains minimal mappings between internet protocol
@@ -53,6 +54,10 @@ var services = map[string]map[string]int{
},
}
+// dnsWaitGroup can be used by tests to wait for all DNS goroutines to
+// complete. This avoids races on the test hooks.
+var dnsWaitGroup sync.WaitGroup
+
const maxProtoLength = len("RSVP-E2E-IGNORE") + 10 // with room to grow
func lookupProtocolMap(name string) (int, error) {
@@ -189,9 +194,14 @@ func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, err
resolverFunc = alt
}
- ch := lookupGroup.DoChan(host, func() (interface{}, error) {
+ dnsWaitGroup.Add(1)
+ ch, called := lookupGroup.DoChan(host, func() (interface{}, error) {
+ defer dnsWaitGroup.Done()
return testHookLookupIP(ctx, resolverFunc, host)
})
+ if !called {
+ dnsWaitGroup.Done()
+ }
select {
case <-ctx.Done():
diff --git a/libgo/go/net/lookup_test.go b/libgo/go/net/lookup_test.go
index e3bf114a8e2..bfb872551c0 100644
--- a/libgo/go/net/lookup_test.go
+++ b/libgo/go/net/lookup_test.go
@@ -105,6 +105,8 @@ func TestLookupGmailMX(t *testing.T) {
t.Skip("IPv4 is required")
}
+ defer dnsWaitGroup.Wait()
+
for _, tt := range lookupGmailMXTests {
mxs, err := LookupMX(tt.name)
if err != nil {
@@ -137,6 +139,8 @@ func TestLookupGmailNS(t *testing.T) {
t.Skip("IPv4 is required")
}
+ defer dnsWaitGroup.Wait()
+
for _, tt := range lookupGmailNSTests {
nss, err := LookupNS(tt.name)
if err != nil {
@@ -170,6 +174,8 @@ func TestLookupGmailTXT(t *testing.T) {
t.Skip("IPv4 is required")
}
+ defer dnsWaitGroup.Wait()
+
for _, tt := range lookupGmailTXTTests {
txts, err := LookupTXT(tt.name)
if err != nil {
@@ -205,6 +211,8 @@ func TestLookupGooglePublicDNSAddr(t *testing.T) {
t.Skip("both IPv4 and IPv6 are required")
}
+ defer dnsWaitGroup.Wait()
+
for _, tt := range lookupGooglePublicDNSAddrTests {
names, err := LookupAddr(tt.addr)
if err != nil {
@@ -226,6 +234,8 @@ func TestLookupIPv6LinkLocalAddr(t *testing.T) {
t.Skip("IPv6 is required")
}
+ defer dnsWaitGroup.Wait()
+
addrs, err := LookupHost("localhost")
if err != nil {
t.Fatal(err)
@@ -262,6 +272,8 @@ func TestLookupCNAME(t *testing.T) {
t.Skip("IPv4 is required")
}
+ defer dnsWaitGroup.Wait()
+
for _, tt := range lookupCNAMETests {
cname, err := LookupCNAME(tt.name)
if err != nil {
@@ -289,6 +301,8 @@ func TestLookupGoogleHost(t *testing.T) {
t.Skip("IPv4 is required")
}
+ defer dnsWaitGroup.Wait()
+
for _, tt := range lookupGoogleHostTests {
addrs, err := LookupHost(tt.name)
if err != nil {
@@ -313,6 +327,8 @@ func TestLookupLongTXT(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
}
+ defer dnsWaitGroup.Wait()
+
txts, err := LookupTXT("golang.rsc.io")
if err != nil {
t.Fatal(err)
@@ -343,6 +359,8 @@ func TestLookupGoogleIP(t *testing.T) {
t.Skip("IPv4 is required")
}
+ defer dnsWaitGroup.Wait()
+
for _, tt := range lookupGoogleIPTests {
ips, err := LookupIP(tt.name)
if err != nil {
@@ -378,6 +396,7 @@ var revAddrTests = []struct {
}
func TestReverseAddress(t *testing.T) {
+ defer dnsWaitGroup.Wait()
for i, tt := range revAddrTests {
a, err := reverseaddr(tt.Addr)
if len(tt.ErrPrefix) > 0 && err == nil {
@@ -401,6 +420,8 @@ func TestDNSFlood(t *testing.T) {
t.Skip("test disabled; use -dnsflood to enable")
}
+ defer dnsWaitGroup.Wait()
+
var N = 5000
if runtime.GOOS == "darwin" {
// On Darwin this test consumes kernel threads much
@@ -482,6 +503,8 @@ func TestLookupDotsWithLocalSource(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
}
+ defer dnsWaitGroup.Wait()
+
for i, fn := range []func() func(){forceGoDNS, forceCgoDNS} {
fixup := fn()
if fixup == nil {
@@ -527,6 +550,8 @@ func TestLookupDotsWithRemoteSource(t *testing.T) {
t.Skip("IPv4 is required")
}
+ defer dnsWaitGroup.Wait()
+
if fixup := forceGoDNS(); fixup != nil {
testDots(t, "go")
fixup()
@@ -747,6 +772,9 @@ func TestLookupNonLDH(t *testing.T) {
if runtime.GOOS == "nacl" {
t.Skip("skip on nacl")
}
+
+ defer dnsWaitGroup.Wait()
+
if fixup := forceGoDNS(); fixup != nil {
defer fixup()
}
diff --git a/libgo/go/net/net.go b/libgo/go/net/net.go
index 91ec048e0be..3ad91036e76 100644
--- a/libgo/go/net/net.go
+++ b/libgo/go/net/net.go
@@ -288,6 +288,8 @@ func (c *conn) SetWriteBuffer(bytes int) error {
// The returned os.File's file descriptor is different from the connection's.
// Attempting to change properties of the original using this duplicate
// may or may not have the desired effect.
+//
+// On Unix systems this will cause the SetDeadline methods to stop working.
func (c *conn) File() (f *os.File, err error) {
f, err = c.fd.dup()
if err != nil {
diff --git a/libgo/go/net/protoconn_test.go b/libgo/go/net/protoconn_test.go
index 05c45d02b9a..def8d657f16 100644
--- a/libgo/go/net/protoconn_test.go
+++ b/libgo/go/net/protoconn_test.go
@@ -8,6 +8,7 @@
package net
import (
+ "internal/testenv"
"os"
"runtime"
"testing"
@@ -138,11 +139,15 @@ func TestUDPConnSpecificMethods(t *testing.T) {
if _, _, err := c.ReadFromUDP(rb); err != nil {
t.Fatal(err)
}
- if _, _, err := c.WriteMsgUDP(wb, nil, c.LocalAddr().(*UDPAddr)); err != nil {
- condFatalf(t, c.LocalAddr().Network(), "%v", err)
- }
- if _, _, _, _, err := c.ReadMsgUDP(rb, nil); err != nil {
- condFatalf(t, c.LocalAddr().Network(), "%v", err)
+ if testenv.IsWindowsXP() {
+ t.Log("skipping broken test on Windows XP (see golang.org/issue/23072)")
+ } else {
+ if _, _, err := c.WriteMsgUDP(wb, nil, c.LocalAddr().(*UDPAddr)); err != nil {
+ condFatalf(t, c.LocalAddr().Network(), "%v", err)
+ }
+ if _, _, _, _, err := c.ReadMsgUDP(rb, nil); err != nil {
+ condFatalf(t, c.LocalAddr().Network(), "%v", err)
+ }
}
if f, err := c.File(); err != nil {
diff --git a/libgo/go/net/udpsock_test.go b/libgo/go/net/udpsock_test.go
index 4ae014c01d9..769576cd976 100644
--- a/libgo/go/net/udpsock_test.go
+++ b/libgo/go/net/udpsock_test.go
@@ -163,6 +163,11 @@ func testWriteToConn(t *testing.T, raddr string) {
switch runtime.GOOS {
case "nacl": // see golang.org/issue/9252
t.Skipf("not implemented yet on %s", runtime.GOOS)
+ case "windows":
+ if testenv.IsWindowsXP() {
+ t.Log("skipping broken test on Windows XP (see golang.org/issue/23072)")
+ return
+ }
default:
if err != nil {
t.Fatal(err)
@@ -206,6 +211,11 @@ func testWriteToPacketConn(t *testing.T, raddr string) {
switch runtime.GOOS {
case "nacl": // see golang.org/issue/9252
t.Skipf("not implemented yet on %s", runtime.GOOS)
+ case "windows":
+ if testenv.IsWindowsXP() {
+ t.Log("skipping broken test on Windows XP (see golang.org/issue/23072)")
+ return
+ }
default:
if err != nil {
t.Fatal(err)
diff --git a/libgo/go/net/url/url.go b/libgo/go/net/url/url.go
index 7c3d24493e0..3e121795422 100644
--- a/libgo/go/net/url/url.go
+++ b/libgo/go/net/url/url.go
@@ -563,6 +563,9 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) {
return nil, host, nil
}
userinfo := authority[:i]
+ if !validUserinfo(userinfo) {
+ return nil, "", errors.New("net/url: invalid userinfo")
+ }
if !strings.Contains(userinfo, ":") {
if userinfo, err = unescape(userinfo, encodeUserPassword); err != nil {
return nil, "", err
@@ -1069,3 +1072,33 @@ func (u *URL) UnmarshalBinary(text []byte) error {
*u = *u1
return nil
}
+
+// validUserinfo reports whether s is a valid userinfo string per RFC 3986
+// Section 3.2.1:
+// userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
+// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+// / "*" / "+" / "," / ";" / "="
+//
+// It doesn't validate pct-encoded. The caller does that via func unescape.
+func validUserinfo(s string) bool {
+ for _, r := range s {
+ if 'A' <= r && r <= 'Z' {
+ continue
+ }
+ if 'a' <= r && r <= 'z' {
+ continue
+ }
+ if '0' <= r && r <= '9' {
+ continue
+ }
+ switch r {
+ case '-', '.', '_', ':', '~', '!', '$', '&', '\'',
+ '(', ')', '*', '+', ',', ';', '=', '%', '@':
+ continue
+ default:
+ return false
+ }
+ }
+ return true
+}
diff --git a/libgo/go/net/url/url_test.go b/libgo/go/net/url/url_test.go
index d6aed3acafa..f2d311a9986 100644
--- a/libgo/go/net/url/url_test.go
+++ b/libgo/go/net/url/url_test.go
@@ -1735,3 +1735,10 @@ func TestNilUser(t *testing.T) {
t.Fatalf("expected empty string, got %s", v)
}
}
+
+func TestInvalidUserPassword(t *testing.T) {
+ _, err := Parse("http://us\ner:pass\nword@foo.com/")
+ if got, wantsub := fmt.Sprint(err), "net/url: invalid userinfo"; !strings.Contains(got, wantsub) {
+ t.Errorf("error = %q; want substring %q", got, wantsub)
+ }
+}