Skip to content

Commit

Permalink
net: export ErrClosed
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
ianlancetaylor committed Aug 26, 2020
1 parent 694fc8e commit e9ad52e
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 6 deletions.
12 changes: 12 additions & 0 deletions doc/go1.16.html
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ <h2 id="library">Core library</h2>
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 -->
Expand Down
17 changes: 11 additions & 6 deletions src/net/error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package net

import (
"context"
"errors"
"fmt"
"internal/poll"
"io"
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
12 changes: 12 additions & 0 deletions src/net/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ package net
import (
"context"
"errors"
"internal/poll"
"io"
"os"
"sync"
Expand Down Expand Up @@ -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
}
Expand Down

0 comments on commit e9ad52e

Please sign in to comment.