From 85b5f86584686677c554b2538a7edee96d684aea Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 30 Mar 2022 17:26:21 -0700 Subject: [PATCH] net: support error.Is of network errors and context errors Change timeouts to be Is(context.DeadlineExceeded) and cancelation to be Is(context.Canceled). Fixes #51428 Change-Id: Ic580bd9da0f338e993fb79138875a78d99cc1a1d Reviewed-on: https://go-review.googlesource.com/c/go/+/396877 Trust: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Gopher Robot Reviewed-by: Tobias Klauser --- doc/go1.19.html | 12 ++++++++++++ src/net/error_test.go | 9 +++++++++ src/net/net.go | 23 +++++++++++++++++------ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index cfeb3d3d79..857d8ed8ce 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -89,6 +89,18 @@ Do not send CLs removing the interior tags from such phrases. Please report any such problems on the issue tracker.

+ +

+ When a net package function or method returns an "I/O timeout" + error, the error will now satisfy errors.Is(err, + context.Canceled). When a net package function returns + an "operation was canceled" error, the error will now satisfy + errors.Is(err, context.DeadlineExceeded). + These changes are intended to make it easier for code to test + for cases in which a context cancelation or timeout causes a net + package function or method to return an error, while preserving + backward compatibility for error messages. +

diff --git a/src/net/error_test.go b/src/net/error_test.go index 4a191673e2..4467dc11b2 100644 --- a/src/net/error_test.go +++ b/src/net/error_test.go @@ -795,3 +795,12 @@ func parseLookupPortError(nestedErr error) error { } return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr) } + +func TestContextError(t *testing.T) { + if !errors.Is(errCanceled, context.Canceled) { + t.Error("errCanceled is not context.Canceled") + } + if !errors.Is(errTimeout, context.DeadlineExceeded) { + t.Error("errTimeout is not context.DeadlineExceeded") + } +} diff --git a/src/net/net.go b/src/net/net.go index d91e743a01..ec718d5e43 100644 --- a/src/net/net.go +++ b/src/net/net.go @@ -413,15 +413,20 @@ var ( errMissingAddress = errors.New("missing address") // For both read and write operations. - errCanceled = errors.New("operation was canceled") + errCanceled = canceledError{} ErrWriteToConnected = errors.New("use of WriteTo with pre-connected connection") ) +// canceledError lets us return the same error string we have always +// returned, while still being Is context.Canceled. +type canceledError struct{} + +func (canceledError) Error() string { return "operation was canceled" } + +func (canceledError) Is(err error) bool { return err == context.Canceled } + // mapErr maps from the context errors to the historical internal net // error values. -// -// TODO(bradfitz): get rid of this after adjusting tests and making -// context.DeadlineExceeded implement net.Error? func mapErr(err error) error { switch err { case context.Canceled: @@ -580,10 +585,12 @@ func (e InvalidAddrError) Temporary() bool { return false } // errTimeout exists to return the historical "i/o timeout" string // for context.DeadlineExceeded. See mapErr. // It is also used when Dialer.Deadline is exceeded. +// error.Is(errTimeout, context.DeadlineExceeded) returns true. // // TODO(iant): We could consider changing this to os.ErrDeadlineExceeded -// in the future, but note that that would conflict with the TODO -// at mapErr that suggests changing it to context.DeadlineExceeded. +// in the future, if we make +// errors.Is(os.ErrDeadlineExceeded, context.DeadlineExceeded) +// return true. var errTimeout error = &timeoutError{} type timeoutError struct{} @@ -592,6 +599,10 @@ func (e *timeoutError) Error() string { return "i/o timeout" } 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 +} + // DNSConfigError represents an error reading the machine's DNS configuration. // (No longer used; kept for compatibility.) type DNSConfigError struct { -- 2.50.0