]> Cypherpunks repositories - gostls13.git/commitdiff
net: fix inconsistent error values on Write
authorMikio Hara <mikioh.mikioh@gmail.com>
Thu, 16 Apr 2015 02:26:44 +0000 (11:26 +0900)
committerMikio Hara <mikioh.mikioh@gmail.com>
Sat, 18 Apr 2015 02:44:08 +0000 (02:44 +0000)
This change fixes inconsistent error values on Write,
WriteTo{,UDP,IP,Unix} and WriteMsg{UDP,IP,Unix}.

Updates #4856.

Change-Id: I4208ab6a0650455ad7d70a80a2d6169351d6055f
Reviewed-on: https://go-review.googlesource.com/8993
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/net/error_test.go
src/net/fd_unix.go
src/net/fd_windows.go
src/net/iprawsock_plan9.go
src/net/iprawsock_posix.go
src/net/net.go
src/net/timeout_test.go
src/net/udpsock_plan9.go
src/net/udpsock_posix.go
src/net/unixsock_plan9.go
src/net/unixsock_posix.go

index 4b57d9e457ef3bdecd0543174f41741a1ecf260a..5668027d9801a8b946ce0a5ee410fa5d593d2e55 100644 (file)
@@ -292,3 +292,43 @@ third:
        }
        return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
 }
+
+// parseWriteError parses nestedErr and reports whether it is a valid
+// error value from Write functions.
+// It returns nil when nestedErr is valid.
+func parseWriteError(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
+       }
+       switch nestedErr {
+       case errClosing, errTimeout, ErrWriteToConnected, io.ErrUnexpectedEOF:
+               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)
+}
index 08c8568a809dc272a2090dcc0e422c56d5b2b143..99d7a939d697127cd71acd0f531a358c694eb64f 100644 (file)
@@ -298,7 +298,7 @@ func (fd *netFD) Write(p []byte) (nn int, err error) {
        }
        defer fd.writeUnlock()
        if err := fd.pd.PrepareWrite(); err != nil {
-               return 0, &OpError{"write", fd.net, fd.raddr, err}
+               return 0, err
        }
        for {
                var n int
@@ -323,9 +323,6 @@ func (fd *netFD) Write(p []byte) (nn int, err error) {
                        break
                }
        }
-       if err != nil {
-               err = &OpError{"write", fd.net, fd.raddr, err}
-       }
        return nn, err
 }
 
@@ -335,7 +332,7 @@ func (fd *netFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
        }
        defer fd.writeUnlock()
        if err := fd.pd.PrepareWrite(); err != nil {
-               return 0, &OpError{"write", fd.net, fd.raddr, err}
+               return 0, err
        }
        for {
                err = syscall.Sendto(fd.sysfd, p, 0, sa)
@@ -348,8 +345,6 @@ func (fd *netFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
        }
        if err == nil {
                n = len(p)
-       } else {
-               err = &OpError{"write", fd.net, fd.raddr, err}
        }
        return
 }
@@ -360,7 +355,7 @@ func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
        }
        defer fd.writeUnlock()
        if err := fd.pd.PrepareWrite(); err != nil {
-               return 0, 0, &OpError{"write", fd.net, fd.raddr, err}
+               return 0, 0, err
        }
        for {
                n, err = syscall.SendmsgN(fd.sysfd, p, oob, sa, 0)
@@ -373,8 +368,6 @@ func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
        }
        if err == nil {
                oobn = len(oob)
-       } else {
-               err = &OpError{"write", fd.net, fd.raddr, err}
        }
        return
 }
index 5bb048c1782a4663c9761cafaea981902ff4c828..e4038b90fa5194964c46fece2673407e606f71c0 100644 (file)
@@ -491,7 +491,7 @@ func (fd *netFD) readFrom(buf []byte) (int, syscall.Sockaddr, error) {
 
 func (fd *netFD) Write(buf []byte) (int, error) {
        if err := fd.writeLock(); err != nil {
-               return 0, &OpError{Op: "write", Net: fd.net, Addr: fd.raddr, Err: err}
+               return 0, err
        }
        defer fd.writeUnlock()
        if raceenabled {
@@ -502,9 +502,6 @@ func (fd *netFD) Write(buf []byte) (int, error) {
        n, err := wsrv.ExecIO(o, "WSASend", func(o *operation) error {
                return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &o.qty, 0, &o.o, nil)
        })
-       if err != nil {
-               err = &OpError{Op: "write", Net: fd.net, Addr: fd.raddr, Err: err}
-       }
        return n, err
 }
 
@@ -513,7 +510,7 @@ func (fd *netFD) writeTo(buf []byte, sa syscall.Sockaddr) (int, error) {
                return 0, nil
        }
        if err := fd.writeLock(); err != nil {
-               return 0, &OpError{Op: "write", Net: fd.net, Addr: fd.laddr, Err: err}
+               return 0, err
        }
        defer fd.writeUnlock()
        o := &fd.wop
@@ -522,9 +519,6 @@ func (fd *netFD) writeTo(buf []byte, sa syscall.Sockaddr) (int, error) {
        n, err := wsrv.ExecIO(o, "WSASendto", func(o *operation) error {
                return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &o.qty, 0, o.sa, &o.o, nil)
        })
-       if err != nil {
-               err = &OpError{Op: "write", Net: fd.net, Addr: fd.laddr, Err: err}
-       }
        return n, err
 }
 
@@ -627,5 +621,5 @@ func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S
 }
 
 func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
-       return 0, 0, &OpError{Op: "write", Net: fd.net, Addr: fd.laddr, Err: syscall.EWINDOWS}
+       return 0, 0, syscall.EWINDOWS
 }
index bdb0d9770f21be0a037d8f94045bc9a694792600..a3d86496066acbcb03a1d52cdc9f174d8a3b071a 100644 (file)
@@ -47,19 +47,19 @@ func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err
 // SetWriteDeadline.  On packet-oriented connections, write timeouts
 // are rare.
 func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) {
-       return 0, syscall.EPLAN9
+       return 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: syscall.EPLAN9}
 }
 
 // WriteTo implements the PacketConn WriteTo method.
 func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) {
-       return 0, syscall.EPLAN9
+       return 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: syscall.EPLAN9}
 }
 
 // WriteMsgIP writes a packet to addr via c, copying the payload from
 // b and the associated out-of-band data from oob.  It returns the
 // number of payload and out-of-band bytes written.
 func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error) {
-       return 0, 0, syscall.EPLAN9
+       return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: syscall.EPLAN9}
 }
 
 // DialIP connects to the remote address raddr on the network protocol
index 3c9d711b651d8861cfa875d262b1beea90992a69..26ef05ff570509b427057a02a9b9709b3cab7883 100644 (file)
@@ -154,9 +154,13 @@ func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) {
        }
        sa, err := addr.sockaddr(c.fd.family)
        if err != nil {
-               return 0, &OpError{"write", c.fd.net, addr, err}
+               return 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: err}
        }
-       return c.fd.writeTo(b, sa)
+       n, err := c.fd.writeTo(b, sa)
+       if err != nil {
+               err = &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: err}
+       }
+       return n, err
 }
 
 // WriteTo implements the PacketConn WriteTo method.
@@ -166,7 +170,7 @@ func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) {
        }
        a, ok := addr.(*IPAddr)
        if !ok {
-               return 0, &OpError{"write", c.fd.net, addr, syscall.EINVAL}
+               return 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: syscall.EINVAL}
        }
        return c.WriteToIP(b, a)
 }
@@ -184,11 +188,16 @@ func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error
        if addr == nil {
                return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
        }
-       sa, err := addr.sockaddr(c.fd.family)
+       var sa syscall.Sockaddr
+       sa, err = addr.sockaddr(c.fd.family)
        if err != nil {
-               return 0, 0, &OpError{"write", c.fd.net, addr, err}
+               return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: err}
        }
-       return c.fd.writeMsg(b, oob, sa)
+       n, oobn, err = c.fd.writeMsg(b, oob, sa)
+       if err != nil {
+               err = &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: err}
+       }
+       return
 }
 
 // DialIP connects to the remote address raddr on the network protocol
index 252a225f263d554ca5d8efe4c213b3317d416196..38375cc8a0c9be0def91395897b19488ca660352 100644 (file)
@@ -138,7 +138,16 @@ func (c *conn) Write(b []byte) (int, error) {
        if !c.ok() {
                return 0, syscall.EINVAL
        }
-       return c.fd.Write(b)
+       n, err := c.fd.Write(b)
+       if err != nil {
+               err = &OpError{Op: "write", 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 n, err
 }
 
 // Close closes the connection.
index 7ccfef0cc5debe10a48e3b0d2e664ef750be521e..fd5658ae5fc597b3d17971f299af9fc1c7146e0f 100644 (file)
@@ -211,6 +211,9 @@ func TestWriteTimeout(t *testing.T) {
        writeUntilTimeout := func() {
                for {
                        _, err := c.Write(buf)
+                       if perr := parseWriteError(err); perr != nil {
+                               t.Error(perr)
+                       }
                        if err != nil {
                                if isTimeoutError(err) {
                                        return
@@ -241,15 +244,9 @@ func TestWriteTimeout(t *testing.T) {
        default:
        }
        c.Close()
-       switch nerr := <-errc; err := nerr.(type) {
-       case *OpError:
-               if err.Err != errClosing {
-                       t.Fatalf("Write: expected err %v, got %v", errClosing, err)
-               }
-       default:
-               if err != errClosing {
-                       t.Fatalf("Write: expected err %v, got %v", errClosing, err)
-               }
+       err = <-errc
+       if perr := parseWriteError(err); perr != nil {
+               t.Error(perr)
        }
 }
 
@@ -676,6 +673,9 @@ func TestWriteDeadlineBufferAvailable(t *testing.T) {
        if res.n != 0 {
                t.Errorf("Write = %d; want 0", res.n)
        }
+       if perr := parseWriteError(res.err); perr != nil {
+               t.Error(perr)
+       }
        if !isTimeoutError(res.err) {
                t.Errorf("Write error = %v; want timeout", res.err)
        }
index ddaaa72093684a051d86fce8c732b7719398b4e5..4749dab8f1babcdf90ae69ad0a0d38734e13ea6d 100644 (file)
@@ -86,7 +86,11 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
        buf := make([]byte, udpHeaderSize+len(b))
        i := copy(buf, h.Bytes())
        copy(buf[i:], b)
-       return c.fd.data.Write(buf)
+       n, err := c.fd.data.Write(buf)
+       if err != nil {
+               err = &OpError{Op: "write", Net: c.fd.dir, Addr: addr, Err: err}
+       }
+       return n, err
 }
 
 // WriteTo implements the PacketConn WriteTo method.
@@ -96,7 +100,7 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
        }
        a, ok := addr.(*UDPAddr)
        if !ok {
-               return 0, &OpError{"write", c.fd.dir, addr, syscall.EINVAL}
+               return 0, &OpError{Op: "write", Net: c.fd.dir, Addr: addr, Err: syscall.EINVAL}
        }
        return c.WriteToUDP(b, a)
 }
@@ -107,7 +111,7 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
 // out-of-band data is copied from oob.  It returns the number of
 // payload and out-of-band bytes written.
 func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) {
-       return 0, 0, syscall.EPLAN9
+       return 0, 0, &OpError{Op: "write", Net: c.fd.dir, Addr: addr, Err: syscall.EPLAN9}
 }
 
 // DialUDP connects to the remote address raddr on the network net,
index 05a11032ce36c8b7f2763d6b485a600b2634402c..c4bd0bc06a218cc9277cac5c12d961184b4d863a 100644 (file)
@@ -118,16 +118,20 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
                return 0, syscall.EINVAL
        }
        if c.fd.isConnected {
-               return 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected}
+               return 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: ErrWriteToConnected}
        }
        if addr == nil {
                return 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
        }
        sa, err := addr.sockaddr(c.fd.family)
        if err != nil {
-               return 0, &OpError{"write", c.fd.net, addr, err}
+               return 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: err}
        }
-       return c.fd.writeTo(b, sa)
+       n, err := c.fd.writeTo(b, sa)
+       if err != nil {
+               err = &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: err}
+       }
+       return n, err
 }
 
 // WriteTo implements the PacketConn WriteTo method.
@@ -137,7 +141,7 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
        }
        a, ok := addr.(*UDPAddr)
        if !ok {
-               return 0, &OpError{"write", c.fd.net, addr, syscall.EINVAL}
+               return 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: syscall.EINVAL}
        }
        return c.WriteToUDP(b, a)
 }
@@ -152,16 +156,21 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er
                return 0, 0, syscall.EINVAL
        }
        if c.fd.isConnected && addr != nil {
-               return 0, 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected}
+               return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: ErrWriteToConnected}
        }
        if !c.fd.isConnected && addr == nil {
                return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
        }
-       sa, err := addr.sockaddr(c.fd.family)
+       var sa syscall.Sockaddr
+       sa, err = addr.sockaddr(c.fd.family)
        if err != nil {
-               return 0, 0, &OpError{"write", c.fd.net, addr, err}
+               return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: err}
        }
-       return c.fd.writeMsg(b, oob, sa)
+       n, oobn, err = c.fd.writeMsg(b, oob, sa)
+       if err != nil {
+               err = &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: err}
+       }
+       return
 }
 
 // DialUDP connects to the remote address raddr on the network net,
index a122a3dcbeed7999709a5d1ebb2506b72b8b62b6..bb8c4dd609c460419d63d91ddf6f28e919610f30 100644 (file)
@@ -47,19 +47,19 @@ func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAdd
 // SetWriteDeadline.  On packet-oriented connections, write timeouts
 // are rare.
 func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (int, error) {
-       return 0, syscall.EPLAN9
+       return 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: syscall.EPLAN9}
 }
 
 // WriteTo implements the PacketConn WriteTo method.
 func (c *UnixConn) WriteTo(b []byte, addr Addr) (int, error) {
-       return 0, syscall.EPLAN9
+       return 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: syscall.EPLAN9}
 }
 
 // WriteMsgUnix writes a packet to addr via c, copying the payload
 // from b and the associated out-of-band data from oob.  It returns
 // the number of payload and out-of-band bytes written.
 func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
-       return 0, 0, syscall.EPLAN9
+       return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: syscall.EPLAN9}
 }
 
 // CloseRead shuts down the reading side of the Unix domain connection.
index 07b6134b48bcafb4081f46f362635fd4d850c212..d51599f3bbaa09c6eb5620c2205e784738ba941d 100644 (file)
@@ -170,7 +170,7 @@ func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAdd
 // Timeout() == true after a fixed time limit; see SetDeadline and
 // SetWriteDeadline.  On packet-oriented connections, write timeouts
 // are rare.
-func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) {
+func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (int, error) {
        if !c.ok() {
                return 0, syscall.EINVAL
        }
@@ -181,10 +181,14 @@ func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) {
                return 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
        }
        if addr.Net != sotypeToNet(c.fd.sotype) {
-               return 0, syscall.EAFNOSUPPORT
+               return 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: syscall.EAFNOSUPPORT}
        }
        sa := &syscall.SockaddrUnix{Name: addr.Name}
-       return c.fd.writeTo(b, sa)
+       n, err := c.fd.writeTo(b, sa)
+       if err != nil {
+               err = &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: err}
+       }
+       return n, err
 }
 
 // WriteTo implements the PacketConn WriteTo method.
@@ -194,7 +198,7 @@ func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) {
        }
        a, ok := addr.(*UnixAddr)
        if !ok {
-               return 0, &OpError{"write", c.fd.net, addr, syscall.EINVAL}
+               return 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: syscall.EINVAL}
        }
        return c.WriteToUnix(b, a)
 }
@@ -209,14 +213,18 @@ func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err
        if c.fd.sotype == syscall.SOCK_DGRAM && c.fd.isConnected {
                return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: ErrWriteToConnected}
        }
+       var sa syscall.Sockaddr
        if addr != nil {
                if addr.Net != sotypeToNet(c.fd.sotype) {
-                       return 0, 0, syscall.EAFNOSUPPORT
+                       return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: syscall.EAFNOSUPPORT}
                }
-               sa := &syscall.SockaddrUnix{Name: addr.Name}
-               return c.fd.writeMsg(b, oob, sa)
+               sa = &syscall.SockaddrUnix{Name: addr.Name}
        }
-       return c.fd.writeMsg(b, oob, nil)
+       n, oobn, err = c.fd.writeMsg(b, oob, sa)
+       if err != nil {
+               err = &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: err}
+       }
+       return
 }
 
 // CloseRead shuts down the reading side of the Unix domain connection.