Skip to content

Commit 310db63

Browse files
committed
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/8994 Reviewed-by: Ian Lance Taylor <iant@golang.org>
1 parent 11b5f98 commit 310db63

8 files changed

+153
-24
lines changed

src/net/error_test.go

+92
Original file line numberDiff line numberDiff line change
@@ -332,3 +332,95 @@ third:
332332
}
333333
return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
334334
}
335+
336+
// parseCloseError parses nestedErr and reports whether it is a valid
337+
// error value from Close functions.
338+
// It returns nil when nestedErr is valid.
339+
func parseCloseError(nestedErr error) error {
340+
if nestedErr == nil {
341+
return nil
342+
}
343+
344+
switch err := nestedErr.(type) {
345+
case *OpError:
346+
if err := err.isValid(); err != nil {
347+
return err
348+
}
349+
nestedErr = err.Err
350+
goto second
351+
}
352+
return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
353+
354+
second:
355+
if isPlatformError(nestedErr) {
356+
return nil
357+
}
358+
switch err := nestedErr.(type) {
359+
case *os.SyscallError:
360+
nestedErr = err.Err
361+
goto third
362+
case *os.PathError: // for Plan 9
363+
nestedErr = err.Err
364+
goto third
365+
}
366+
switch nestedErr {
367+
case errClosing:
368+
return nil
369+
}
370+
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
371+
372+
third:
373+
if isPlatformError(nestedErr) {
374+
return nil
375+
}
376+
return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
377+
}
378+
379+
func TestCloseError(t *testing.T) {
380+
ln, err := newLocalListener("tcp")
381+
if err != nil {
382+
t.Fatal(err)
383+
}
384+
defer ln.Close()
385+
c, err := Dial(ln.Addr().Network(), ln.Addr().String())
386+
if err != nil {
387+
t.Fatal(err)
388+
}
389+
defer c.Close()
390+
391+
for i := 0; i < 3; i++ {
392+
err = c.(*TCPConn).CloseRead()
393+
if perr := parseCloseError(err); perr != nil {
394+
t.Errorf("#%d: %v", i, perr)
395+
}
396+
}
397+
for i := 0; i < 3; i++ {
398+
err = c.(*TCPConn).CloseWrite()
399+
if perr := parseCloseError(err); perr != nil {
400+
t.Errorf("#%d: %v", i, perr)
401+
}
402+
}
403+
for i := 0; i < 3; i++ {
404+
err = c.Close()
405+
if perr := parseCloseError(err); perr != nil {
406+
t.Errorf("#%d: %v", i, perr)
407+
}
408+
err = ln.Close()
409+
if perr := parseCloseError(err); perr != nil {
410+
t.Errorf("#%d: %v", i, perr)
411+
}
412+
}
413+
414+
pc, err := ListenPacket("udp", "127.0.0.1:0")
415+
if err != nil {
416+
t.Fatal(err)
417+
}
418+
defer pc.Close()
419+
420+
for i := 0; i < 3; i++ {
421+
err = pc.Close()
422+
if perr := parseCloseError(err); perr != nil {
423+
t.Errorf("#%d: %v", i, perr)
424+
}
425+
}
426+
}

src/net/fd_unix.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,7 @@ func (fd *netFD) shutdown(how int) error {
205205
return err
206206
}
207207
defer fd.decref()
208-
err := syscall.Shutdown(fd.sysfd, how)
209-
if err != nil {
210-
return &OpError{"shutdown", fd.net, fd.laddr, err}
211-
}
212-
return nil
208+
return syscall.Shutdown(fd.sysfd, how)
213209
}
214210

215211
func (fd *netFD) closeRead() error {

src/net/fd_windows.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -437,11 +437,7 @@ func (fd *netFD) shutdown(how int) error {
437437
return err
438438
}
439439
defer fd.decref()
440-
err := syscall.Shutdown(fd.sysfd, how)
441-
if err != nil {
442-
return &OpError{"shutdown", fd.net, fd.laddr, err}
443-
}
444-
return nil
440+
return syscall.Shutdown(fd.sysfd, how)
445441
}
446442

447443
func (fd *netFD) closeRead() error {

src/net/net.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,16 @@ func (c *conn) Close() error {
155155
if !c.ok() {
156156
return syscall.EINVAL
157157
}
158-
return c.fd.Close()
158+
err := c.fd.Close()
159+
if err != nil {
160+
err = &OpError{Op: "close", Net: c.fd.net, Err: err}
161+
if c.fd.raddr != nil {
162+
err.(*OpError).Addr = c.fd.raddr
163+
} else {
164+
err.(*OpError).Addr = c.fd.laddr // for unconnected-mode sockets
165+
}
166+
}
167+
return err
159168
}
160169

161170
// LocalAddr returns the local network address.

src/net/tcpsock_plan9.go

+16-4
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ func (c *TCPConn) CloseRead() error {
3636
if !c.ok() {
3737
return syscall.EINVAL
3838
}
39-
return c.fd.closeRead()
39+
err := c.fd.closeRead()
40+
if err != nil {
41+
err = &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: err}
42+
}
43+
return err
4044
}
4145

4246
// CloseWrite shuts down the writing side of the TCP connection.
@@ -45,7 +49,11 @@ func (c *TCPConn) CloseWrite() error {
4549
if !c.ok() {
4650
return syscall.EINVAL
4751
}
48-
return c.fd.closeWrite()
52+
err := c.fd.closeWrite()
53+
if err != nil {
54+
err = &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: err}
55+
}
56+
return err
4957
}
5058

5159
// SetLinger sets the behavior of Close on a connection which still
@@ -155,9 +163,13 @@ func (l *TCPListener) Close() error {
155163
}
156164
if _, err := l.fd.ctl.WriteString("hangup"); err != nil {
157165
l.fd.ctl.Close()
158-
return &OpError{"close", l.fd.ctl.Name(), l.fd.laddr, err}
166+
return &OpError{Op: "close", Net: l.fd.net, Addr: l.fd.laddr, Err: err}
167+
}
168+
err := l.fd.ctl.Close()
169+
if err != nil {
170+
err = &OpError{Op: "close", Net: l.fd.net, Addr: l.fd.laddr, Err: err}
159171
}
160-
return l.fd.ctl.Close()
172+
return err
161173
}
162174

163175
// Addr returns the listener's network address, a *TCPAddr.

src/net/tcpsock_posix.go

+15-3
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,11 @@ func (c *TCPConn) CloseRead() error {
7878
if !c.ok() {
7979
return syscall.EINVAL
8080
}
81-
return c.fd.closeRead()
81+
err := c.fd.closeRead()
82+
if err != nil {
83+
err = &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: err}
84+
}
85+
return err
8286
}
8387

8488
// CloseWrite shuts down the writing side of the TCP connection.
@@ -87,7 +91,11 @@ func (c *TCPConn) CloseWrite() error {
8791
if !c.ok() {
8892
return syscall.EINVAL
8993
}
90-
return c.fd.closeWrite()
94+
err := c.fd.closeWrite()
95+
if err != nil {
96+
err = &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: err}
97+
}
98+
return err
9199
}
92100

93101
// SetLinger sets the behavior of Close on a connection which still
@@ -254,7 +262,11 @@ func (l *TCPListener) Close() error {
254262
if l == nil || l.fd == nil {
255263
return syscall.EINVAL
256264
}
257-
return l.fd.Close()
265+
err := l.fd.Close()
266+
if err != nil {
267+
err = &OpError{Op: "close", Net: l.fd.net, Addr: l.fd.laddr, Err: err}
268+
}
269+
return err
258270
}
259271

260272
// Addr returns the listener's network address, a *TCPAddr.

src/net/unixsock_plan9.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err
6565
// CloseRead shuts down the reading side of the Unix domain connection.
6666
// Most callers should just use Close.
6767
func (c *UnixConn) CloseRead() error {
68-
return syscall.EPLAN9
68+
return &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: syscall.EPLAN9}
6969
}
7070

7171
// CloseWrite shuts down the writing side of the Unix domain connection.
7272
// Most callers should just use Close.
7373
func (c *UnixConn) CloseWrite() error {
74-
return syscall.EPLAN9
74+
return &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: syscall.EPLAN9}
7575
}
7676

7777
// DialUnix connects to the remote address raddr on the network net,
@@ -111,7 +111,7 @@ func (l *UnixListener) Accept() (Conn, error) {
111111
// Close stops listening on the Unix address. Already accepted
112112
// connections are not closed.
113113
func (l *UnixListener) Close() error {
114-
return syscall.EPLAN9
114+
return &OpError{Op: "close", Net: "<nil>", Addr: nil, Err: syscall.EPLAN9}
115115
}
116116

117117
// Addr returns the listener's network address.

src/net/unixsock_posix.go

+15-3
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,11 @@ func (c *UnixConn) CloseRead() error {
233233
if !c.ok() {
234234
return syscall.EINVAL
235235
}
236-
return c.fd.closeRead()
236+
err := c.fd.closeRead()
237+
if err != nil {
238+
err = &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: err}
239+
}
240+
return err
237241
}
238242

239243
// CloseWrite shuts down the writing side of the Unix domain connection.
@@ -242,7 +246,11 @@ func (c *UnixConn) CloseWrite() error {
242246
if !c.ok() {
243247
return syscall.EINVAL
244248
}
245-
return c.fd.closeWrite()
249+
err := c.fd.closeWrite()
250+
if err != nil {
251+
err = &OpError{Op: "close", Net: c.fd.net, Addr: c.fd.raddr, Err: err}
252+
}
253+
return err
246254
}
247255

248256
// DialUnix connects to the remote address raddr on the network net,
@@ -335,7 +343,11 @@ func (l *UnixListener) Close() error {
335343
if l.path[0] != '@' {
336344
syscall.Unlink(l.path)
337345
}
338-
return l.fd.Close()
346+
err := l.fd.Close()
347+
if err != nil {
348+
err = &OpError{Op: "close", Net: l.fd.net, Addr: l.fd.laddr, Err: err}
349+
}
350+
return err
339351
}
340352

341353
// Addr returns the listener's network address.

0 commit comments

Comments
 (0)