http3: allow concurrent calls to Body.Close (#4798)

This commit is contained in:
RPRX
2024-12-28 02:52:17 +00:00
committed by GitHub
parent f6e7789854
commit 3552381374
2 changed files with 18 additions and 7 deletions

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"io" "io"
"sync"
"github.com/quic-go/quic-go" "github.com/quic-go/quic-go"
) )
@@ -96,7 +97,7 @@ type hijackableBody struct {
// The channel is closed when the user is done with this response: // The channel is closed when the user is done with this response:
// either when Read() errors, or when Close() is called. // either when Read() errors, or when Close() is called.
reqDone chan<- struct{} reqDone chan<- struct{}
reqDoneClosed bool reqDoneOnce sync.Once
} }
var _ io.ReadCloser = &hijackableBody{} var _ io.ReadCloser = &hijackableBody{}
@@ -117,13 +118,11 @@ func (r *hijackableBody) Read(b []byte) (int, error) {
} }
func (r *hijackableBody) requestDone() { func (r *hijackableBody) requestDone() {
if r.reqDoneClosed || r.reqDone == nil {
return
}
if r.reqDone != nil { if r.reqDone != nil {
r.reqDoneOnce.Do(func() {
close(r.reqDone) close(r.reqDone)
})
} }
r.reqDoneClosed = true
} }
func (r *hijackableBody) Close() error { func (r *hijackableBody) Close() error {

View File

@@ -54,6 +54,18 @@ var _ = Describe("Response Body", func() {
Expect(rb.Close()).To(Succeed()) Expect(rb.Close()).To(Succeed())
}) })
It("allows concurrent calls to Close", func() {
str := mockquic.NewMockStream(mockCtrl)
rb := newResponseBody(&stream{Stream: str}, -1, reqDone)
str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeRequestCanceled)).MaxTimes(2)
go func() {
defer GinkgoRecover()
Expect(rb.Close()).To(Succeed())
}()
Expect(rb.Close()).To(Succeed())
Expect(reqDone).To(BeClosed())
})
Context("length limiting", func() { Context("length limiting", func() {
It("reads all frames", func() { It("reads all frames", func() {
var buf bytes.Buffer var buf bytes.Buffer