forked from quic-go/quic-go
http3: add basic client-side qlog support (#5374)
This commit is contained in:
@@ -17,6 +17,9 @@ import (
|
||||
"golang.org/x/net/idna"
|
||||
|
||||
"github.com/quic-go/qpack"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/http3/qlog"
|
||||
"github.com/quic-go/quic-go/qlogwriter"
|
||||
)
|
||||
|
||||
const bodyCopyBufferSize = 8 * 1024
|
||||
@@ -36,10 +39,10 @@ func newRequestWriter() *requestWriter {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *requestWriter) WriteRequestHeader(wr io.Writer, req *http.Request, gzip bool) error {
|
||||
func (w *requestWriter) WriteRequestHeader(wr io.Writer, req *http.Request, gzip bool, streamID quic.StreamID, qlogger qlogwriter.Recorder) error {
|
||||
// TODO: figure out how to add support for trailers
|
||||
buf := &bytes.Buffer{}
|
||||
if err := w.writeHeaders(buf, req, gzip); err != nil {
|
||||
if err := w.writeHeaders(buf, req, gzip, streamID, qlogger); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := wr.Write(buf.Bytes()); err != nil {
|
||||
@@ -50,22 +53,26 @@ func (w *requestWriter) WriteRequestHeader(wr io.Writer, req *http.Request, gzip
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *requestWriter) writeHeaders(wr io.Writer, req *http.Request, gzip bool) error {
|
||||
func (w *requestWriter) writeHeaders(wr io.Writer, req *http.Request, gzip bool, streamID quic.StreamID, qlogger qlogwriter.Recorder) error {
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
defer w.encoder.Close()
|
||||
defer w.headerBuf.Reset()
|
||||
|
||||
if err := w.encodeHeaders(req, gzip, "", actualContentLength(req)); err != nil {
|
||||
headerFields, err := w.encodeHeaders(req, gzip, "", actualContentLength(req), qlogger != nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := make([]byte, 0, 128)
|
||||
b = (&headersFrame{Length: uint64(w.headerBuf.Len())}).Append(b)
|
||||
if qlogger != nil {
|
||||
qlogCreatedHeadersFrame(qlogger, streamID, len(b)+w.headerBuf.Len(), w.headerBuf.Len(), headerFields)
|
||||
}
|
||||
if _, err := wr.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := wr.Write(w.headerBuf.Bytes())
|
||||
_, err = wr.Write(w.headerBuf.Bytes())
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -77,17 +84,19 @@ func isExtendedConnectRequest(req *http.Request) bool {
|
||||
// Modified to support Extended CONNECT:
|
||||
// Contrary to what the godoc for the http.Request says,
|
||||
// we do respect the Proto field if the method is CONNECT.
|
||||
func (w *requestWriter) encodeHeaders(req *http.Request, addGzipHeader bool, trailers string, contentLength int64) error {
|
||||
//
|
||||
// The returned header fields are only set if doQlog is true.
|
||||
func (w *requestWriter) encodeHeaders(req *http.Request, addGzipHeader bool, trailers string, contentLength int64, doQlog bool) ([]qlog.HeaderField, error) {
|
||||
host := req.Host
|
||||
if host == "" {
|
||||
host = req.URL.Host
|
||||
}
|
||||
host, err := httpguts.PunycodeHostPort(host)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if !httpguts.ValidHostHeader(host) {
|
||||
return errors.New("http3: invalid Host header")
|
||||
return nil, errors.New("http3: invalid Host header")
|
||||
}
|
||||
|
||||
// http.NewRequest sets this field to HTTP/1.1
|
||||
@@ -101,9 +110,9 @@ func (w *requestWriter) encodeHeaders(req *http.Request, addGzipHeader bool, tra
|
||||
path = strings.TrimPrefix(path, req.URL.Scheme+"://"+host)
|
||||
if !validPseudoPath(path) {
|
||||
if req.URL.Opaque != "" {
|
||||
return fmt.Errorf("invalid request :path %q from URL.Opaque = %q", orig, req.URL.Opaque)
|
||||
return nil, fmt.Errorf("invalid request :path %q from URL.Opaque = %q", orig, req.URL.Opaque)
|
||||
} else {
|
||||
return fmt.Errorf("invalid request :path %q", orig)
|
||||
return nil, fmt.Errorf("invalid request :path %q", orig)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,11 +123,11 @@ func (w *requestWriter) encodeHeaders(req *http.Request, addGzipHeader bool, tra
|
||||
// continue to reuse the hpack encoder for future requests)
|
||||
for k, vv := range req.Header {
|
||||
if !httpguts.ValidHeaderFieldName(k) {
|
||||
return fmt.Errorf("invalid HTTP header name %q", k)
|
||||
return nil, fmt.Errorf("invalid HTTP header name %q", k)
|
||||
}
|
||||
for _, v := range vv {
|
||||
if !httpguts.ValidHeaderFieldValue(v) {
|
||||
return fmt.Errorf("invalid HTTP header value %q for header %q", v, k)
|
||||
return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,15 +215,22 @@ func (w *requestWriter) encodeHeaders(req *http.Request, addGzipHeader bool, tra
|
||||
traceHeaders := traceHasWroteHeaderField(trace)
|
||||
|
||||
// Header list size is ok. Write the headers.
|
||||
var headerFields []qlog.HeaderField
|
||||
if doQlog {
|
||||
headerFields = make([]qlog.HeaderField, 0, len(req.Header))
|
||||
}
|
||||
enumerateHeaders(func(name, value string) {
|
||||
name = strings.ToLower(name)
|
||||
w.encoder.WriteField(qpack.HeaderField{Name: name, Value: value})
|
||||
if traceHeaders {
|
||||
traceWroteHeaderField(trace, name, value)
|
||||
}
|
||||
if doQlog {
|
||||
headerFields = append(headerFields, qlog.HeaderField{Name: name, Value: value})
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
return headerFields, nil
|
||||
}
|
||||
|
||||
// authorityAddr returns a given authority (a host/IP, or host:port / ip:port)
|
||||
|
||||
Reference in New Issue
Block a user