Skip to content

Commit ab17593

Browse files
neildmknyszek
authored andcommitted
[release-branch.go1.17] net/http: do not cancel request context on response body read
When sending a Request with a non-context deadline, we create a context with a timeout. This context is canceled when closing the response body, and also if a read from the response body returns an error (including io.EOF). Cancelling the context in Response.Body.Read interferes with the HTTP/2 client cleaning up after a request is completed, and is unnecessary: The user should always close the body, the impact from not canceling the context is minor (the context timer leaks until it fires). Fixes #49559. For #49366. Change-Id: Ieaed866116916261d9079f71d8fea7a7b303b8fb Reviewed-on: https://go-review.googlesource.com/c/go/+/361919 Trust: Damien Neil <[email protected]> Run-TryBot: Damien Neil <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> (cherry picked from commit 76fbd61) Reviewed-on: https://go-review.googlesource.com/c/go/+/368085 Reviewed-by: Michael Knyszek <[email protected]>
1 parent f0ee7c6 commit ab17593

File tree

2 files changed

+27
-1
lines changed

2 files changed

+27
-1
lines changed

src/net/http/client.go

-1
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,6 @@ func (b *cancelTimerBody) Read(p []byte) (n int, err error) {
965965
if err == nil {
966966
return n, nil
967967
}
968-
b.stop()
969968
if err == io.EOF {
970969
return n, err
971970
}

src/net/http/client_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -1353,6 +1353,33 @@ func TestClientTimeoutCancel(t *testing.T) {
13531353
}
13541354
}
13551355

1356+
func TestClientTimeoutDoesNotExpire_h1(t *testing.T) { testClientTimeoutDoesNotExpire(t, h1Mode) }
1357+
func TestClientTimeoutDoesNotExpire_h2(t *testing.T) { testClientTimeoutDoesNotExpire(t, h2Mode) }
1358+
1359+
// Issue 49366: if Client.Timeout is set but not hit, no error should be returned.
1360+
func testClientTimeoutDoesNotExpire(t *testing.T, h2 bool) {
1361+
setParallel(t)
1362+
defer afterTest(t)
1363+
1364+
cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
1365+
w.Write([]byte("body"))
1366+
}))
1367+
defer cst.close()
1368+
1369+
cst.c.Timeout = 1 * time.Hour
1370+
req, _ := NewRequest("GET", cst.ts.URL, nil)
1371+
res, err := cst.c.Do(req)
1372+
if err != nil {
1373+
t.Fatal(err)
1374+
}
1375+
if _, err = io.Copy(io.Discard, res.Body); err != nil {
1376+
t.Fatalf("io.Copy(io.Discard, res.Body) = %v, want nil", err)
1377+
}
1378+
if err = res.Body.Close(); err != nil {
1379+
t.Fatalf("res.Body.Close() = %v, want nil", err)
1380+
}
1381+
}
1382+
13561383
func TestClientRedirectEatsBody_h1(t *testing.T) { testClientRedirectEatsBody(t, h1Mode) }
13571384
func TestClientRedirectEatsBody_h2(t *testing.T) { testClientRedirectEatsBody(t, h2Mode) }
13581385
func testClientRedirectEatsBody(t *testing.T, h2 bool) {

0 commit comments

Comments
 (0)