diff options
| author | Ian Lance Taylor <iant@golang.org> | 2017-04-07 15:53:19 -0700 |
|---|---|---|
| committer | Ian Lance Taylor <iant@golang.org> | 2017-04-26 00:03:14 +0000 |
| commit | fb4b4342fe298fda640bfa74f24b7bd58519deba (patch) | |
| tree | c7a059175bfb5630b432a5efe5d467abc59c2794 /src/net | |
| parent | 2fb2ebc32ee37a66e3d6a77ff9450665153a604c (diff) | |
| download | go-fb4b4342fe298fda640bfa74f24b7bd58519deba.tar.xz | |
os, net, internal/poll: return consistent error for closed socket
In the past we returned "use of closed network connection" when using
a closed network descriptor in some way. In CL 36799 that was changed
to return "use of closed file or network connection". Because programs
have no access to a value of this error type (see issue #4373) they
resort to doing direct string comparisons (see issue #19252). This CL
restores the old error string so that we don't break programs
unnecessarily with the 1.9 release.
This adds a test to the net package for the expected string.
For symmetry check that the os package returns the expected error,
which for os already exists as os.ErrClosed.
Updates #4373.
Fixed #19252.
Change-Id: I5b83fd12cfa03501a077cad9336499b819f4a38b
Reviewed-on: https://go-review.googlesource.com/39997
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
Diffstat (limited to 'src/net')
| -rw-r--r-- | src/net/error_test.go | 35 | ||||
| -rw-r--r-- | src/net/fd_unix.go | 6 | ||||
| -rw-r--r-- | src/net/file_test.go | 4 | ||||
| -rw-r--r-- | src/net/net_test.go | 14 |
4 files changed, 35 insertions, 24 deletions
diff --git a/src/net/error_test.go b/src/net/error_test.go index 021968b079..9791e6fe4d 100644 --- a/src/net/error_test.go +++ b/src/net/error_test.go @@ -13,6 +13,7 @@ import ( "net/internal/socktest" "os" "runtime" + "strings" "testing" "time" ) @@ -98,7 +99,7 @@ second: goto third } switch nestedErr { - case errCanceled, poll.ErrClosing, errMissingAddress, errNoSuitableAddress, + case errCanceled, poll.ErrNetClosing, errMissingAddress, errNoSuitableAddress, context.DeadlineExceeded, context.Canceled: return nil } @@ -433,7 +434,7 @@ second: goto third } switch nestedErr { - case poll.ErrClosing, poll.ErrTimeout: + case poll.ErrNetClosing, poll.ErrTimeout: return nil } return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) @@ -475,7 +476,7 @@ second: goto third } switch nestedErr { - case errCanceled, poll.ErrClosing, errMissingAddress, poll.ErrTimeout, ErrWriteToConnected, io.ErrUnexpectedEOF: + case errCanceled, poll.ErrNetClosing, errMissingAddress, poll.ErrTimeout, ErrWriteToConnected, io.ErrUnexpectedEOF: return nil } return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) @@ -490,11 +491,21 @@ third: // parseCloseError parses nestedErr and reports whether it is a valid // error value from Close functions. // It returns nil when nestedErr is valid. -func parseCloseError(nestedErr error) error { +func parseCloseError(nestedErr error, isShutdown bool) error { if nestedErr == nil { return nil } + // Because historically we have not exported the error that we + // return for an operation on a closed network connection, + // there are programs that test for the exact error string. + // Verify that string here so that we don't break those + // programs unexpectedly. See issues #4373 and #19252. + want := "use of closed network connection" + if !isShutdown && !strings.Contains(nestedErr.Error(), want) { + return fmt.Errorf("error string %q does not contain expected string %q", nestedErr, want) + } + switch err := nestedErr.(type) { case *OpError: if err := err.isValid(); err != nil { @@ -518,7 +529,7 @@ second: goto third } switch nestedErr { - case poll.ErrClosing: + case poll.ErrNetClosing: return nil } return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) @@ -548,23 +559,23 @@ func TestCloseError(t *testing.T) { for i := 0; i < 3; i++ { err = c.(*TCPConn).CloseRead() - if perr := parseCloseError(err); perr != nil { + if perr := parseCloseError(err, true); perr != nil { t.Errorf("#%d: %v", i, perr) } } for i := 0; i < 3; i++ { err = c.(*TCPConn).CloseWrite() - if perr := parseCloseError(err); perr != nil { + if perr := parseCloseError(err, true); perr != nil { t.Errorf("#%d: %v", i, perr) } } for i := 0; i < 3; i++ { err = c.Close() - if perr := parseCloseError(err); perr != nil { + if perr := parseCloseError(err, false); perr != nil { t.Errorf("#%d: %v", i, perr) } err = ln.Close() - if perr := parseCloseError(err); perr != nil { + if perr := parseCloseError(err, false); perr != nil { t.Errorf("#%d: %v", i, perr) } } @@ -577,7 +588,7 @@ func TestCloseError(t *testing.T) { for i := 0; i < 3; i++ { err = pc.Close() - if perr := parseCloseError(err); perr != nil { + if perr := parseCloseError(err, false); perr != nil { t.Errorf("#%d: %v", i, perr) } } @@ -614,7 +625,7 @@ second: goto third } switch nestedErr { - case poll.ErrClosing, poll.ErrTimeout: + case poll.ErrNetClosing, poll.ErrTimeout: return nil } return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) @@ -693,7 +704,7 @@ second: goto third } switch nestedErr { - case poll.ErrClosing: + case poll.ErrNetClosing: return nil } return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) diff --git a/src/net/fd_unix.go b/src/net/fd_unix.go index 505a1f1a02..1122ee4dbe 100644 --- a/src/net/fd_unix.go +++ b/src/net/fd_unix.go @@ -43,7 +43,7 @@ func newFD(sysfd, family, sotype int, net string) (*netFD, error) { } func (fd *netFD) init() error { - return fd.pfd.Init() + return fd.pfd.Init(fd.net, true) } func (fd *netFD) setAddr(laddr, raddr Addr) { @@ -75,7 +75,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret erro return mapErr(ctx.Err()) default: } - if err := fd.pfd.Init(); err != nil { + if err := fd.pfd.Init(fd.net, true); err != nil { return err } runtime.KeepAlive(fd) @@ -93,7 +93,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret erro default: return os.NewSyscallError("connect", err) } - if err := fd.pfd.Init(); err != nil { + if err := fd.pfd.Init(fd.net, true); err != nil { return err } if deadline, _ := ctx.Deadline(); !deadline.IsZero() { diff --git a/src/net/file_test.go b/src/net/file_test.go index 6566ce21a1..abf8b3a699 100644 --- a/src/net/file_test.go +++ b/src/net/file_test.go @@ -90,7 +90,7 @@ func TestFileConn(t *testing.T) { f, err = c1.File() } if err := c1.Close(); err != nil { - if perr := parseCloseError(err); perr != nil { + if perr := parseCloseError(err, false); perr != nil { t.Error(perr) } t.Error(err) @@ -256,7 +256,7 @@ func TestFilePacketConn(t *testing.T) { f, err = c1.File() } if err := c1.Close(); err != nil { - if perr := parseCloseError(err); perr != nil { + if perr := parseCloseError(err, false); perr != nil { t.Error(perr) } t.Error(err) diff --git a/src/net/net_test.go b/src/net/net_test.go index 9a9a7e552c..024505e7c6 100644 --- a/src/net/net_test.go +++ b/src/net/net_test.go @@ -54,7 +54,7 @@ func TestCloseRead(t *testing.T) { err = c.CloseRead() } if err != nil { - if perr := parseCloseError(err); perr != nil { + if perr := parseCloseError(err, true); perr != nil { t.Error(perr) } t.Fatal(err) @@ -94,7 +94,7 @@ func TestCloseWrite(t *testing.T) { err = c.CloseWrite() } if err != nil { - if perr := parseCloseError(err); perr != nil { + if perr := parseCloseError(err, true); perr != nil { t.Error(perr) } t.Error(err) @@ -139,7 +139,7 @@ func TestCloseWrite(t *testing.T) { err = c.CloseWrite() } if err != nil { - if perr := parseCloseError(err); perr != nil { + if perr := parseCloseError(err, true); perr != nil { t.Error(perr) } t.Fatal(err) @@ -184,7 +184,7 @@ func TestConnClose(t *testing.T) { defer c.Close() if err := c.Close(); err != nil { - if perr := parseCloseError(err); perr != nil { + if perr := parseCloseError(err, false); perr != nil { t.Error(perr) } t.Fatal(err) @@ -215,7 +215,7 @@ func TestListenerClose(t *testing.T) { dst := ln.Addr().String() if err := ln.Close(); err != nil { - if perr := parseCloseError(err); perr != nil { + if perr := parseCloseError(err, false); perr != nil { t.Error(perr) } t.Fatal(err) @@ -269,7 +269,7 @@ func TestPacketConnClose(t *testing.T) { defer c.Close() if err := c.Close(); err != nil { - if perr := parseCloseError(err); perr != nil { + if perr := parseCloseError(err, false); perr != nil { t.Error(perr) } t.Fatal(err) @@ -292,7 +292,7 @@ func TestListenCloseListen(t *testing.T) { } addr := ln.Addr().String() if err := ln.Close(); err != nil { - if perr := parseCloseError(err); perr != nil { + if perr := parseCloseError(err, false); perr != nil { t.Error(perr) } t.Fatal(err) |
