net/http/internal/http2: reject STREAM_ENDED + Content-Length request

We silently accepted HEADERS with END_STREAM and non-zero
"content-length" header set, meaning no DATA frames will follow.

The Handler saw ContentLength == 0 and EOF instead of rejecting the
malformed request.

Change-Id: I01e3ec3bddc4cc6c43f8bcdcfa40b0dd7d9d7f55
Reviewed-on: https://go-review.googlesource.com/c/go/+/777180
Reviewed-by: Nicholas Husin <nsh@golang.org>
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Nicholas Husin <husin@google.com>
This commit is contained in:
Brad Fitzpatrick 2026-05-12 14:45:56 +00:00 committed by Gopher Robot
parent c22f92a751
commit 7601c4bf42
2 changed files with 25 additions and 7 deletions

View file

@ -2174,14 +2174,18 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res
return nil, nil, err
}
bodyOpen := !f.StreamEnded()
if bodyOpen {
if vv, ok := rp.Header["Content-Length"]; ok {
if cl, err := strconv.ParseUint(vv[0], 10, 63); err == nil {
req.ContentLength = int64(cl)
} else {
req.ContentLength = 0
}
if vv, ok := rp.Header["Content-Length"]; ok {
if cl, err := strconv.ParseUint(vv[0], 10, 63); err == nil {
req.ContentLength = int64(cl)
} else {
req.ContentLength = 0
}
if !bodyOpen && req.ContentLength != 0 {
return nil, nil, sc.countError("bodyless_content_length", streamError(f.StreamID, ErrCodeProtocol))
}
}
if bodyOpen {
if _, ok := rp.Header["Content-Length"]; !ok {
req.ContentLength = -1
}
req.Body.(*requestBody).pipe = &pipe{

View file

@ -779,6 +779,20 @@ func testServer_Request_Post_Body_ContentLength_TooLarge(t testing.TB) {
})
}
func TestServer_Request_Post_Body_ContentLength_EndStream(t *testing.T) {
testRejectRequest(t, func(st *serverTester) {
st.writeHeaders(HeadersFrameParam{
StreamID: 1, // clients send odd numbers
BlockFragment: st.encodeHeader(
":method", "POST",
"content-length", "3",
),
EndStream: true,
EndHeaders: true,
})
})
}
func TestServer_Request_Post_Body_ContentLength_TooSmall(t *testing.T) {
synctestTest(t, testServer_Request_Post_Body_ContentLength_TooSmall)
}