mirror of
https://github.com/soheilhy/cmux.git
synced 2025-01-19 03:06:07 +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:
parent
59b6f01712
commit
d5924ef0b4
23
buffer.go
23
buffer.go
@ -16,23 +16,30 @@ type bufferedReader struct {
|
|||||||
bufferRead int
|
bufferRead int
|
||||||
bufferSize int
|
bufferSize int
|
||||||
sniffing bool
|
sniffing bool
|
||||||
|
lastErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *bufferedReader) Read(p []byte) (int, error) {
|
func (s *bufferedReader) Read(p []byte) (int, error) {
|
||||||
// Functionality of bytes.Reader.
|
if s.bufferSize > s.bufferRead {
|
||||||
bn := copy(p, s.buffer.Bytes()[s.bufferRead:s.bufferSize])
|
// If we have already read something from the buffer before, we return the
|
||||||
s.bufferRead += bn
|
// 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:]
|
// If there is nothing more to return in the sniffed buffer, read from the
|
||||||
|
// source.
|
||||||
// Funtionality of io.TeeReader.
|
|
||||||
sn, sErr := s.source.Read(p)
|
sn, sErr := s.source.Read(p)
|
||||||
if sn > 0 && s.sniffing {
|
if sn > 0 && s.sniffing {
|
||||||
|
s.lastErr = sErr
|
||||||
if wn, wErr := s.buffer.Write(p[:sn]); wErr != nil {
|
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) {
|
func (s *bufferedReader) reset(snif bool) {
|
||||||
|
@ -279,7 +279,13 @@ func TestHTTP2(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
var b [len(http2.ClientPreface)]byte
|
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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if string(b[:]) != http2.ClientPreface {
|
if string(b[:]) != http2.ClientPreface {
|
||||||
|
Loading…
Reference in New Issue
Block a user