aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDamien Neil <dneil@google.com>2024-02-27 14:53:33 -0800
committerDamien Neil <dneil@google.com>2024-02-28 17:11:55 +0000
commit606b8ff5eff5e83e92b5b7466f9682115c1a6883 (patch)
tree20d786f075a86c08bb3cf0f8ae45ca415d390ff4 /src
parentb6753baaedf6ae932c3ad4af1451163045e7ff84 (diff)
downloadgo-606b8ff5eff5e83e92b5b7466f9682115c1a6883.tar.xz
net/http: make timeout errors match context.DeadlineExceeded
When returning an error which implements net.Error and reports itself as a timeout, also report it as matching context.DeadlineExceeded. This matches the behavior of timeout errors in the net package and elsewhere. Fixes #50856 Change-Id: I2ca911e3677a699af27ba89b1200401baa8b3b1b Reviewed-on: https://go-review.googlesource.com/c/go/+/567537 Reviewed-by: Jonathan Amsterdam <jba@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src')
-rw-r--r--src/net/http/client.go10
-rw-r--r--src/net/http/client_test.go9
-rw-r--r--src/net/http/transport.go16
3 files changed, 20 insertions, 15 deletions
diff --git a/src/net/http/client.go b/src/net/http/client.go
index ee6de24fc1..99ed7dc927 100644
--- a/src/net/http/client.go
+++ b/src/net/http/client.go
@@ -725,10 +725,7 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) {
// c.send() always closes req.Body
reqBodyClosed = true
if !deadline.IsZero() && didTimeout() {
- err = &httpError{
- err: err.Error() + " (Client.Timeout exceeded while awaiting headers)",
- timeout: true,
- }
+ err = &timeoutError{err.Error() + " (Client.Timeout exceeded while awaiting headers)"}
}
return nil, uerr(err)
}
@@ -968,10 +965,7 @@ func (b *cancelTimerBody) Read(p []byte) (n int, err error) {
return n, err
}
if b.reqDidTimeout() {
- err = &httpError{
- err: err.Error() + " (Client.Timeout or context cancellation while reading body)",
- timeout: true,
- }
+ err = &timeoutError{err.Error() + " (Client.Timeout or context cancellation while reading body)"}
}
return n, err
}
diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go
index 7459b9cb6e..80a6664d3b 100644
--- a/src/net/http/client_test.go
+++ b/src/net/http/client_test.go
@@ -1249,6 +1249,9 @@ func testClientTimeout(t *testing.T, mode testMode) {
} else if !ne.Timeout() {
t.Errorf("net.Error.Timeout = false; want true")
}
+ if !errors.Is(err, context.DeadlineExceeded) {
+ t.Errorf("ReadAll error = %q; expected some context.DeadlineExceeded", err)
+ }
if got := ne.Error(); !strings.Contains(got, "(Client.Timeout") {
if runtime.GOOS == "windows" && strings.HasPrefix(runtime.GOARCH, "arm") {
testenv.SkipFlaky(t, 43120)
@@ -1292,6 +1295,9 @@ func testClientTimeout_Headers(t *testing.T, mode testMode) {
if !ne.Timeout() {
t.Error("net.Error.Timeout = false; want true")
}
+ if !errors.Is(err, context.DeadlineExceeded) {
+ t.Errorf("ReadAll error = %q; expected some context.DeadlineExceeded", err)
+ }
if got := ne.Error(); !strings.Contains(got, "Client.Timeout exceeded") {
if runtime.GOOS == "windows" && strings.HasPrefix(runtime.GOARCH, "arm") {
testenv.SkipFlaky(t, 43120)
@@ -1992,6 +1998,9 @@ func testClientDoCanceledVsTimeout(t *testing.T, mode testMode) {
if g, w := ue.Err, wantErr; g != w {
t.Errorf("url.Error.Err = %v; want %v", g, w)
}
+ if got := errors.Is(err, context.DeadlineExceeded); got != wantIsTimeout {
+ t.Errorf("errors.Is(err, context.DeadlineExceeded) = %v, want %v", got, wantIsTimeout)
+ }
})
}
}
diff --git a/src/net/http/transport.go b/src/net/http/transport.go
index 411f6b2912..75934f00de 100644
--- a/src/net/http/transport.go
+++ b/src/net/http/transport.go
@@ -2557,16 +2557,18 @@ type writeRequest struct {
continueCh <-chan struct{}
}
-type httpError struct {
- err string
- timeout bool
+// httpTimeoutError represents a timeout.
+// It implements net.Error and wraps context.DeadlineExceeded.
+type timeoutError struct {
+ err string
}
-func (e *httpError) Error() string { return e.err }
-func (e *httpError) Timeout() bool { return e.timeout }
-func (e *httpError) Temporary() bool { return true }
+func (e *timeoutError) Error() string { return e.err }
+func (e *timeoutError) Timeout() bool { return true }
+func (e *timeoutError) Temporary() bool { return true }
+func (e *timeoutError) Is(err error) bool { return err == context.DeadlineExceeded }
-var errTimeout error = &httpError{err: "net/http: timeout awaiting response headers", timeout: true}
+var errTimeout error = &timeoutError{"net/http: timeout awaiting response headers"}
// errRequestCanceled is set to be identical to the one from h2 to facilitate
// testing.