Commit 310db63c authored by Mikio Hara's avatar Mikio Hara

net: fix inconsistent error values on Close

This change fixes inconsistent error values on Close, CloseRead and
CloseWrite.

Updates #4856.

Change-Id: I3c4d46ccd7d6e1a2f52d8e75b512f62c533a368d
Reviewed-on: https://go-review.googlesource.com/8994Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
parent 11b5f98b
...@@ -332,3 +332,95 @@ third: ...@@ -332,3 +332,95 @@ third:
} }
return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr) return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
} }
// 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 {
if nestedErr == nil {
return nil
}
switch err := nestedErr.(type) {
case *OpError:
if err := err.isValid(); err != nil {
return err
}
nestedErr = err.Err
goto second
}
return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
second:
if isPlatformError(nestedErr) {
return nil
}
switch err := nestedErr.(type) {
case *os.SyscallError:
nestedErr = err.Err
goto third
case *os.PathError: // for Plan 9
nestedErr = err.Err
goto third
}
switch nestedErr {
case errClosing:
return nil
}
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
third:
if isPlatformError(nestedErr) {
return nil
}
return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
}
func TestCloseError(t *testing.T) {
ln, err := newLocalListener("tcp")
if err != nil {
t.Fatal(err)
}
defer ln.Close()
c, err := Dial(ln.Addr().Network(), ln.Addr().String())
if err != nil {
t.Fatal(err)
}
defer c.Close()
for i := 0; i < 3; i++ {
err = c.(*TCPConn).CloseRead()
if perr := parseCloseError(err); perr != nil {
t.Errorf("#%d: %v", i, perr)
}
}
for i := 0; i < 3; i++ {
err = c.(*TCPConn).CloseWrite()
if perr := parseCloseError(err); perr != nil {
t.Errorf("#%d: %v", i, perr)
}
}
for i := 0; i < 3; i++ {
err = c.Close()
if perr := parseCloseError(err); perr != nil {
t.Errorf("#%d: %v", i, perr)
}
err = ln.Close()
if perr := parseCloseError(err); perr != nil {
t.Errorf("#%d: %v", i, perr)
}
}
pc, err := ListenPacket("udp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer pc.Close()
for i := 0; i < 3; i++ {
err = pc.Close()
if perr := parseCloseError(err); perr != nil {
t.Errorf("#%d: %v", i, perr)
}
}
}
...@@ -205,11 +205,7 @@ func (fd *netFD) shutdown(how int) error { ...@@ -205,11 +205,7 @@ func (fd *netFD) shutdown(how int) error {
return err return err
} }
defer fd.decref() defer fd.decref()
err := syscall.Shutdown(fd.sysfd, how) return syscall.Shutdown(fd.sysfd, how)
if err != nil {
return &OpError{"shutdown", fd.net, fd.laddr, err}
}
return nil
} }
func (fd *netFD) closeRead() error { func (fd *netFD) closeRead() error {
......
...@@ -437,11 +437,7 @@ func (fd *netFD) shutdown(how int) error { ...@@ -437,11 +437,7 @@ func (fd *netFD) shutdown(how int) error {
return err return err
} }
defer fd.decref() defer fd.decref()
err := syscall.Shutdown(fd.sysfd, how) return syscall.Shutdown(fd.sysfd, how)
if err != nil {
return &OpError{"shutdown", fd.net, fd.laddr, err}
}
return nil
} }
func (fd *netFD) closeRead() error { func (fd *netFD) closeRead() error {
......
...@@ -155,7 +155,16 @@ func (c *conn) Close() error { ...@@ -155,7 +155,16 @@ func (c *conn) Close() error {
if !c.ok() { if !c.ok() {
return syscall.EINVAL return syscall.EINVAL
} }
return c.fd.Close() err := c.fd.Close()
if err != nil {
err = &OpError{Op: "close", Net: c.fd.net, Err: err}
if c.fd.raddr != nil {
err.(*OpError).Addr = c.fd.raddr
} else {
err.(*OpError).Addr = c.fd.laddr // for unconnected-mode sockets
}
}
return err
} }
// LocalAddr returns the local network address. // LocalAddr returns the local network address.
......
...@@ -36,7 +36,11 @@ func (c *TCPConn) CloseRead() error { ...@@ -36,7 +36,11 @@ func (c *TCPConn) CloseRead() error {
if !c.ok() { if !c.ok() {
return syscall.EINVAL return syscall.EINVAL
} }
return c.fd.closeRead() err := c.fd.closeRead()
if err != nil {
err = &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: err}
}
return err
} }
// CloseWrite shuts down the writing side of the TCP connection. // CloseWrite shuts down the writing side of the TCP connection.
...@@ -45,7 +49,11 @@ func (c *TCPConn) CloseWrite() error { ...@@ -45,7 +49,11 @@ func (c *TCPConn) CloseWrite() error {
if !c.ok() { if !c.ok() {
return syscall.EINVAL return syscall.EINVAL
} }
return c.fd.closeWrite() err := c.fd.closeWrite()
if err != nil {
err = &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: err}
}
return err
} }
// SetLinger sets the behavior of Close on a connection which still // SetLinger sets the behavior of Close on a connection which still
...@@ -155,9 +163,13 @@ func (l *TCPListener) Close() error { ...@@ -155,9 +163,13 @@ func (l *TCPListener) Close() error {
} }
if _, err := l.fd.ctl.WriteString("hangup"); err != nil { if _, err := l.fd.ctl.WriteString("hangup"); err != nil {
l.fd.ctl.Close() l.fd.ctl.Close()
return &OpError{"close", l.fd.ctl.Name(), l.fd.laddr, err} return &OpError{Op: "close", Net: l.fd.net, Addr: l.fd.laddr, Err: err}
}
err := l.fd.ctl.Close()
if err != nil {
err = &OpError{Op: "close", Net: l.fd.net, Addr: l.fd.laddr, Err: err}
} }
return l.fd.ctl.Close() return err
} }
// Addr returns the listener's network address, a *TCPAddr. // Addr returns the listener's network address, a *TCPAddr.
......
...@@ -78,7 +78,11 @@ func (c *TCPConn) CloseRead() error { ...@@ -78,7 +78,11 @@ func (c *TCPConn) CloseRead() error {
if !c.ok() { if !c.ok() {
return syscall.EINVAL return syscall.EINVAL
} }
return c.fd.closeRead() err := c.fd.closeRead()
if err != nil {
err = &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: err}
}
return err
} }
// CloseWrite shuts down the writing side of the TCP connection. // CloseWrite shuts down the writing side of the TCP connection.
...@@ -87,7 +91,11 @@ func (c *TCPConn) CloseWrite() error { ...@@ -87,7 +91,11 @@ func (c *TCPConn) CloseWrite() error {
if !c.ok() { if !c.ok() {
return syscall.EINVAL return syscall.EINVAL
} }
return c.fd.closeWrite() err := c.fd.closeWrite()
if err != nil {
err = &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: err}
}
return err
} }
// SetLinger sets the behavior of Close on a connection which still // SetLinger sets the behavior of Close on a connection which still
...@@ -254,7 +262,11 @@ func (l *TCPListener) Close() error { ...@@ -254,7 +262,11 @@ func (l *TCPListener) Close() error {
if l == nil || l.fd == nil { if l == nil || l.fd == nil {
return syscall.EINVAL return syscall.EINVAL
} }
return l.fd.Close() err := l.fd.Close()
if err != nil {
err = &OpError{Op: "close", Net: l.fd.net, Addr: l.fd.laddr, Err: err}
}
return err
} }
// Addr returns the listener's network address, a *TCPAddr. // Addr returns the listener's network address, a *TCPAddr.
......
...@@ -65,13 +65,13 @@ func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err ...@@ -65,13 +65,13 @@ func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err
// CloseRead shuts down the reading side of the Unix domain connection. // CloseRead shuts down the reading side of the Unix domain connection.
// Most callers should just use Close. // Most callers should just use Close.
func (c *UnixConn) CloseRead() error { func (c *UnixConn) CloseRead() error {
return syscall.EPLAN9 return &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: syscall.EPLAN9}
} }
// CloseWrite shuts down the writing side of the Unix domain connection. // CloseWrite shuts down the writing side of the Unix domain connection.
// Most callers should just use Close. // Most callers should just use Close.
func (c *UnixConn) CloseWrite() error { func (c *UnixConn) CloseWrite() error {
return syscall.EPLAN9 return &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: syscall.EPLAN9}
} }
// DialUnix connects to the remote address raddr on the network net, // DialUnix connects to the remote address raddr on the network net,
...@@ -111,7 +111,7 @@ func (l *UnixListener) Accept() (Conn, error) { ...@@ -111,7 +111,7 @@ func (l *UnixListener) Accept() (Conn, error) {
// Close stops listening on the Unix address. Already accepted // Close stops listening on the Unix address. Already accepted
// connections are not closed. // connections are not closed.
func (l *UnixListener) Close() error { func (l *UnixListener) Close() error {
return syscall.EPLAN9 return &OpError{Op: "close", Net: "<nil>", Addr: nil, Err: syscall.EPLAN9}
} }
// Addr returns the listener's network address. // Addr returns the listener's network address.
......
...@@ -233,7 +233,11 @@ func (c *UnixConn) CloseRead() error { ...@@ -233,7 +233,11 @@ func (c *UnixConn) CloseRead() error {
if !c.ok() { if !c.ok() {
return syscall.EINVAL return syscall.EINVAL
} }
return c.fd.closeRead() err := c.fd.closeRead()
if err != nil {
err = &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: err}
}
return err
} }
// CloseWrite shuts down the writing side of the Unix domain connection. // CloseWrite shuts down the writing side of the Unix domain connection.
...@@ -242,7 +246,11 @@ func (c *UnixConn) CloseWrite() error { ...@@ -242,7 +246,11 @@ func (c *UnixConn) CloseWrite() error {
if !c.ok() { if !c.ok() {
return syscall.EINVAL return syscall.EINVAL
} }
return c.fd.closeWrite() err := c.fd.closeWrite()
if err != nil {
err = &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: err}
}
return err
} }
// DialUnix connects to the remote address raddr on the network net, // DialUnix connects to the remote address raddr on the network net,
...@@ -335,7 +343,11 @@ func (l *UnixListener) Close() error { ...@@ -335,7 +343,11 @@ func (l *UnixListener) Close() error {
if l.path[0] != '@' { if l.path[0] != '@' {
syscall.Unlink(l.path) syscall.Unlink(l.path)
} }
return l.fd.Close() err := l.fd.Close()
if err != nil {
err = &OpError{Op: "close", Net: l.fd.net, Addr: l.fd.laddr, Err: err}
}
return err
} }
// Addr returns the listener's network address. // Addr returns the listener's network address.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment