http3: enforce ordering requirement between pseudo and regular headers (#3968)

* http3: enforce ordering requirement between pseudo and regular headers

* simplify logic
This commit is contained in:
Marten Seemann 2023-07-18 09:13:16 -07:00 committed by GitHub
parent 4378283f95
commit 514df55288
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 24 additions and 3 deletions

View file

@ -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)
}
}

View file

@ -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"},