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