]> Cypherpunks repositories - gostls13.git/commitdiff
net: support error.Is of network errors and context errors
authorIan Lance Taylor <iant@golang.org>
Thu, 31 Mar 2022 00:26:21 +0000 (17:26 -0700)
committerIan Lance Taylor <iant@golang.org>
Sun, 3 Apr 2022 15:43:15 +0000 (15:43 +0000)
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 <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
doc/go1.19.html
src/net/error_test.go
src/net/net.go

index cfeb3d3d79417ae3415be569e8c7a02cead2314c..857d8ed8cee6dd922c3bfb0d884b96bceffe6543 100644 (file)
@@ -89,6 +89,18 @@ Do not send CLs removing the interior tags from such phrases.
       Please report any such problems on <a href="/issue/new">the
       issue tracker</a>.
     </p>
+
+    <p><!-- CL 396877 -->
+      When a net package function or method returns an "I/O timeout"
+      error, the error will now satisfy <code>errors.Is(err,
+      context.Canceled)</code>.  When a net package function returns
+      an "operation was canceled" error, the error will now satisfy
+      <code>errors.Is(err, context.DeadlineExceeded)</code>.
+      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.
+    </p>
   </dd>
 </dl><!-- net -->
 
index 4a191673e2fcb22101a2d7a58c4459166d17a2d0..4467dc11b20965036678575d9d4779499c3be4ce 100644 (file)
@@ -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")
+       }
+}
index d91e743a014e21c7b586b733fb731868bf9c49d5..ec718d5e43246d56796e9626f059d3475c3fd151 100644 (file)
@@ -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 {