]> Cypherpunks repositories - gostls13.git/commitdiff
net: export ErrClosed
authorIan Lance Taylor <iant@golang.org>
Tue, 25 Aug 2020 04:42:20 +0000 (21:42 -0700)
committerIan Lance Taylor <iant@golang.org>
Wed, 26 Aug 2020 22:48:00 +0000 (22:48 +0000)
This permits programs to reliably detect whether they are using a
closed network connection.

Fixes #4373

Change-Id: Ib4ce8cc82bbb134c4689f0ebc8b9b11bb8b32a22
Reviewed-on: https://go-review.googlesource.com/c/go/+/250357
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
Reviewed-by: Russ Cox <rsc@golang.org>
doc/go1.16.html
src/net/error_test.go
src/net/net.go

index b11af7fd99a77c92da3f1b7a3a0a892362849558..c82b3b9276f84418c90bba11b5c884eeec1d8f32 100644 (file)
@@ -99,6 +99,18 @@ Do not send CLs removing the interior tags from such phrases.
   TODO
 </p>
 
+<h3 id="net"><a href="/pkg/net/">net</a></h3>
+
+<p><!-- CL -->
+  The case of I/O on a closed network connection, or I/O on a network
+  connection that is closed before any of the I/O completes, can now
+  be detected using the new <a href="/pkg/net/#ErrClosed">ErrClosed</a> error.
+  A typical use would be <code>errors.Is(err, net.ErrClosed)</code>.
+  In earlier releases the only way to reliably detect this case was to
+  match the string returned by the <code>Error</code> method
+  with <code>"use of closed network connection"</code>.
+</p>
+
 <h3 id="unicode"><a href="/pkg/unicode/">unicode</a></h3>
 
 <p><!-- CL 248765 -->
index 8d4a7ffb3d033b90f4b032811181b47d7ccf83c1..62dfb9c15d3920ef9bf388ce8a9459fe91072603 100644 (file)
@@ -8,6 +8,7 @@ package net
 
 import (
        "context"
+       "errors"
        "fmt"
        "internal/poll"
        "io"
@@ -101,7 +102,7 @@ second:
                goto third
        }
        switch nestedErr {
-       case errCanceled, poll.ErrNetClosing, errMissingAddress, errNoSuitableAddress,
+       case errCanceled, ErrClosed, errMissingAddress, errNoSuitableAddress,
                context.DeadlineExceeded, context.Canceled:
                return nil
        }
@@ -436,7 +437,7 @@ second:
                goto third
        }
        switch nestedErr {
-       case poll.ErrNetClosing, errTimeout, poll.ErrNotPollable, os.ErrDeadlineExceeded:
+       case ErrClosed, errTimeout, poll.ErrNotPollable, os.ErrDeadlineExceeded:
                return nil
        }
        return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
@@ -478,7 +479,7 @@ second:
                goto third
        }
        switch nestedErr {
-       case errCanceled, poll.ErrNetClosing, errMissingAddress, errTimeout, os.ErrDeadlineExceeded, ErrWriteToConnected, io.ErrUnexpectedEOF:
+       case errCanceled, ErrClosed, errMissingAddress, errTimeout, os.ErrDeadlineExceeded, ErrWriteToConnected, io.ErrUnexpectedEOF:
                return nil
        }
        return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
@@ -508,6 +509,10 @@ func parseCloseError(nestedErr error, isShutdown bool) error {
                return fmt.Errorf("error string %q does not contain expected string %q", nestedErr, want)
        }
 
+       if !isShutdown && !errors.Is(nestedErr, ErrClosed) {
+               return fmt.Errorf("errors.Is(%v, errClosed) returns false, want true", nestedErr)
+       }
+
        switch err := nestedErr.(type) {
        case *OpError:
                if err := err.isValid(); err != nil {
@@ -531,7 +536,7 @@ second:
                goto third
        }
        switch nestedErr {
-       case poll.ErrNetClosing:
+       case ErrClosed:
                return nil
        }
        return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
@@ -627,7 +632,7 @@ second:
                goto third
        }
        switch nestedErr {
-       case poll.ErrNetClosing, errTimeout, poll.ErrNotPollable, os.ErrDeadlineExceeded:
+       case ErrClosed, errTimeout, poll.ErrNotPollable, os.ErrDeadlineExceeded:
                return nil
        }
        return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
@@ -706,7 +711,7 @@ second:
                goto third
        }
        switch nestedErr {
-       case poll.ErrNetClosing:
+       case ErrClosed:
                return nil
        }
        return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
index 2e61a7c02e07dc73b83859d98819f4c90222f12f..4b4ed129ccc3f8e798e601f6110230f0579ad49e 100644 (file)
@@ -81,6 +81,7 @@ package net
 import (
        "context"
        "errors"
+       "internal/poll"
        "io"
        "os"
        "sync"
@@ -632,6 +633,17 @@ func (e *DNSError) Timeout() bool { return e.IsTimeout }
 // error and return a DNSError for which Temporary returns false.
 func (e *DNSError) Temporary() bool { return e.IsTimeout || e.IsTemporary }
 
+// errClosed exists just so that the docs for ErrClosed don't mention
+// the internal package poll.
+var errClosed = poll.ErrNetClosing
+
+// ErrClosed is the error returned by an I/O call on a network
+// connection that has already been closed, or that is closed by
+// another goroutine before the I/O is completed. This may be wrapped
+// in another error, and should normally be tested using
+// errors.Is(err, net.ErrClosed).
+var ErrClosed = errClosed
+
 type writerOnly struct {
        io.Writer
 }