mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-04 12:47:36 +03:00
160 lines
3.3 KiB
Go
160 lines
3.3 KiB
Go
package http3
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
|
)
|
|
|
|
type byteReader interface {
|
|
io.ByteReader
|
|
io.Reader
|
|
}
|
|
|
|
type byteReaderImpl struct{ io.Reader }
|
|
|
|
func (br *byteReaderImpl) ReadByte() (byte, error) {
|
|
b := make([]byte, 1)
|
|
if _, err := br.Reader.Read(b); err != nil {
|
|
return 0, err
|
|
}
|
|
return b[0], nil
|
|
}
|
|
|
|
type frame interface{}
|
|
|
|
func parseNextFrame(b io.Reader) (frame, error) {
|
|
br, ok := b.(byteReader)
|
|
if !ok {
|
|
br = &byteReaderImpl{b}
|
|
}
|
|
t, err := utils.ReadVarInt(br)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
l, err := utils.ReadVarInt(br)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch t {
|
|
case 0x0:
|
|
return &dataFrame{Length: l}, nil
|
|
case 0x1:
|
|
return &headersFrame{Length: l}, nil
|
|
case 0x4:
|
|
return parseSettingsFrame(br, l)
|
|
case 0x3: // CANCEL_PUSH
|
|
fallthrough
|
|
case 0x5: // PUSH_PROMISE
|
|
fallthrough
|
|
case 0x7: // GOAWAY
|
|
fallthrough
|
|
case 0xd: // MAX_PUSH_ID
|
|
fallthrough
|
|
case 0xe: // DUPLICATE_PUSH
|
|
fallthrough
|
|
default:
|
|
// skip over unknown frames
|
|
if _, err := io.CopyN(ioutil.Discard, br, int64(l)); err != nil {
|
|
return nil, err
|
|
}
|
|
return parseNextFrame(b)
|
|
}
|
|
}
|
|
|
|
type dataFrame struct {
|
|
Length uint64
|
|
}
|
|
|
|
func (f *dataFrame) Write(b *bytes.Buffer) {
|
|
utils.WriteVarInt(b, 0x0)
|
|
utils.WriteVarInt(b, f.Length)
|
|
}
|
|
|
|
type headersFrame struct {
|
|
Length uint64
|
|
}
|
|
|
|
func (f *headersFrame) Write(b *bytes.Buffer) {
|
|
utils.WriteVarInt(b, 0x1)
|
|
utils.WriteVarInt(b, f.Length)
|
|
}
|
|
|
|
const settingDatagram = 0x276
|
|
|
|
type settingsFrame struct {
|
|
Datagram bool
|
|
other map[uint64]uint64 // all settings that we don't explicitly recognize
|
|
}
|
|
|
|
func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) {
|
|
if l > 8*(1<<10) {
|
|
return nil, fmt.Errorf("unexpected size for SETTINGS frame: %d", l)
|
|
}
|
|
buf := make([]byte, l)
|
|
if _, err := io.ReadFull(r, buf); err != nil {
|
|
if err == io.ErrUnexpectedEOF {
|
|
return nil, io.EOF
|
|
}
|
|
return nil, err
|
|
}
|
|
frame := &settingsFrame{}
|
|
b := bytes.NewReader(buf)
|
|
var readDatagram bool
|
|
for b.Len() > 0 {
|
|
id, err := utils.ReadVarInt(b)
|
|
if err != nil { // should not happen. We allocated the whole frame already.
|
|
return nil, err
|
|
}
|
|
val, err := utils.ReadVarInt(b)
|
|
if err != nil { // should not happen. We allocated the whole frame already.
|
|
return nil, err
|
|
}
|
|
|
|
switch id {
|
|
case settingDatagram:
|
|
if readDatagram {
|
|
return nil, fmt.Errorf("duplicate setting: %d", id)
|
|
}
|
|
readDatagram = true
|
|
if val != 0 && val != 1 {
|
|
return nil, fmt.Errorf("invalid value for H3_DATAGRAM: %d", val)
|
|
}
|
|
frame.Datagram = val == 1
|
|
default:
|
|
if _, ok := frame.other[id]; ok {
|
|
return nil, fmt.Errorf("duplicate setting: %d", id)
|
|
}
|
|
if frame.other == nil {
|
|
frame.other = make(map[uint64]uint64)
|
|
}
|
|
frame.other[id] = val
|
|
}
|
|
}
|
|
return frame, nil
|
|
}
|
|
|
|
func (f *settingsFrame) Write(b *bytes.Buffer) {
|
|
utils.WriteVarInt(b, 0x4)
|
|
var l protocol.ByteCount
|
|
for id, val := range f.other {
|
|
l += utils.VarIntLen(id) + utils.VarIntLen(val)
|
|
}
|
|
if f.Datagram {
|
|
l += utils.VarIntLen(settingDatagram) + utils.VarIntLen(1)
|
|
}
|
|
utils.WriteVarInt(b, uint64(l))
|
|
if f.Datagram {
|
|
utils.WriteVarInt(b, settingDatagram)
|
|
utils.WriteVarInt(b, 1)
|
|
}
|
|
for id, val := range f.other {
|
|
utils.WriteVarInt(b, id)
|
|
utils.WriteVarInt(b, val)
|
|
}
|
|
}
|