forked from quic-go/quic-go
http3: qlog the frame length and payload length of parsed frames (#5380)
This commit is contained in:
@@ -22,6 +22,29 @@ type frame any
|
||||
|
||||
var errHijacked = errors.New("hijacked")
|
||||
|
||||
type countingByteReader struct {
|
||||
quicvarint.Reader
|
||||
NumRead int
|
||||
}
|
||||
|
||||
func (r *countingByteReader) ReadByte() (byte, error) {
|
||||
b, err := r.Reader.ReadByte()
|
||||
if err == nil {
|
||||
r.NumRead++
|
||||
}
|
||||
return b, err
|
||||
}
|
||||
|
||||
func (r *countingByteReader) Read(b []byte) (int, error) {
|
||||
n, err := r.Reader.Read(b)
|
||||
r.NumRead += n
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *countingByteReader) Reset() {
|
||||
r.NumRead = 0
|
||||
}
|
||||
|
||||
type frameParser struct {
|
||||
r io.Reader
|
||||
streamID quic.StreamID
|
||||
@@ -30,9 +53,9 @@ type frameParser struct {
|
||||
}
|
||||
|
||||
func (p *frameParser) ParseNext(qlogger qlogwriter.Recorder) (frame, error) {
|
||||
qr := quicvarint.NewReader(p.r)
|
||||
r := &countingByteReader{Reader: quicvarint.NewReader(p.r)}
|
||||
for {
|
||||
t, err := quicvarint.Read(qr)
|
||||
t, err := quicvarint.Read(r)
|
||||
if err != nil {
|
||||
if p.unknownFrameHandler != nil {
|
||||
hijacked, err := p.unknownFrameHandler(0, err)
|
||||
@@ -56,36 +79,46 @@ func (p *frameParser) ParseNext(qlogger qlogwriter.Recorder) (frame, error) {
|
||||
}
|
||||
// If the unknownFrameHandler didn't process the frame, it is our responsibility to skip it.
|
||||
}
|
||||
l, err := quicvarint.Read(qr)
|
||||
l, err := quicvarint.Read(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch t {
|
||||
case 0x0:
|
||||
case 0x0: // DATA
|
||||
if qlogger != nil {
|
||||
qlogger.RecordEvent(qlog.FrameParsed{
|
||||
StreamID: p.streamID,
|
||||
Raw: qlog.RawInfo{PayloadLength: int(l)},
|
||||
Frame: qlog.Frame{Frame: qlog.DataFrame{}},
|
||||
Raw: qlog.RawInfo{
|
||||
Length: int(l) + r.NumRead,
|
||||
PayloadLength: int(l),
|
||||
},
|
||||
Frame: qlog.Frame{Frame: qlog.DataFrame{}},
|
||||
})
|
||||
}
|
||||
return &dataFrame{Length: l}, nil
|
||||
case 0x1:
|
||||
return &headersFrame{Length: l}, nil
|
||||
case 0x4:
|
||||
return parseSettingsFrame(p.r, l, p.streamID, qlogger)
|
||||
case 0x1: // HEADERS
|
||||
return &headersFrame{
|
||||
Length: l,
|
||||
headerLen: r.NumRead,
|
||||
}, nil
|
||||
case 0x4: // SETTINGS
|
||||
return parseSettingsFrame(r, l, p.streamID, qlogger)
|
||||
case 0x3: // CANCEL_PUSH
|
||||
case 0x5: // PUSH_PROMISE
|
||||
case 0x7:
|
||||
f, err := parseGoAwayFrame(qr, l)
|
||||
case 0x7: // GOAWAY
|
||||
f, err := parseGoAwayFrame(r, l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if qlogger != nil {
|
||||
qlogger.RecordEvent(qlog.FrameParsed{
|
||||
StreamID: p.streamID,
|
||||
Frame: qlog.Frame{Frame: qlog.GoAwayFrame{StreamID: f.StreamID}},
|
||||
Raw: qlog.RawInfo{
|
||||
Length: r.NumRead,
|
||||
PayloadLength: int(l),
|
||||
},
|
||||
Frame: qlog.Frame{Frame: qlog.GoAwayFrame{StreamID: f.StreamID}},
|
||||
})
|
||||
}
|
||||
return f, nil
|
||||
@@ -95,9 +128,10 @@ func (p *frameParser) ParseNext(qlogger qlogwriter.Recorder) (frame, error) {
|
||||
return nil, fmt.Errorf("http3: reserved frame type: %d", t)
|
||||
}
|
||||
// skip over unknown frames
|
||||
if _, err := io.CopyN(io.Discard, qr, int64(l)); err != nil {
|
||||
if _, err := io.CopyN(io.Discard, r, int64(l)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +145,8 @@ func (f *dataFrame) Append(b []byte) []byte {
|
||||
}
|
||||
|
||||
type headersFrame struct {
|
||||
Length uint64
|
||||
Length uint64
|
||||
headerLen int // number of bytes read for type and length field
|
||||
}
|
||||
|
||||
func (f *headersFrame) Append(b []byte) []byte {
|
||||
@@ -137,7 +172,7 @@ func pointer[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
func parseSettingsFrame(r io.Reader, l uint64, streamID quic.StreamID, qlogger qlogwriter.Recorder) (*settingsFrame, error) {
|
||||
func parseSettingsFrame(r *countingByteReader, l uint64, streamID quic.StreamID, qlogger qlogwriter.Recorder) (*settingsFrame, error) {
|
||||
if l > 8*(1<<10) {
|
||||
return nil, fmt.Errorf("unexpected size for SETTINGS frame: %d", l)
|
||||
}
|
||||
@@ -202,8 +237,11 @@ func parseSettingsFrame(r io.Reader, l uint64, streamID quic.StreamID, qlogger q
|
||||
|
||||
qlogger.RecordEvent(qlog.FrameParsed{
|
||||
StreamID: streamID,
|
||||
Raw: qlog.RawInfo{PayloadLength: len(buf)},
|
||||
Frame: qlog.Frame{Frame: settingsFrame},
|
||||
Raw: qlog.RawInfo{
|
||||
Length: r.NumRead,
|
||||
PayloadLength: int(l),
|
||||
},
|
||||
Frame: qlog.Frame{Frame: settingsFrame},
|
||||
})
|
||||
}
|
||||
return frame, nil
|
||||
@@ -241,14 +279,14 @@ type goAwayFrame struct {
|
||||
StreamID quic.StreamID
|
||||
}
|
||||
|
||||
func parseGoAwayFrame(r io.ByteReader, l uint64) (*goAwayFrame, error) {
|
||||
func parseGoAwayFrame(r *countingByteReader, l uint64) (*goAwayFrame, error) {
|
||||
frame := &goAwayFrame{}
|
||||
cbr := countingByteReader{ByteReader: r}
|
||||
id, err := quicvarint.Read(&cbr)
|
||||
startLen := r.NumRead
|
||||
id, err := quicvarint.Read(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cbr.Read != int(l) {
|
||||
if r.NumRead-startLen != int(l) {
|
||||
return nil, errors.New("GOAWAY frame: inconsistent length")
|
||||
}
|
||||
frame.StreamID = quic.StreamID(id)
|
||||
|
||||
Reference in New Issue
Block a user