Skip to content

Commit e9ad52e

Browse files
net: export ErrClosed
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Tobias Klauser <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent 694fc8e commit e9ad52e

File tree

3 files changed

+35
-6
lines changed

3 files changed

+35
-6
lines changed

doc/go1.16.html

+12
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,18 @@ <h2 id="library">Core library</h2>
9999
TODO
100100
</p>
101101

102+
<h3 id="net"><a href="/pkg/net/">net</a></h3>
103+
104+
<p><!-- CL -->
105+
The case of I/O on a closed network connection, or I/O on a network
106+
connection that is closed before any of the I/O completes, can now
107+
be detected using the new <a href="/pkg/net/#ErrClosed">ErrClosed</a> error.
108+
A typical use would be <code>errors.Is(err, net.ErrClosed)</code>.
109+
In earlier releases the only way to reliably detect this case was to
110+
match the string returned by the <code>Error</code> method
111+
with <code>"use of closed network connection"</code>.
112+
</p>
113+
102114
<h3 id="unicode"><a href="/pkg/unicode/">unicode</a></h3>
103115

104116
<p><!-- CL 248765 -->

src/net/error_test.go

+11-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package net
88

99
import (
1010
"context"
11+
"errors"
1112
"fmt"
1213
"internal/poll"
1314
"io"
@@ -101,7 +102,7 @@ second:
101102
goto third
102103
}
103104
switch nestedErr {
104-
case errCanceled, poll.ErrNetClosing, errMissingAddress, errNoSuitableAddress,
105+
case errCanceled, ErrClosed, errMissingAddress, errNoSuitableAddress,
105106
context.DeadlineExceeded, context.Canceled:
106107
return nil
107108
}
@@ -436,7 +437,7 @@ second:
436437
goto third
437438
}
438439
switch nestedErr {
439-
case poll.ErrNetClosing, errTimeout, poll.ErrNotPollable, os.ErrDeadlineExceeded:
440+
case ErrClosed, errTimeout, poll.ErrNotPollable, os.ErrDeadlineExceeded:
440441
return nil
441442
}
442443
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
@@ -478,7 +479,7 @@ second:
478479
goto third
479480
}
480481
switch nestedErr {
481-
case errCanceled, poll.ErrNetClosing, errMissingAddress, errTimeout, os.ErrDeadlineExceeded, ErrWriteToConnected, io.ErrUnexpectedEOF:
482+
case errCanceled, ErrClosed, errMissingAddress, errTimeout, os.ErrDeadlineExceeded, ErrWriteToConnected, io.ErrUnexpectedEOF:
482483
return nil
483484
}
484485
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
@@ -508,6 +509,10 @@ func parseCloseError(nestedErr error, isShutdown bool) error {
508509
return fmt.Errorf("error string %q does not contain expected string %q", nestedErr, want)
509510
}
510511

512+
if !isShutdown && !errors.Is(nestedErr, ErrClosed) {
513+
return fmt.Errorf("errors.Is(%v, errClosed) returns false, want true", nestedErr)
514+
}
515+
511516
switch err := nestedErr.(type) {
512517
case *OpError:
513518
if err := err.isValid(); err != nil {
@@ -531,7 +536,7 @@ second:
531536
goto third
532537
}
533538
switch nestedErr {
534-
case poll.ErrNetClosing:
539+
case ErrClosed:
535540
return nil
536541
}
537542
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
@@ -627,7 +632,7 @@ second:
627632
goto third
628633
}
629634
switch nestedErr {
630-
case poll.ErrNetClosing, errTimeout, poll.ErrNotPollable, os.ErrDeadlineExceeded:
635+
case ErrClosed, errTimeout, poll.ErrNotPollable, os.ErrDeadlineExceeded:
631636
return nil
632637
}
633638
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
@@ -706,7 +711,7 @@ second:
706711
goto third
707712
}
708713
switch nestedErr {
709-
case poll.ErrNetClosing:
714+
case ErrClosed:
710715
return nil
711716
}
712717
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)

src/net/net.go

+12
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ package net
8181
import (
8282
"context"
8383
"errors"
84+
"internal/poll"
8485
"io"
8586
"os"
8687
"sync"
@@ -632,6 +633,17 @@ func (e *DNSError) Timeout() bool { return e.IsTimeout }
632633
// error and return a DNSError for which Temporary returns false.
633634
func (e *DNSError) Temporary() bool { return e.IsTimeout || e.IsTemporary }
634635

636+
// errClosed exists just so that the docs for ErrClosed don't mention
637+
// the internal package poll.
638+
var errClosed = poll.ErrNetClosing
639+
640+
// ErrClosed is the error returned by an I/O call on a network
641+
// connection that has already been closed, or that is closed by
642+
// another goroutine before the I/O is completed. This may be wrapped
643+
// in another error, and should normally be tested using
644+
// errors.Is(err, net.ErrClosed).
645+
var ErrClosed = errClosed
646+
635647
type writerOnly struct {
636648
io.Writer
637649
}

0 commit comments

Comments
 (0)