diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2018-01-17 14:20:29 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2018-01-17 14:20:29 +0000 |
commit | c6d6367f848cfd8381aba41e035c5e7e873667c5 (patch) | |
tree | a218e98243463fc27f5053b4444e2544c63cd57a /libgo/go/net | |
parent | 9bff0086915f544fa648ea81131f035cb9ce79a4 (diff) |
libgo: update to Go1.10beta2 release
Reviewed-on: https://go-review.googlesource.com/87897
From-SVN: r256794
Diffstat (limited to 'libgo/go/net')
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) + } +} |