Skip to content

Commit 5bcdd63

Browse files
committed
net: don't return io.EOF from zero byte reads
Updates #15735 Change-Id: I42ab2345443bbaeaf935d683460fc2c941b7679c Reviewed-on: https://go-review.googlesource.com/23227 Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent c08436d commit 5bcdd63

File tree

3 files changed

+65
-1
lines changed

3 files changed

+65
-1
lines changed

src/net/fd_unix.go

+8
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,14 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
201201
return 0, err
202202
}
203203
defer fd.readUnlock()
204+
if len(p) == 0 {
205+
// If the caller wanted a zero byte read, return immediately
206+
// without trying. (But after acquiring the readLock.) Otherwise
207+
// syscall.Read returns 0, nil and eofError turns that into
208+
// io.EOF.
209+
// TODO(bradfitz): make it wait for readability? (Issue 15735)
210+
return 0, nil
211+
}
204212
if err := fd.pd.prepareRead(); err != nil {
205213
return 0, err
206214
}

src/net/fd_windows.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,9 @@ func (fd *netFD) Read(buf []byte) (int, error) {
427427
if race.Enabled {
428428
race.Acquire(unsafe.Pointer(&ioSync))
429429
}
430-
err = fd.eofError(n, err)
430+
if len(buf) != 0 {
431+
err = fd.eofError(n, err)
432+
}
431433
if _, ok := err.(syscall.Errno); ok {
432434
err = os.NewSyscallError("wsarecv", err)
433435
}

src/net/net_test.go

+54
Original file line numberDiff line numberDiff line change
@@ -360,3 +360,57 @@ func TestAcceptIgnoreAbortedConnRequest(t *testing.T) {
360360
t.Error(err)
361361
}
362362
}
363+
364+
func TestZeroByteRead(t *testing.T) {
365+
for _, network := range []string{"tcp", "unix", "unixpacket"} {
366+
if !testableNetwork(network) {
367+
t.Logf("skipping %s test", network)
368+
continue
369+
}
370+
371+
ln, err := newLocalListener(network)
372+
if err != nil {
373+
t.Fatal(err)
374+
}
375+
connc := make(chan Conn, 1)
376+
go func() {
377+
defer ln.Close()
378+
c, err := ln.Accept()
379+
if err != nil {
380+
t.Error(err)
381+
}
382+
connc <- c // might be nil
383+
}()
384+
c, err := Dial(network, ln.Addr().String())
385+
if err != nil {
386+
t.Fatal(err)
387+
}
388+
defer c.Close()
389+
sc := <-connc
390+
if sc == nil {
391+
continue
392+
}
393+
defer sc.Close()
394+
395+
if runtime.GOOS == "windows" {
396+
// A zero byte read on Windows caused a wait for readability first.
397+
// Rather than change that behavior, satisfy it in this test.
398+
// See Issue 15735.
399+
go io.WriteString(sc, "a")
400+
}
401+
402+
n, err := c.Read(nil)
403+
if n != 0 || err != nil {
404+
t.Errorf("%s: zero byte client read = %v, %v; want 0, nil", network, n, err)
405+
}
406+
407+
if runtime.GOOS == "windows" {
408+
// Same as comment above.
409+
go io.WriteString(c, "a")
410+
}
411+
n, err = sc.Read(nil)
412+
if n != 0 || err != nil {
413+
t.Errorf("%s: zero byte server read = %v, %v; want 0, nil", network, n, err)
414+
}
415+
}
416+
}

0 commit comments

Comments
 (0)