From 514df55288913cb3caa2a17577cc0b46c9021343 Mon Sep 17 00:00:00 2001 From: Marten Seemann <martenseemann@gmail.com> Date: Tue, 18 Jul 2023 09:13:16 -0700 Subject: [PATCH] http3: enforce ordering requirement between pseudo and regular headers (#3968) * http3: enforce ordering requirement between pseudo and regular headers * simplify logic --- http3/request.go | 16 +++++++++++++--- http3/request_test.go | 11 +++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/http3/request.go b/http3/request.go index 6105144f..f82feb8f 100644 --- a/http3/request.go +++ b/http3/request.go @@ -17,6 +17,7 @@ func requestFromHeaders(headers []qpack.HeaderField) (*http.Request, error) { var path, authority, method, protocol, scheme, contentLengthStr string httpHeaders := http.Header{} + var readFirstRegularHeader bool for _, h := range headers { // field names need to be lowercase, see section 4.2 of RFC 9114 if strings.ToLower(h.Name) != h.Name { @@ -25,6 +26,18 @@ func requestFromHeaders(headers []qpack.HeaderField) (*http.Request, error) { if !httpguts.ValidHeaderFieldValue(h.Value) { return nil, fmt.Errorf("invalid header field value for %s: %q", h.Name, h.Value) } + if h.IsPseudo() { + if readFirstRegularHeader { + // all pseudo headers must appear before regular header fields, see section 4.3 of RFC 9114 + return nil, fmt.Errorf("received pseudo header %s after a regular header field", h.Name) + } + } else { + if !httpguts.ValidHeaderFieldName(h.Name) { + return nil, fmt.Errorf("invalid header field name: %q", h.Name) + } + readFirstRegularHeader = true + } + switch h.Name { case ":path": path = h.Value @@ -40,9 +53,6 @@ func requestFromHeaders(headers []qpack.HeaderField) (*http.Request, error) { contentLengthStr = h.Value default: if !h.IsPseudo() { - if !httpguts.ValidHeaderFieldName(h.Name) { - return nil, fmt.Errorf("invalid header field name: %q", h.Name) - } httpHeaders.Add(h.Name, h.Value) } } diff --git a/http3/request_test.go b/http3/request_test.go index 0edfa285..87275c9c 100644 --- a/http3/request_test.go +++ b/http3/request_test.go @@ -66,6 +66,17 @@ var _ = Describe("Request", func() { Expect(err).To(MatchError(`invalid header field value for content: "\n"`)) }) + It("rejects pseudo header fields after regular header fields", func() { + headers := []qpack.HeaderField{ + {Name: ":path", Value: "/foo"}, + {Name: "content-length", Value: "42"}, + {Name: ":authority", Value: "quic.clemente.io"}, + {Name: ":method", Value: "GET"}, + } + _, err := requestFromHeaders(headers) + Expect(err).To(MatchError("received pseudo header :authority after a regular header field")) + }) + It("rejects negative Content-Length values", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"},