From 26d88f7511c97601bb0d6780eaa8b9c1c8ec0e5f Mon Sep 17 00:00:00 2001 From: Leonid Bugaev Date: Sat, 27 Jun 2026 08:45:28 +0000 Subject: [PATCH] transport: return unexpected EOF for partial headers --- internal/transport/transport.go | 4 +++- internal/transport/transport_test.go | 30 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/internal/transport/transport.go b/internal/transport/transport.go index d2e49538f0b0..62bd1a776b20 100644 --- a/internal/transport/transport.go +++ b/internal/transport/transport.go @@ -368,14 +368,16 @@ func (s *Stream) ReadMessageHeader(header []byte) (err error) { return er } s.readRequester.requestRead(len(header)) + bytesRead := 0 for len(header) != 0 { n, err := s.trReader.ReadMessageHeader(header) + bytesRead += n header = header[n:] if len(header) == 0 { err = nil } if err != nil { - if n > 0 && err == io.EOF { + if bytesRead > 0 && err == io.EOF { err = io.ErrUnexpectedEOF } return err diff --git a/internal/transport/transport_test.go b/internal/transport/transport_test.go index 5ecca56a3a80..fedece5133c4 100644 --- a/internal/transport/transport_test.go +++ b/internal/transport/transport_test.go @@ -3387,6 +3387,36 @@ func (s) TestReadMessageHeaderMultipleBuffers(t *testing.T) { } } +func (s) TestReadMessageHeaderPartialHeaderEOF(t *testing.T) { + const headerLen = 5 + bytesRead := 0 + s := Stream{ + readRequester: &fakeReadRequester{}, + } + s.buf.init() + recvBuffer := &s.buf + s.trReader = transportReader{ + reader: recvBufferReader{ + recv: recvBuffer, + }, + windowHandler: &mockWindowUpdater{ + f: func(i int) { + bytesRead += i + }, + }, + } + + recvBuffer.put(recvMsg{buffer: make(mem.SliceBuffer, 3)}) + recvBuffer.put(recvMsg{err: io.EOF}) + + if err := s.ReadMessageHeader(make([]byte, headerLen)); !errors.Is(err, io.ErrUnexpectedEOF) { + t.Fatalf("ReadMessageHeader() error = %v, want %v", err, io.ErrUnexpectedEOF) + } + if bytesRead != 3 { + t.Fatalf("bytesRead = %d, want 3", bytesRead) + } +} + // Tests a scenario when the client doesn't send an RST frame when the // configured deadline is reached. The test verifies that the server sends an // RST stream only after the deadline is reached.