From: kayos@tcp.direct Date: Mon, 8 Jan 2024 07:42:00 +0000 (+0000) Subject: net/http: wrap client errors X-Git-Tag: go1.23rc1~1382 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=67d30fc315f90207a7fd8cde465ab14687835f1c;p=gostls13.git net/http: wrap client errors Fixes #50856 Change-Id: I7fe89fcce223e1571debb73436f8aeb3bfbe4b9f GitHub-Last-Rev: be570e7883be06adbd227a1dfe63a80e384d96f6 GitHub-Pull-Request: golang/go#63448 Reviewed-on: https://go-review.googlesource.com/c/go/+/533119 LUCI-TryBot-Result: Go LUCI Reviewed-by: Damien Neil Reviewed-by: qiulaidongfeng <2645477756@qq.com> Reviewed-by: Cherry Mui --- diff --git a/src/net/http/client.go b/src/net/http/client.go index ee6de24fc1..45b9b915b4 100644 --- a/src/net/http/client.go +++ b/src/net/http/client.go @@ -726,7 +726,7 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) { reqBodyClosed = true if !deadline.IsZero() && didTimeout() { err = &httpError{ - err: err.Error() + " (Client.Timeout exceeded while awaiting headers)", + err: fmt.Errorf("%w (Client.Timeout exceeded while awaiting headers)", err), timeout: true, } } @@ -969,7 +969,7 @@ func (b *cancelTimerBody) Read(p []byte) (n int, err error) { } if b.reqDidTimeout() { err = &httpError{ - err: err.Error() + " (Client.Timeout or context cancellation while reading body)", + err: fmt.Errorf("%w (Client.Timeout exceeded or context cancellation while reading body)", err), timeout: true, } } diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go index 7459b9cb6e..7c71da139a 100644 --- a/src/net/http/client_test.go +++ b/src/net/http/client_test.go @@ -2127,3 +2127,22 @@ func testProbeZeroLengthBody(t *testing.T, mode testMode) { t.Fatalf("server got body %q, want %q", gotBody, content) } } + +func TestClientTimeoutReturnsContextDeadlineExceeded(t *testing.T) { + run(t, testClientTimeoutReturnsContextDeadlineExceeded) +} +func testClientTimeoutReturnsContextDeadlineExceeded(t *testing.T, mode testMode) { + doneCh := make(chan struct{}) + defer close(doneCh) + cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) { + <-doneCh + w.WriteHeader(200) + })) + // check that, upon exceeding Client.Timeout, the returned error is context.DeadlineExceeded. + cst.c.Timeout = 1 * time.Millisecond + req, _ := NewRequest("GET", cst.ts.URL, nil) + _, err := cst.c.Do(req) + if !errors.Is(err, context.DeadlineExceeded) { + t.Fatalf("expected context.DeadlineExceeded, got %v", err) + } +} diff --git a/src/net/http/transport.go b/src/net/http/transport.go index 57c70e72f9..6a1234e7f2 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -2556,15 +2556,17 @@ type writeRequest struct { } type httpError struct { - err string + err error timeout bool } -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 *httpError) Error() string { return e.err.Error() } +func (e *httpError) Timeout() bool { return e.timeout } +func (e *httpError) Temporary() bool { return true } +func (e *httpError) Is(target error) bool { return errors.Is(e.err, target) } +func (e *httpError) Unwrap() error { return e.err } -var errTimeout error = &httpError{err: "net/http: timeout awaiting response headers", timeout: true} +var errTimeout error = &httpError{err: errors.New("net/http: timeout awaiting response headers"), timeout: true} // errRequestCanceled is set to be identical to the one from h2 to facilitate // testing.