aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/next/78137.md2
-rw-r--r--src/net/unixsock.go7
-rw-r--r--src/net/unixsock_test.go95
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)
+ }
+ })
+}