Skip to content

Commit

Permalink
Merge pull request redis#1524 from eelcocramer/bufio-fix
Browse files Browse the repository at this point in the history
Fixes bufio buffer full error when reading from an array larger than 4096 bytes
  • Loading branch information
vmihailenco authored Oct 10, 2020
2 parents bdeca5f + 39571cc commit b66d05b
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 8 deletions.
30 changes: 22 additions & 8 deletions internal/proto/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,29 @@ func (r *Reader) ReadLine() ([]byte, error) {
// - there is a pending read error;
// - or line does not end with \r\n.
func (r *Reader) readLine() ([]byte, error) {
b, err := r.rd.ReadSlice('\n')
if err != nil {
return nil, err
}
if len(b) <= 2 || b[len(b)-1] != '\n' || b[len(b)-2] != '\r' {
return nil, fmt.Errorf("redis: invalid reply: %q", b)
var s []byte
multi := false
for {
b, err := r.rd.ReadSlice('\n')
if err != nil {
// in case the end of the buffer is not reached
if err == bufio.ErrBufferFull {
s = append(s, b...)
multi = true
continue
} else {
return nil, err
}
}
if len(b) <= 2 || b[len(b)-1] != '\n' || b[len(b)-2] != '\r' {
return nil, fmt.Errorf("redis: invalid reply: %q", b)
}
if multi {
b = append(s, b...)
}
b = b[:len(b)-2]
return b, nil
}
b = b[:len(b)-2]
return b, nil
}

func (r *Reader) ReadReply(m MultiBulkParse) (interface{}, error) {
Expand Down
16 changes: 16 additions & 0 deletions internal/proto/reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package proto_test

import (
"bytes"
"io"
"testing"

"github.com/go-redis/redis/v8/internal/proto"
Expand All @@ -27,6 +28,21 @@ func BenchmarkReader_ParseReply_Slice(b *testing.B) {
benchmarkParseReply(b, "*2\r\n$5\r\nhello\r\n$5\r\nworld\r\n", multiBulkParse, false)
}

func TestReader_ReadLine(t *testing.T) {
original := bytes.Repeat([]byte("a"), 8192)
original[len(original)-2] = '\r'
original[len(original)-1] = '\n'
r := proto.NewReader(bytes.NewReader(original))
read, err := r.ReadLine()
if err != nil && err != io.EOF {
t.Errorf("Should be able to read the full buffer: %v", err)
}

if bytes.Compare(read, original[:len(original)-2]) != 0 {
t.Errorf("Values must be equal: %d expected %d", len(read), len(original[:len(original)-2]))
}
}

func benchmarkParseReply(b *testing.B, reply string, m proto.MultiBulkParse, wanterr bool) {
buf := new(bytes.Buffer)
for i := 0; i < b.N; i++ {
Expand Down

0 comments on commit b66d05b

Please sign in to comment.