Skip to content

Commit f6cd82c

Browse files
bugerPranjali-2501
authored andcommitted
transport: return unexpected EOF for partial reads (grpc#9204)
Fixes grpc#9200 Track cumulative bytes read while filling a gRPC message header or body. If EOF occurs after any partial progress, return `io.ErrUnexpectedEOF` even when the final read call itself returned zero bytes. Preserve plain `io.EOF` when EOF occurs before any data is read. Regression tests split partial headers and bodies from EOF across separate receive-buffer messages. They verify that partial reads return `io.ErrUnexpectedEOF` while zero-progress reads retain `io.EOF`. RELEASE NOTES: * transport: Truncated gRPC message headers and bodies now return `io.ErrUnexpectedEOF` after partial data is received. (cherry picked from commit a9cbf17)
1 parent d5a4111 commit f6cd82c

2 files changed

Lines changed: 107 additions & 2 deletions

File tree

internal/transport/transport.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -466,15 +466,18 @@ func (s *Stream) ReadMessageHeader(header []byte) (err error) {
466466
return er
467467
}
468468
s.readRequester.requestRead(len(header))
469+
bytesRead := 0
469470
for len(header) != 0 {
470471
n, err := s.trReader.ReadMessageHeader(header)
472+
bytesRead += n
471473
header = header[n:]
472474
if len(header) == 0 {
473475
err = nil
474476
}
475477
if err != nil {
476-
if n > 0 && err == io.EOF {
478+
if bytesRead > 0 && err == io.EOF {
477479
err = io.ErrUnexpectedEOF
480+
s.trReader.er = err
478481
}
479482
return err
480483
}
@@ -505,19 +508,22 @@ func (s *Stream) read(n int) (data mem.BufferSlice, err error) {
505508
allocCap := min(ceil(n, http2MaxFrameLen), 128)
506509
data = make(mem.BufferSlice, 0, allocCap)
507510
s.readRequester.requestRead(n)
511+
bytesRead := 0
508512
for n != 0 {
509513
buf, err := s.trReader.Read(n)
510514
var bufLen int
511515
if buf != nil {
512516
bufLen = buf.Len()
513517
}
518+
bytesRead += bufLen
514519
n -= bufLen
515520
if n == 0 {
516521
err = nil
517522
}
518523
if err != nil {
519-
if bufLen > 0 && err == io.EOF {
524+
if bytesRead > 0 && err == io.EOF {
520525
err = io.ErrUnexpectedEOF
526+
s.trReader.er = err
521527
}
522528
data.Free()
523529
return nil, err

internal/transport/transport_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3462,6 +3462,105 @@ func (s) TestReadMessageHeaderMultipleBuffers(t *testing.T) {
34623462
}
34633463
}
34643464

3465+
func (s) TestReadMessageHeaderPartialHeaderEOF(t *testing.T) {
3466+
const headerLen = 5
3467+
stream := Stream{
3468+
readRequester: &fakeReadRequester{},
3469+
}
3470+
stream.buf.init(mem.DefaultBufferPool())
3471+
recvBuffer := &stream.buf
3472+
stream.trReader = transportReader{
3473+
reader: recvBufferReader{
3474+
recv: recvBuffer,
3475+
},
3476+
windowHandler: &mockWindowUpdater{f: func(int) {}},
3477+
}
3478+
3479+
recvBuffer.put(recvMsg{buffer: make(mem.SliceBuffer, 3)})
3480+
recvBuffer.put(recvMsg{err: io.EOF})
3481+
3482+
if err := stream.ReadMessageHeader(make([]byte, headerLen)); !errors.Is(err, io.ErrUnexpectedEOF) {
3483+
t.Fatalf("ReadMessageHeader() error = %v, want %v", err, io.ErrUnexpectedEOF)
3484+
}
3485+
if err := stream.ReadMessageHeader(make([]byte, headerLen)); !errors.Is(err, io.ErrUnexpectedEOF) {
3486+
t.Fatalf("second ReadMessageHeader() error = %v, want %v", err, io.ErrUnexpectedEOF)
3487+
}
3488+
}
3489+
3490+
func (s) TestReadMessageHeaderEOF(t *testing.T) {
3491+
stream := Stream{
3492+
readRequester: &fakeReadRequester{},
3493+
}
3494+
stream.buf.init(mem.DefaultBufferPool())
3495+
recvBuffer := &stream.buf
3496+
stream.trReader = transportReader{
3497+
reader: recvBufferReader{
3498+
recv: recvBuffer,
3499+
},
3500+
windowHandler: &mockWindowUpdater{},
3501+
}
3502+
3503+
recvBuffer.put(recvMsg{err: io.EOF})
3504+
3505+
err := stream.ReadMessageHeader(make([]byte, 5))
3506+
if !errors.Is(err, io.EOF) {
3507+
t.Fatalf("ReadMessageHeader() error = %v, want %v", err, io.EOF)
3508+
}
3509+
if errors.Is(err, io.ErrUnexpectedEOF) {
3510+
t.Fatalf("ReadMessageHeader() error = %v, want not %v", err, io.ErrUnexpectedEOF)
3511+
}
3512+
}
3513+
3514+
func (s) TestReadPartialMessageEOF(t *testing.T) {
3515+
const messageLen = 5
3516+
stream := Stream{
3517+
readRequester: &fakeReadRequester{},
3518+
}
3519+
stream.buf.init(mem.DefaultBufferPool())
3520+
recvBuffer := &stream.buf
3521+
stream.trReader = transportReader{
3522+
reader: recvBufferReader{
3523+
recv: recvBuffer,
3524+
},
3525+
windowHandler: &mockWindowUpdater{f: func(int) {}},
3526+
}
3527+
3528+
recvBuffer.put(recvMsg{buffer: make(mem.SliceBuffer, 3)})
3529+
recvBuffer.put(recvMsg{err: io.EOF})
3530+
3531+
if _, err := stream.read(messageLen); !errors.Is(err, io.ErrUnexpectedEOF) {
3532+
t.Fatalf("read(%d) error = %v, want %v", messageLen, err, io.ErrUnexpectedEOF)
3533+
}
3534+
if _, err := stream.read(messageLen); !errors.Is(err, io.ErrUnexpectedEOF) {
3535+
t.Fatalf("second read(%d) error = %v, want %v", messageLen, err, io.ErrUnexpectedEOF)
3536+
}
3537+
}
3538+
3539+
func (s) TestReadMessageEOF(t *testing.T) {
3540+
const messageLen = 5
3541+
stream := Stream{
3542+
readRequester: &fakeReadRequester{},
3543+
}
3544+
stream.buf.init(mem.DefaultBufferPool())
3545+
recvBuffer := &stream.buf
3546+
stream.trReader = transportReader{
3547+
reader: recvBufferReader{
3548+
recv: recvBuffer,
3549+
},
3550+
windowHandler: &mockWindowUpdater{f: func(int) {}},
3551+
}
3552+
3553+
recvBuffer.put(recvMsg{err: io.EOF})
3554+
3555+
_, err := stream.read(messageLen)
3556+
if !errors.Is(err, io.EOF) {
3557+
t.Fatalf("read(%d) error = %v, want %v", messageLen, err, io.EOF)
3558+
}
3559+
if errors.Is(err, io.ErrUnexpectedEOF) {
3560+
t.Fatalf("read(%d) error = %v, want not %v", messageLen, err, io.ErrUnexpectedEOF)
3561+
}
3562+
}
3563+
34653564
// Tests a scenario when the client doesn't send an RST frame when the
34663565
// configured deadline is reached. The test verifies that the server sends an
34673566
// RST stream only after the deadline is reached.

0 commit comments

Comments
 (0)