Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion http2/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -2518,7 +2518,11 @@ func (b transportResponseBody) Close() error {
cs := b.cs
cc := cs.cc

serverSentStreamEnd := cs.bufPipe.Err() == io.EOF
pipeErr := cs.bufPipe.Err()
if pipeErr == errClosedResponseBody {
return nil
}
serverSentStreamEnd := pipeErr == io.EOF
unread := cs.bufPipe.Len()

if unread > 0 || !serverSentStreamEnd {
Expand Down
88 changes: 88 additions & 0 deletions http2/transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4088,6 +4088,94 @@ func TestTransportAllocationsAfterResponseBodyClose(t *testing.T) {
}
}

func TestTransportResponseBodyCloseIsIdempotent(t *testing.T) {
for _, test := range []struct {
name string
endStream bool
}{
{name: "unfinished"},
{name: "after EOF", endStream: true},
} {
t.Run(test.name, func(t *testing.T) {
ct := newClientTester(t)
ct.client = func() error {
req, err := http.NewRequest("GET", "https://dummy.tld/", nil)
if err != nil {
return err
}
res, err := ct.tr.RoundTrip(req)
if err != nil {
return err
}
if test.endStream {
if _, err := io.ReadAll(res.Body); err != nil {
return err
}
}
if err := res.Body.Close(); err != nil {
return err
}
return res.Body.Close()
}
ct.server = func() error {
ct.greet()
hf, err := ct.firstHeaders()
if err != nil {
return err
}

var buf bytes.Buffer
enc := hpack.NewEncoder(&buf)
enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
if err := ct.fr.WriteHeaders(HeadersFrameParam{
StreamID: hf.StreamID,
EndHeaders: true,
BlockFragment: buf.Bytes(),
}); err != nil {
return err
}
if test.endStream {
if err := ct.fr.WriteData(hf.StreamID, true, []byte("body")); err != nil {
return err
}
}

ct.sc.SetReadDeadline(time.Now().Add(500 * time.Millisecond))
resets := 0
for {
f, err := ct.fr.ReadFrame()
if err != nil {
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
break
}
return fmt.Errorf("reading client frames: %v", err)
}
switch f := f.(type) {
case *SettingsFrame, *WindowUpdateFrame:
continue
case *RSTStreamFrame:
if f.StreamID != hf.StreamID || f.ErrCode != ErrCodeCancel {
return fmt.Errorf("unexpected RST_STREAM: %v", summarizeFrame(f))
}
resets++
default:
return fmt.Errorf("got unexpected client frame %T", f)
}
}
wantResets := 1
if test.endStream {
wantResets = 0
}
if resets != wantResets {
return fmt.Errorf("received %d RST_STREAM frames; want %d", resets, wantResets)
}
return nil
}
ct.run()
})
}
}

// Issue 18891: make sure Request.Body == NoBody means no DATA frame
// is ever sent, even if empty.
func TestTransportNoBodyMeansNoDATA(t *testing.T) {
Expand Down