diff options
author | Daniel Cormier <danielc@knowbe4.com> | 2019-06-19 12:32:30 -0400 |
---|---|---|
committer | Robert Griesemer <gri@golang.org> | 2019-06-20 20:04:54 +0000 |
commit | 3ed5a53f9d1c9713c7d2748f7744534e404b80de (patch) | |
tree | 911391b941514329b8199ea90a3854082ca0fccf | |
parent | 94aa15543208c6f2e914539bfeddc3dd1e62a32b (diff) | |
download | go-3ed5a53f9d1c9713c7d2748f7744534e404b80de.tar.gz go-3ed5a53f9d1c9713c7d2748f7744534e404b80de.zip |
bufio: Reader.Read may return io.EOF even when it has data buffered
If reading 0 bytes, don't return the error from the underlying
io.Reader if there is still data buffered.
Fixes #32693
Change-Id: I12a97bd6003c638c15d41028942f27edf88340e2
Reviewed-on: https://go-review.googlesource.com/c/go/+/182997
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
-rw-r--r-- | src/bufio/bufio.go | 3 | ||||
-rw-r--r-- | src/bufio/bufio_test.go | 54 |
2 files changed, 57 insertions, 0 deletions
diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go index 0125d729d1..0f05d3b322 100644 --- a/src/bufio/bufio.go +++ b/src/bufio/bufio.go @@ -197,6 +197,9 @@ func (b *Reader) Discard(n int) (discarded int, err error) { func (b *Reader) Read(p []byte) (n int, err error) { n = len(p) if n == 0 { + if b.Buffered() > 0 { + return 0, nil + } return 0, b.readErr() } if b.r == b.w { diff --git a/src/bufio/bufio_test.go b/src/bufio/bufio_test.go index f7a0682e70..782ca2149a 100644 --- a/src/bufio/bufio_test.go +++ b/src/bufio/bufio_test.go @@ -1481,6 +1481,60 @@ func newScriptedReader(steps ...func(p []byte) (n int, err error)) io.Reader { return &sr } +// eofReader returns the number of bytes read and io.EOF for the read that consumes the last of the content. +type eofReader struct { + buf []byte +} + +func (r *eofReader) Read(p []byte) (int, error) { + read := copy(p, r.buf) + r.buf = r.buf[read:] + + switch read { + case 0, len(r.buf): + // As allowed in the documentation, this will return io.EOF + // in the same call that consumes the last of the data. + // https://godoc.org/io#Reader + return read, io.EOF + } + + return read, nil +} + +func TestPartialReadEOF(t *testing.T) { + src := make([]byte, 10) + eofR := &eofReader{buf: src} + r := NewReader(eofR) + + // Start by reading 5 of the 10 available bytes. + dest := make([]byte, 5) + read, err := r.Read(dest) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if n := len(dest); read != n { + t.Fatalf("read %d bytes; wanted %d bytes", read, n) + } + + // The Reader should have buffered all the content from the io.Reader. + if n := len(eofR.buf); n != 0 { + t.Fatalf("got %d bytes left in bufio.Reader source; want 0 bytes", n) + } + // To prove the point, check that there are still 5 bytes available to read. + if n := r.Buffered(); n != 5 { + t.Fatalf("got %d bytes buffered in bufio.Reader; want 5 bytes", n) + } + + // This is the second read of 0 bytes. + read, err = r.Read([]byte{}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if read != 0 { + t.Fatalf("read %d bytes; want 0 bytes", read) + } +} + func BenchmarkReaderCopyOptimal(b *testing.B) { // Optimal case is where the underlying reader implements io.WriterTo srcBuf := bytes.NewBuffer(make([]byte, 8192)) |