http3: add client-side HTTP Trailer support (#4581)

* http3: add HTTP Trailer support for clients

This change only adds support to read HTTP trailers sent to clients.

* chore: add protection against some out-of-spec behavior + tests

* chore: re-add test accidentally overwtitten

* chore: empty commit to re-trigger ci

* fix: address some review notes (wip)

* fix: simplify code in stream.Read by using a callback from requestStream.ReadResponse

* restructure where trailers are read and parsed

* WIP simplify trailer parsing design

* chore: refactor to use simpler trailer parsing strategy

* make gofumpt happy

* Update http3/headers.go

Co-authored-by: Marten Seemann <martenseemann@gmail.com>

* remove stray TODO

---------

Co-authored-by: Marten Seemann <martenseemann@gmail.com>
This commit is contained in:
Kevin McDonald
2024-08-23 07:25:58 +02:00
committed by GitHub
parent d067fe4156
commit 17fb3b96ba
8 changed files with 221 additions and 51 deletions

View File

@@ -3,8 +3,10 @@ package http3
import (
"context"
"fmt"
"io"
"log/slog"
"net"
"net/http"
"sync"
"sync/atomic"
"time"
@@ -112,8 +114,36 @@ func (c *connection) openRequestStream(
c.streams[str.StreamID()] = datagrams
c.streamMx.Unlock()
qstr := newStateTrackingStream(str, c, datagrams)
hstr := newStream(qstr, c, datagrams)
return newRequestStream(hstr, requestWriter, reqDone, c.decoder, disableCompression, maxHeaderBytes), nil
rsp := &http.Response{}
hstr := newStream(qstr, c, datagrams, func(r io.Reader, l uint64) error {
hdr, err := c.parseTrailer(r, l, maxHeaderBytes)
if err != nil {
return err
}
rsp.Trailer = hdr
return nil
})
return newRequestStream(hstr, requestWriter, reqDone, c.decoder, disableCompression, maxHeaderBytes, rsp), nil
}
func (c *connection) parseTrailer(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)
}
b := make([]byte, l)
if _, err := io.ReadFull(r, b); err != nil {
return nil, err
}
fields, err := c.decoder.DecodeFull(b)
if err != nil {
return nil, err
}
h := http.Header{}
for _, field := range fields {
h.Add(field.Name, field.Value)
}
return h, nil
}
func (c *connection) acceptStream(ctx context.Context) (quic.Stream, *datagrammer, error) {