mirror of
https://github.com/golang/net.git
synced 2026-03-31 02:17:08 +09:00
internal/http3: ensure bodyReader cannot be read after being closed
Closing bodyReader currently only closes the underlying QUIC stream. As a result, any unread data that was written to bodyStream prior to it being closed can still be read. This is inconsistent with how we expect net/http.Response.Body to behave. For golang/go#70914 Change-Id: I58226c0d23ea3bbd97f3ceb5c3659e91660f84c5 Reviewed-on: https://go-review.googlesource.com/c/net/+/741982 Reviewed-by: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Nicholas Husin <husin@google.com>
This commit is contained in:
committed by
Nicholas Husin
parent
d7c76faf07
commit
da558ff100
@@ -8,6 +8,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -136,5 +137,9 @@ func (r *bodyReader) Close() error {
|
||||
// Unlike the HTTP/1 and HTTP/2 body readers (at the time of this comment being written),
|
||||
// calling Close concurrently with Read will interrupt the read.
|
||||
r.st.stream.CloseRead()
|
||||
// Make sure that any data that has already been written to bodyReader
|
||||
// cannot be read after it has been closed.
|
||||
r.err = net.ErrClosed
|
||||
r.remain = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,8 +8,10 @@ package http3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
@@ -46,7 +48,13 @@ func TestReadData(t *testing.T) {
|
||||
size int64
|
||||
eof bool
|
||||
}
|
||||
wantError struct{}
|
||||
// Check that reading the body results in non-EOF error.
|
||||
wantError struct {
|
||||
// If err is not nil, also check that the error received is err.
|
||||
err error
|
||||
}
|
||||
// Close the body.
|
||||
closeBody struct{}
|
||||
)
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
@@ -178,6 +186,21 @@ func TestReadData(t *testing.T) {
|
||||
wantBody{size: 4},
|
||||
wantBody{size: 8},
|
||||
},
|
||||
}, {
|
||||
name: "read after body close",
|
||||
steps: []any{
|
||||
receiveHeaders{contentLength: -1},
|
||||
receiveDataHeader{size: 2},
|
||||
receiveData{size: 2},
|
||||
receiveDataHeader{size: 4},
|
||||
receiveData{size: 4},
|
||||
receiveDataHeader{size: 8},
|
||||
receiveData{size: 8},
|
||||
wantBody{size: 2},
|
||||
wantBody{size: 4},
|
||||
closeBody{},
|
||||
wantError{err: net.ErrClosed},
|
||||
},
|
||||
}} {
|
||||
|
||||
runTest := func(t testing.TB, h http.Header, st *testQUICStream, body func() io.ReadCloser) {
|
||||
@@ -246,9 +269,17 @@ func TestReadData(t *testing.T) {
|
||||
}
|
||||
}
|
||||
case wantError:
|
||||
if n, err := body().Read([]byte{0}); n != 0 || err == nil || err == io.EOF {
|
||||
n, err := body().Read([]byte{0})
|
||||
if n != 0 || err == nil || err == io.EOF {
|
||||
t.Fatalf("resp.Body.Read() = %v, %v; want error", n, err)
|
||||
}
|
||||
if step.err != nil && !errors.Is(step.err, err) {
|
||||
t.Fatalf("resp.Body.Read() = %v, %v; want %v error", n, err, step.err)
|
||||
}
|
||||
case closeBody:
|
||||
if err := body().Close(); err != nil {
|
||||
t.Fatalf("resp.Body.Close() = %v, want nil", err)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unknown test step %T", step)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user