From 10d977d76b3a15a009ba039b2360d9c2580ac9ea Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 11 Jul 2018 22:55:16 +0000 Subject: [PATCH] net/http: add tests to validate that Client.Timeout closes connections For #23399 Change-Id: I9bc7c21fda6bfa89af2e7656e5c85aa9edd4f29e Reviewed-on: https://go-review.googlesource.com/123435 Reviewed-by: Ian Lance Taylor --- src/net/http/transport_test.go | 119 +++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 52f628ddc9..01ddf7adb9 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -4702,3 +4702,122 @@ func TestTransportCheckContextDoneEarly(t *testing.T) { t.Errorf("error = %v; want %v", err, wantErr) } } + +// Issue 23399: verify that if a client request times out, the Transport's +// conn is closed so that it's not reused. +// +// This is the test variant that times out before the server replies with +// any response headers. +func TestClientTimeoutKillsConn_BeforeHeaders(t *testing.T) { + setParallel(t) + defer afterTest(t) + inHandler := make(chan net.Conn, 1) + handlerReadReturned := make(chan bool, 1) + cst := newClientServerTest(t, h1Mode, HandlerFunc(func(w ResponseWriter, r *Request) { + conn, _, err := w.(Hijacker).Hijack() + if err != nil { + t.Error(err) + return + } + inHandler <- conn + n, err := conn.Read([]byte{0}) + if n != 0 || err != io.EOF { + t.Errorf("unexpected Read result: %v, %v", n, err) + } + handlerReadReturned <- true + })) + defer cst.close() + + const timeout = 50 * time.Millisecond + cst.c.Timeout = timeout + + _, err := cst.c.Get(cst.ts.URL) + if err == nil { + t.Fatal("unexpected Get succeess") + } + + select { + case c := <-inHandler: + select { + case <-handlerReadReturned: + // Success. + return + case <-time.After(5 * time.Second): + t.Error("Handler's conn.Read seems to be stuck in Read") + c.Close() // close it to unblock Handler + } + case <-time.After(timeout * 10): + // If we didn't get into the Handler in 50ms, that probably means + // the builder was just slow and the the Get failed in that time + // but never made it to the server. That's fine. We'll usually + // test the part above on faster machines. + t.Skip("skipping test on slow builder") + } +} + +// Issue 23399: verify that if a client request times out, the Transport's +// conn is closed so that it's not reused. +// +// This is the test variant that has the server send response headers +// first, and time out during the the write of the response body. +func TestClientTimeoutKillsConn_AfterHeaders(t *testing.T) { + setParallel(t) + defer afterTest(t) + inHandler := make(chan net.Conn, 1) + handlerReadReturned := make(chan bool, 1) + cst := newClientServerTest(t, h1Mode, HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("Content-Length", "100") + w.(Flusher).Flush() + conn, _, err := w.(Hijacker).Hijack() + if err != nil { + t.Error(err) + return + } + conn.Write([]byte("foo")) + inHandler <- conn + n, err := conn.Read([]byte{0}) + if n != 0 || err != io.EOF { + t.Errorf("unexpected Read result: %v, %v", n, err) + } + handlerReadReturned <- true + })) + defer cst.close() + + const timeout = 50 * time.Millisecond + cst.c.Timeout = timeout + + res, err := cst.c.Get(cst.ts.URL) + if err != nil { + select { + case <-inHandler: + t.Fatalf("Get error: %v", err) + default: + // Failed before entering handler. Ignore result. + t.Skip("skipping test on slow builder") + } + } + + got, err := ioutil.ReadAll(res.Body) + if err == nil { + t.Fatal("unexpected result") + } + t.Logf("Read %q, %v", got, err) + + select { + case c := <-inHandler: + select { + case <-handlerReadReturned: + // Success. + return + case <-time.After(5 * time.Second): + t.Error("Handler's conn.Read seems to be stuck in Read") + c.Close() // close it to unblock Handler + } + case <-time.After(timeout * 10): + // If we didn't get into the Handler in 50ms, that probably means + // the builder was just slow and the the Get failed in that time + // but never made it to the server. That's fine. We'll usually + // test the past above on faster machines. + t.Skip("skipping test on slow builder") + } +} -- 2.48.1