From fb4b4342fe298fda640bfa74f24b7bd58519deba Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 7 Apr 2017 15:53:19 -0700 Subject: 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 TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick Reviewed-by: Russ Cox --- src/net/error_test.go | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) (limited to 'src/net/error_test.go') 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) -- cgit v1.3-5-g9baa