aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Cormier <danielc@knowbe4.com>2019-06-19 12:32:30 -0400
committerRobert Griesemer <gri@golang.org>2019-06-20 20:04:54 +0000
commit3ed5a53f9d1c9713c7d2748f7744534e404b80de (patch)
tree911391b941514329b8199ea90a3854082ca0fccf
parent94aa15543208c6f2e914539bfeddc3dd1e62a32b (diff)
downloadgo-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.go3
-rw-r--r--src/bufio/bufio_test.go54
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))