diff options
| -rw-r--r-- | doc/next/78137.md | 2 | ||||
| -rw-r--r-- | src/net/unixsock.go | 7 | ||||
| -rw-r--r-- | src/net/unixsock_test.go | 95 |
3 files changed, 101 insertions, 3 deletions
diff --git a/doc/next/78137.md b/doc/next/78137.md new file mode 100644 index 0000000000..ecb3a55e99 --- /dev/null +++ b/doc/next/78137.md @@ -0,0 +1,2 @@ +net: UnixConn read methods now return io.EOF directly instead of
+wrapping it in net.OpError when the underlying read returns EOF.
\ No newline at end of file diff --git a/src/net/unixsock.go b/src/net/unixsock.go index 0ee79f35de..47ab31fd7f 100644 --- a/src/net/unixsock.go +++ b/src/net/unixsock.go @@ -6,6 +6,7 @@ package net import ( "context" + "io" "os" "sync" "syscall" @@ -108,7 +109,7 @@ func (c *UnixConn) ReadFromUnix(b []byte) (int, *UnixAddr, error) { return 0, nil, syscall.EINVAL } n, addr, err := c.readFrom(b) - if err != nil { + if err != nil && err != io.EOF { err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} } return n, addr, err @@ -120,7 +121,7 @@ func (c *UnixConn) ReadFrom(b []byte) (int, Addr, error) { return 0, nil, syscall.EINVAL } n, addr, err := c.readFrom(b) - if err != nil { + if err != nil && err != io.EOF { err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} } if addr == nil { @@ -141,7 +142,7 @@ func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAdd return 0, 0, 0, nil, syscall.EINVAL } n, oobn, flags, addr, err = c.readMsg(b, oob) - if err != nil { + if err != nil && err != io.EOF { err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} } return diff --git a/src/net/unixsock_test.go b/src/net/unixsock_test.go index 3e4a14b255..9c2d309153 100644 --- a/src/net/unixsock_test.go +++ b/src/net/unixsock_test.go @@ -9,7 +9,9 @@ package net import ( "bytes" "internal/testenv" + "io" "os" + "path/filepath" "reflect" "runtime" "syscall" @@ -476,3 +478,96 @@ func TestUnixUnlink(t *testing.T) { l.Close() }) } + +// Ensure UnixConn read methods return io.EOF directly instead of wrapping it +// in net.OpError, per the io.Reader contract. See issue #78137. +func TestUnixConnReadEOF(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("unix sockets not reliable on windows") + } + if !testableNetwork("unix") { + t.Skip("unix test") + } + dir := t.TempDir() + addr := &UnixAddr{ + Name: filepath.Join(dir, "sock"), + Net: "unix", + } + + listen := func(t *testing.T) *UnixListener { + ln, err := ListenUnix("unix", addr) + if err != nil { + t.Fatal(err) + } + return ln + } + + startServer := func(t *testing.T, ln *UnixListener) { + go func() { + srv, err := ln.AcceptUnix() + if err != nil { + t.Error(err) + return + } + srv.Close() + }() + } + + dial := func(t *testing.T) *UnixConn { + cl, err := DialUnix("unix", nil, addr) + if err != nil { + t.Fatal(err) + } + return cl + } + + // Test ReadMsgUnix + t.Run("ReadMsgUnix", func(t *testing.T) { + ln := listen(t) + defer ln.Close() + + startServer(t, ln) + + cl := dial(t) + defer cl.Close() + + _, _, _, _, err := cl.ReadMsgUnix(make([]byte, 1), nil) + if err != io.EOF { + t.Fatalf("ReadMsgUnix returned %v, want io.EOF", err) + } + }) + + // Test ReadFromUnix + t.Run("ReadFromUnix", func(t *testing.T) { + ln := listen(t) + defer ln.Close() + + startServer(t, ln) + + cl := dial(t) + defer cl.Close() + + buf := make([]byte, 1) + _, _, err := cl.ReadFromUnix(buf) + if err != io.EOF { + t.Fatalf("ReadFromUnix returned %v, want io.EOF", err) + } + }) + + // Test ReadFrom + t.Run("ReadFrom", func(t *testing.T) { + ln := listen(t) + defer ln.Close() + + startServer(t, ln) + + cl := dial(t) + defer cl.Close() + + buf := make([]byte, 1) + _, _, err := cl.ReadFrom(buf) + if err != io.EOF { + t.Fatalf("ReadFrom returned %v, want io.EOF", err) + } + }) +} |
