Skip to content

Commit

Permalink
net: don't return io.EOF from zero byte reads
Browse files Browse the repository at this point in the history
Updates #15735

Change-Id: I42ab2345443bbaeaf935d683460fc2c941b7679c
Reviewed-on: https://go-review.googlesource.com/23227
Reviewed-by: Ian Lance Taylor <iant@golang.org>
  • Loading branch information
bradfitz committed May 19, 2016
1 parent c08436d commit 5bcdd63
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 1 deletion.
8 changes: 8 additions & 0 deletions src/net/fd_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,14 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
return 0, err
}
defer fd.readUnlock()
if len(p) == 0 {
// If the caller wanted a zero byte read, return immediately
// without trying. (But after acquiring the readLock.) Otherwise
// syscall.Read returns 0, nil and eofError turns that into
// io.EOF.
// TODO(bradfitz): make it wait for readability? (Issue 15735)
return 0, nil
}
if err := fd.pd.prepareRead(); err != nil {
return 0, err
}
Expand Down
4 changes: 3 additions & 1 deletion src/net/fd_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,9 @@ func (fd *netFD) Read(buf []byte) (int, error) {
if race.Enabled {
race.Acquire(unsafe.Pointer(&ioSync))
}
err = fd.eofError(n, err)
if len(buf) != 0 {
err = fd.eofError(n, err)
}
if _, ok := err.(syscall.Errno); ok {
err = os.NewSyscallError("wsarecv", err)
}
Expand Down
54 changes: 54 additions & 0 deletions src/net/net_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,57 @@ func TestAcceptIgnoreAbortedConnRequest(t *testing.T) {
t.Error(err)
}
}

func TestZeroByteRead(t *testing.T) {
for _, network := range []string{"tcp", "unix", "unixpacket"} {
if !testableNetwork(network) {
t.Logf("skipping %s test", network)
continue
}

ln, err := newLocalListener(network)
if err != nil {
t.Fatal(err)
}
connc := make(chan Conn, 1)
go func() {
defer ln.Close()
c, err := ln.Accept()
if err != nil {
t.Error(err)
}
connc <- c // might be nil
}()
c, err := Dial(network, ln.Addr().String())
if err != nil {
t.Fatal(err)
}
defer c.Close()
sc := <-connc
if sc == nil {
continue
}
defer sc.Close()

if runtime.GOOS == "windows" {
// A zero byte read on Windows caused a wait for readability first.
// Rather than change that behavior, satisfy it in this test.
// See Issue 15735.
go io.WriteString(sc, "a")
}

n, err := c.Read(nil)
if n != 0 || err != nil {
t.Errorf("%s: zero byte client read = %v, %v; want 0, nil", network, n, err)
}

if runtime.GOOS == "windows" {
// Same as comment above.
go io.WriteString(c, "a")
}
n, err = sc.Read(nil)
if n != 0 || err != nil {
t.Errorf("%s: zero byte server read = %v, %v; want 0, nil", network, n, err)
}
}
}

0 comments on commit 5bcdd63

Please sign in to comment.