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/error_test.go | |
| 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/error_test.go')
| -rw-r--r-- | src/net/error_test.go | 35 |
1 files changed, 23 insertions, 12 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) |
