From 7c3544ca34bc4ea1dc6e37381f7b395bc9936aa5 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Fri, 30 Aug 2024 20:28:06 +0800 Subject: [PATCH] http3: set ContentLength to -1 if no Content-Length header is set (#4645) This applies to both the http.Response and the http.Request. --- http3/headers.go | 3 ++- http3/headers_test.go | 12 +++++++++++- http3/http_stream.go | 6 +----- integrationtests/self/http_test.go | 1 + 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/http3/headers.go b/http3/headers.go index 7c67342e..3f17f222 100644 --- a/http3/headers.go +++ b/http3/headers.go @@ -22,7 +22,7 @@ type header struct { Status string // for Extended connect Protocol string - // parsed and deduplicated + // parsed and deduplicated. -1 if no Content-Length header is sent ContentLength int64 // all non-pseudo headers Headers http.Header @@ -91,6 +91,7 @@ func parseHeaders(headers []qpack.HeaderField, isRequest bool) (header, error) { } } } + hdr.ContentLength = -1 if len(contentLengthStr) > 0 { // use ParseUint instead of ParseInt, so that parsing fails on negative values cl, err := strconv.ParseUint(contentLengthStr, 10, 63) diff --git a/http3/headers_test.go b/http3/headers_test.go index e9bf7a76..5bc41e31 100644 --- a/http3/headers_test.go +++ b/http3/headers_test.go @@ -33,6 +33,17 @@ var _ = Describe("Request", func() { Expect(req.RequestURI).To(Equal("/foo")) }) + It("sets the ContentLength to -1", func() { + headers := []qpack.HeaderField{ + {Name: ":path", Value: "/foo"}, + {Name: ":authority", Value: "quic.clemente.io"}, + {Name: ":method", Value: "GET"}, + } + req, err := requestFromHeaders(headers) + Expect(err).ToNot(HaveOccurred()) + Expect(req.ContentLength).To(BeEquivalentTo(-1)) + }) + It("rejects upper-case fields", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, @@ -343,7 +354,6 @@ var _ = Describe("Response", func() { It("rejects invalid status codes", func() { headers := []qpack.HeaderField{ {Name: ":status", Value: "foobar"}, - {Name: "content-length", Value: "42"}, } err := updateResponseFromHeaders(&http.Response{}, headers) Expect(err).To(HaveOccurred()) diff --git a/http3/http_stream.go b/http3/http_stream.go index af46226b..47add370 100644 --- a/http3/http_stream.go +++ b/http3/http_stream.go @@ -238,11 +238,7 @@ func (s *requestStream) ReadResponse() (*http.Response, error) { // Check that the server doesn't send more data in DATA frames than indicated by the Content-Length header (if set). // See section 4.1.2 of RFC 9114. - contentLength := int64(-1) - if _, ok := res.Header["Content-Length"]; ok && res.ContentLength >= 0 { - contentLength = res.ContentLength - } - respBody := newResponseBody(s.stream, contentLength, s.reqDone) + respBody := newResponseBody(s.stream, res.ContentLength, s.reqDone) // Rules for when to set Content-Length are defined in https://tools.ietf.org/html/rfc7230#section-3.3.2. isInformational := res.StatusCode >= 100 && res.StatusCode < 200 diff --git a/integrationtests/self/http_test.go b/integrationtests/self/http_test.go index cd97896b..cf978685 100644 --- a/integrationtests/self/http_test.go +++ b/integrationtests/self/http_test.go @@ -289,6 +289,7 @@ var _ = Describe("HTTP tests", func() { Expect(resp.StatusCode).To(Equal(200)) body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 20*time.Second)) Expect(err).ToNot(HaveOccurred()) + Expect(resp.ContentLength).To(BeEquivalentTo(-1)) Expect(body).To(Equal(PRDataLong)) })