http3: reject pseudo header fields in trailers (#4639)

* http3: reject pseudo header fields in trailers

As defined in section 4.3 of RFC 9114.

* http3: improve allocs for trailer map

* http3: rename connection.parseTrailer to decodeTrailers
This commit is contained in:
Marten Seemann
2024-08-23 17:42:18 +07:00
committed by GitHub
parent 17fb3b96ba
commit 920bfb46af
3 changed files with 31 additions and 7 deletions

View File

@@ -116,7 +116,7 @@ func (c *connection) openRequestStream(
qstr := newStateTrackingStream(str, c, datagrams)
rsp := &http.Response{}
hstr := newStream(qstr, c, datagrams, func(r io.Reader, l uint64) error {
hdr, err := c.parseTrailer(r, l, maxHeaderBytes)
hdr, err := c.decodeTrailers(r, l, maxHeaderBytes)
if err != nil {
return err
}
@@ -126,7 +126,7 @@ func (c *connection) openRequestStream(
return newRequestStream(hstr, requestWriter, reqDone, c.decoder, disableCompression, maxHeaderBytes, rsp), nil
}
func (c *connection) parseTrailer(r io.Reader, l, maxHeaderBytes uint64) (http.Header, error) {
func (c *connection) decodeTrailers(r io.Reader, l, maxHeaderBytes uint64) (http.Header, error) {
if l > maxHeaderBytes {
return nil, fmt.Errorf("HEADERS frame too large: %d bytes (max: %d)", l, maxHeaderBytes)
}
@@ -139,11 +139,7 @@ func (c *connection) parseTrailer(r io.Reader, l, maxHeaderBytes uint64) (http.H
if err != nil {
return nil, err
}
h := http.Header{}
for _, field := range fields {
h.Add(field.Name, field.Value)
}
return h, nil
return parseTrailers(fields)
}
func (c *connection) acceptStream(ctx context.Context) (quic.Stream, *datagrammer, error) {

View File

@@ -101,6 +101,17 @@ func parseHeaders(headers []qpack.HeaderField, isRequest bool) (header, error) {
return hdr, nil
}
func parseTrailers(headers []qpack.HeaderField) (http.Header, error) {
h := make(http.Header, len(headers))
for _, field := range headers {
if field.IsPseudo() {
return nil, fmt.Errorf("http3: received pseudo header in trailer: %s", field.Name)
}
h.Add(field.Name, field.Value)
}
return h, nil
}
func requestFromHeaders(headerFields []qpack.HeaderField) (*http.Request, error) {
hdr, err := parseHeaders(headerFields, true)
if err != nil {

View File

@@ -358,4 +358,21 @@ var _ = Describe("Response", func() {
err := updateResponseFromHeaders(&http.Response{}, headers)
Expect(err).To(MatchError("invalid response pseudo header: :method"))
})
It("parses trailers", func() {
headers := []qpack.HeaderField{
{Name: "content-length", Value: "42"},
}
hdr, err := parseTrailers(headers)
Expect(err).ToNot(HaveOccurred())
Expect(hdr.Get("Content-Length")).To(Equal("42"))
})
It("parses trailers", func() {
headers := []qpack.HeaderField{
{Name: ":status", Value: "200"},
}
_, err := parseTrailers(headers)
Expect(err).To(MatchError("http3: received pseudo header in trailer: :status"))
})
})