uquic/internal/wire/stream_frame.go
Marten Seemann 5974c6c113 correctly determine the length of STREAM frames for IETF QUIC
The length of the data has an influence on the length of the frame (if
it contains the data length), and the length can either consume 1 or 2
bytes due to variable length encoding.
2018-02-05 12:02:49 +08:00

158 lines
4 KiB
Go

package wire
import (
"bytes"
"errors"
"io"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
)
// A StreamFrame of QUIC
type StreamFrame struct {
StreamID protocol.StreamID
FinBit bool
DataLenPresent bool
Offset protocol.ByteCount
Data []byte
}
// ParseStreamFrame reads a STREAM frame
func ParseStreamFrame(r *bytes.Reader, version protocol.VersionNumber) (*StreamFrame, error) {
if !version.UsesIETFFrameFormat() {
return parseLegacyStreamFrame(r, version)
}
frame := &StreamFrame{}
typeByte, err := r.ReadByte()
if err != nil {
return nil, err
}
frame.FinBit = typeByte&0x1 > 0
frame.DataLenPresent = typeByte&0x2 > 0
hasOffset := typeByte&0x4 > 0
streamID, err := utils.ReadVarInt(r)
if err != nil {
return nil, err
}
frame.StreamID = protocol.StreamID(streamID)
if hasOffset {
offset, err := utils.ReadVarInt(r)
if err != nil {
return nil, err
}
frame.Offset = protocol.ByteCount(offset)
}
var dataLen uint64
if frame.DataLenPresent {
var err error
dataLen, err = utils.ReadVarInt(r)
if err != nil {
return nil, err
}
// shortcut to prevent the unneccessary allocation of dataLen bytes
// if the dataLen is larger than the remaining length of the packet
// reading the packet contents would result in EOF when attempting to READ
if dataLen > uint64(r.Len()) {
return nil, io.EOF
}
} else {
// The rest of the packet is data
dataLen = uint64(r.Len())
}
if dataLen != 0 {
frame.Data = make([]byte, dataLen)
if _, err := io.ReadFull(r, frame.Data); err != nil {
// this should never happen, since we already checked the dataLen earlier
return nil, err
}
}
if frame.Offset+frame.DataLen() > protocol.MaxByteCount {
return nil, qerr.Error(qerr.InvalidStreamData, "data overflows maximum offset")
}
if !frame.FinBit && frame.DataLen() == 0 {
return nil, qerr.EmptyStreamFrameNoFin
}
return frame, nil
}
// Write writes a STREAM frame
func (f *StreamFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
if !version.UsesIETFFrameFormat() {
return f.writeLegacy(b, version)
}
if len(f.Data) == 0 && !f.FinBit {
return errors.New("StreamFrame: attempting to write empty frame without FIN")
}
typeByte := byte(0x10)
if f.FinBit {
typeByte ^= 0x1
}
hasOffset := f.Offset != 0
if f.DataLenPresent {
typeByte ^= 0x2
}
if hasOffset {
typeByte ^= 0x4
}
b.WriteByte(typeByte)
utils.WriteVarInt(b, uint64(f.StreamID))
if hasOffset {
utils.WriteVarInt(b, uint64(f.Offset))
}
if f.DataLenPresent {
utils.WriteVarInt(b, uint64(f.DataLen()))
}
b.Write(f.Data)
return nil
}
// MinLength returns the length of the header of a StreamFrame
// the total length of the frame is frame.MinLength() + frame.DataLen()
func (f *StreamFrame) MinLength(version protocol.VersionNumber) protocol.ByteCount {
if !version.UsesIETFFrameFormat() {
return f.minLengthLegacy(version)
}
length := 1 + utils.VarIntLen(uint64(f.StreamID))
if f.Offset != 0 {
length += utils.VarIntLen(uint64(f.Offset))
}
if f.DataLenPresent {
length += utils.VarIntLen(uint64(f.DataLen()))
}
return length
}
// MaxDataLen returns the maximum data length
// If 0 is returned, writing will fail (a STREAM_FRAME must contain at least 1 byte of data).
func (f *StreamFrame) MaxDataLen(maxSize protocol.ByteCount, version protocol.VersionNumber) protocol.ByteCount {
if !version.UsesIETFFrameFormat() {
return f.maxDataLenLegacy(maxSize, version)
}
headerLen := 1 + utils.VarIntLen(uint64(f.StreamID))
if f.Offset != 0 {
headerLen += utils.VarIntLen(uint64(f.Offset))
}
if f.DataLenPresent {
// pretend that the data size will be 1 bytes
// if it turns out that varint encoding the length will consume 2 bytes, we need to adjust the data length afterwards
headerLen++
}
if headerLen > maxSize {
return 0
}
maxDataLen := maxSize - headerLen
if f.DataLenPresent && utils.VarIntLen(uint64(maxDataLen)) != 1 {
maxDataLen--
}
return maxDataLen
}