2
0
mirror of https://github.com/soheilhy/cmux.git synced 2024-09-21 03:16:43 +08:00

Fix a blocking issue in buffer reader

After sniffing and buffering data, if we try to read from
the socket again, bufio.Reader may block. This breaks HTTP handlers
in go1.5.2+ if one tries on browsers or with curl. Go's HTTP client,
however, is not broken. This issue is also there with TeeReader.

Return immediately with the data in the sniffed buffer.
This commit is contained in:
Soheil Hassas Yeganeh 2016-04-24 12:55:13 -04:00
parent 59b6f01712
commit d5924ef0b4
2 changed files with 22 additions and 9 deletions

View File

@ -16,23 +16,30 @@ type bufferedReader struct {
bufferRead int
bufferSize int
sniffing bool
lastErr error
}
func (s *bufferedReader) Read(p []byte) (int, error) {
// Functionality of bytes.Reader.
bn := copy(p, s.buffer.Bytes()[s.bufferRead:s.bufferSize])
s.bufferRead += bn
if s.bufferSize > s.bufferRead {
// If we have already read something from the buffer before, we return the
// same data and the last error if any. We need to immediately return,
// otherwise we may block for ever, if we try to be smart and call
// source.Read() seeking a little bit of more data.
bn := copy(p, s.buffer.Bytes()[s.bufferRead:s.bufferSize])
s.bufferRead += bn
return bn, s.lastErr
}
p = p[bn:]
// Funtionality of io.TeeReader.
// If there is nothing more to return in the sniffed buffer, read from the
// source.
sn, sErr := s.source.Read(p)
if sn > 0 && s.sniffing {
s.lastErr = sErr
if wn, wErr := s.buffer.Write(p[:sn]); wErr != nil {
return bn + wn, wErr
return wn, wErr
}
}
return bn + sn, sErr
return sn, sErr
}
func (s *bufferedReader) reset(snif bool) {

View File

@ -279,7 +279,13 @@ func TestHTTP2(t *testing.T) {
t.Fatal(err)
}
var b [len(http2.ClientPreface)]byte
if _, err := muxedConn.Read(b[:]); err != io.EOF {
var n int
// We have the sniffed buffer first...
if n, err = muxedConn.Read(b[:]); err == io.EOF {
t.Fatal(err)
}
// and then we read from the source.
if _, err = muxedConn.Read(b[n:]); err != io.EOF {
t.Fatal(err)
}
if string(b[:]) != http2.ClientPreface {