mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-05 05:07:36 +03:00
implement parsing and writing of the new STREAM frames
This commit is contained in:
parent
1a515d1371
commit
11f746a183
14 changed files with 1015 additions and 601 deletions
|
@ -2,7 +2,6 @@ package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// A PacketNumber in QUIC
|
// A PacketNumber in QUIC
|
||||||
|
@ -57,13 +56,13 @@ func (t PacketType) String() string {
|
||||||
type ConnectionID uint64
|
type ConnectionID uint64
|
||||||
|
|
||||||
// A StreamID in QUIC
|
// A StreamID in QUIC
|
||||||
type StreamID uint32
|
type StreamID uint64
|
||||||
|
|
||||||
// A ByteCount in QUIC
|
// A ByteCount in QUIC
|
||||||
type ByteCount uint64
|
type ByteCount uint64
|
||||||
|
|
||||||
// MaxByteCount is the maximum value of a ByteCount
|
// MaxByteCount is the maximum value of a ByteCount
|
||||||
const MaxByteCount = ByteCount(math.MaxUint64)
|
const MaxByteCount = ByteCount(1<<62 - 1)
|
||||||
|
|
||||||
// MaxReceivePacketSize maximum packet size of any QUIC packet, based on
|
// MaxReceivePacketSize maximum packet size of any QUIC packet, based on
|
||||||
// ethernet's max size, minus the IP and UDP headers. IPv6 has a 40 byte header,
|
// ethernet's max size, minus the IP and UDP headers. IPv6 has a 40 byte header,
|
||||||
|
|
|
@ -56,6 +56,11 @@ var _ = Describe("Version", func() {
|
||||||
Expect(VersionTLS.UsesIETFFrameFormat()).To(BeTrue())
|
Expect(VersionTLS.UsesIETFFrameFormat()).To(BeTrue())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("tells if a version uses the IETF frame types", func() {
|
||||||
|
Expect(Version39.UsesIETFFrameFormat()).To(BeFalse())
|
||||||
|
Expect(VersionTLS.UsesIETFFrameFormat()).To(BeTrue())
|
||||||
|
})
|
||||||
|
|
||||||
It("says if a stream contributes to connection-level flowcontrol, for gQUIC", func() {
|
It("says if a stream contributes to connection-level flowcontrol, for gQUIC", func() {
|
||||||
Expect(Version39.StreamContributesToConnectionFlowControl(1)).To(BeFalse())
|
Expect(Version39.StreamContributesToConnectionFlowControl(1)).To(BeFalse())
|
||||||
Expect(Version39.StreamContributesToConnectionFlowControl(2)).To(BeTrue())
|
Expect(Version39.StreamContributesToConnectionFlowControl(2)).To(BeTrue())
|
||||||
|
|
|
@ -19,13 +19,12 @@ type StreamFrame struct {
|
||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// ParseStreamFrame reads a STREAM frame
|
||||||
errInvalidStreamIDLen = errors.New("StreamFrame: Invalid StreamID length")
|
|
||||||
errInvalidOffsetLen = errors.New("StreamFrame: Invalid offset length")
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParseStreamFrame reads a stream frame. The type byte must not have been read yet.
|
|
||||||
func ParseStreamFrame(r *bytes.Reader, version protocol.VersionNumber) (*StreamFrame, error) {
|
func ParseStreamFrame(r *bytes.Reader, version protocol.VersionNumber) (*StreamFrame, error) {
|
||||||
|
if !version.UsesIETFFrameFormat() {
|
||||||
|
return parseLegacyStreamFrame(r, version)
|
||||||
|
}
|
||||||
|
|
||||||
frame := &StreamFrame{}
|
frame := &StreamFrame{}
|
||||||
|
|
||||||
typeByte, err := r.ReadByte()
|
typeByte, err := r.ReadByte()
|
||||||
|
@ -33,44 +32,39 @@ func ParseStreamFrame(r *bytes.Reader, version protocol.VersionNumber) (*StreamF
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.FinBit = typeByte&0x40 > 0
|
frame.FinBit = typeByte&0x1 > 0
|
||||||
frame.DataLenPresent = typeByte&0x20 > 0
|
frame.DataLenPresent = typeByte&0x2 > 0
|
||||||
offsetLen := typeByte & 0x1c >> 2
|
hasOffset := typeByte&0x4 > 0
|
||||||
if offsetLen != 0 {
|
|
||||||
offsetLen++
|
|
||||||
}
|
|
||||||
streamIDLen := typeByte&0x3 + 1
|
|
||||||
|
|
||||||
sid, err := utils.GetByteOrder(version).ReadUintN(r, streamIDLen)
|
streamID, err := utils.ReadVarInt(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
frame.StreamID = protocol.StreamID(sid)
|
frame.StreamID = protocol.StreamID(streamID)
|
||||||
|
if hasOffset {
|
||||||
offset, err := utils.GetByteOrder(version).ReadUintN(r, offsetLen)
|
offset, err := utils.ReadVarInt(r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
frame.Offset = protocol.ByteCount(offset)
|
|
||||||
|
|
||||||
var dataLen uint16
|
|
||||||
if frame.DataLenPresent {
|
|
||||||
dataLen, err = utils.GetByteOrder(version).ReadUint16(r)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
frame.Offset = protocol.ByteCount(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// shortcut to prevent the unneccessary allocation of dataLen bytes
|
var dataLen uint64
|
||||||
// if the dataLen is larger than the remaining length of the packet
|
if frame.DataLenPresent {
|
||||||
// reading the packet contents would result in EOF when attempting to READ
|
var err error
|
||||||
if int(dataLen) > r.Len() {
|
dataLen, err = utils.ReadVarInt(r)
|
||||||
return nil, io.EOF
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
|
}
|
||||||
if !frame.DataLenPresent {
|
// 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
|
// The rest of the packet is data
|
||||||
dataLen = uint16(r.Len())
|
dataLen = uint64(r.Len())
|
||||||
}
|
}
|
||||||
if dataLen != 0 {
|
if dataLen != 0 {
|
||||||
frame.Data = make([]byte, dataLen)
|
frame.Data = make([]byte, dataLen)
|
||||||
|
@ -79,8 +73,7 @@ func ParseStreamFrame(r *bytes.Reader, version protocol.VersionNumber) (*StreamF
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if frame.Offset+frame.DataLen() > protocol.MaxByteCount {
|
||||||
if frame.Offset+frame.DataLen() < frame.Offset {
|
|
||||||
return nil, qerr.Error(qerr.InvalidStreamData, "data overflows maximum offset")
|
return nil, qerr.Error(qerr.InvalidStreamData, "data overflows maximum offset")
|
||||||
}
|
}
|
||||||
if !frame.FinBit && frame.DataLen() == 0 {
|
if !frame.FinBit && frame.DataLen() == 0 {
|
||||||
|
@ -89,118 +82,51 @@ func ParseStreamFrame(r *bytes.Reader, version protocol.VersionNumber) (*StreamF
|
||||||
return frame, nil
|
return frame, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteStreamFrame writes a stream frame.
|
// Write writes a STREAM frame
|
||||||
func (f *StreamFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
|
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 {
|
if len(f.Data) == 0 && !f.FinBit {
|
||||||
return errors.New("StreamFrame: attempting to write empty frame without FIN")
|
return errors.New("StreamFrame: attempting to write empty frame without FIN")
|
||||||
}
|
}
|
||||||
|
|
||||||
typeByte := uint8(0x80) // sets the leftmost bit to 1
|
typeByte := byte(0x10)
|
||||||
if f.FinBit {
|
if f.FinBit {
|
||||||
typeByte ^= 0x40
|
typeByte ^= 0x1
|
||||||
}
|
}
|
||||||
|
hasOffset := f.Offset != 0
|
||||||
if f.DataLenPresent {
|
if f.DataLenPresent {
|
||||||
typeByte ^= 0x20
|
typeByte ^= 0x2
|
||||||
}
|
}
|
||||||
|
if hasOffset {
|
||||||
offsetLength := f.getOffsetLength()
|
typeByte ^= 0x4
|
||||||
if offsetLength > 0 {
|
|
||||||
typeByte ^= (uint8(offsetLength) - 1) << 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
streamIDLen := f.calculateStreamIDLength()
|
|
||||||
typeByte ^= streamIDLen - 1
|
|
||||||
|
|
||||||
b.WriteByte(typeByte)
|
b.WriteByte(typeByte)
|
||||||
|
utils.WriteVarInt(b, uint64(f.StreamID))
|
||||||
switch streamIDLen {
|
if hasOffset {
|
||||||
case 1:
|
utils.WriteVarInt(b, uint64(f.Offset))
|
||||||
b.WriteByte(uint8(f.StreamID))
|
|
||||||
case 2:
|
|
||||||
utils.GetByteOrder(version).WriteUint16(b, uint16(f.StreamID))
|
|
||||||
case 3:
|
|
||||||
utils.GetByteOrder(version).WriteUint24(b, uint32(f.StreamID))
|
|
||||||
case 4:
|
|
||||||
utils.GetByteOrder(version).WriteUint32(b, uint32(f.StreamID))
|
|
||||||
default:
|
|
||||||
return errInvalidStreamIDLen
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch offsetLength {
|
|
||||||
case 0:
|
|
||||||
case 2:
|
|
||||||
utils.GetByteOrder(version).WriteUint16(b, uint16(f.Offset))
|
|
||||||
case 3:
|
|
||||||
utils.GetByteOrder(version).WriteUint24(b, uint32(f.Offset))
|
|
||||||
case 4:
|
|
||||||
utils.GetByteOrder(version).WriteUint32(b, uint32(f.Offset))
|
|
||||||
case 5:
|
|
||||||
utils.GetByteOrder(version).WriteUint40(b, uint64(f.Offset))
|
|
||||||
case 6:
|
|
||||||
utils.GetByteOrder(version).WriteUint48(b, uint64(f.Offset))
|
|
||||||
case 7:
|
|
||||||
utils.GetByteOrder(version).WriteUint56(b, uint64(f.Offset))
|
|
||||||
case 8:
|
|
||||||
utils.GetByteOrder(version).WriteUint64(b, uint64(f.Offset))
|
|
||||||
default:
|
|
||||||
return errInvalidOffsetLen
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.DataLenPresent {
|
if f.DataLenPresent {
|
||||||
utils.GetByteOrder(version).WriteUint16(b, uint16(len(f.Data)))
|
utils.WriteVarInt(b, uint64(f.DataLen()))
|
||||||
}
|
}
|
||||||
|
|
||||||
b.Write(f.Data)
|
b.Write(f.Data)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *StreamFrame) calculateStreamIDLength() uint8 {
|
|
||||||
if f.StreamID < (1 << 8) {
|
|
||||||
return 1
|
|
||||||
} else if f.StreamID < (1 << 16) {
|
|
||||||
return 2
|
|
||||||
} else if f.StreamID < (1 << 24) {
|
|
||||||
return 3
|
|
||||||
}
|
|
||||||
return 4
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *StreamFrame) getOffsetLength() protocol.ByteCount {
|
|
||||||
if f.Offset == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if f.Offset < (1 << 16) {
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
if f.Offset < (1 << 24) {
|
|
||||||
return 3
|
|
||||||
}
|
|
||||||
if f.Offset < (1 << 32) {
|
|
||||||
return 4
|
|
||||||
}
|
|
||||||
if f.Offset < (1 << 40) {
|
|
||||||
return 5
|
|
||||||
}
|
|
||||||
if f.Offset < (1 << 48) {
|
|
||||||
return 6
|
|
||||||
}
|
|
||||||
if f.Offset < (1 << 56) {
|
|
||||||
return 7
|
|
||||||
}
|
|
||||||
return 8
|
|
||||||
}
|
|
||||||
|
|
||||||
// MinLength returns the length of the header of a StreamFrame
|
// MinLength returns the length of the header of a StreamFrame
|
||||||
// the total length of the StreamFrame is frame.MinLength() + frame.DataLen()
|
// the total length of the frame is frame.MinLength() + frame.DataLen()
|
||||||
func (f *StreamFrame) MinLength(protocol.VersionNumber) (protocol.ByteCount, error) {
|
func (f *StreamFrame) MinLength(version protocol.VersionNumber) (protocol.ByteCount, error) {
|
||||||
length := protocol.ByteCount(1) + protocol.ByteCount(f.calculateStreamIDLength()) + f.getOffsetLength()
|
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 {
|
if f.DataLenPresent {
|
||||||
length += 2
|
length += utils.VarIntLen(uint64(f.DataLen()))
|
||||||
}
|
}
|
||||||
return length, nil
|
return length, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DataLen gives the length of data in bytes
|
|
||||||
func (f *StreamFrame) DataLen() protocol.ByteCount {
|
|
||||||
return protocol.ByteCount(len(f.Data))
|
|
||||||
}
|
|
||||||
|
|
197
internal/wire/stream_frame_legacy.go
Normal file
197
internal/wire/stream_frame_legacy.go
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errInvalidStreamIDLen = errors.New("StreamFrame: Invalid StreamID length")
|
||||||
|
errInvalidOffsetLen = errors.New("StreamFrame: Invalid offset length")
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseLegacyStreamFrame reads a stream frame. The type byte must not have been read yet.
|
||||||
|
func parseLegacyStreamFrame(r *bytes.Reader, version protocol.VersionNumber) (*StreamFrame, error) {
|
||||||
|
frame := &StreamFrame{}
|
||||||
|
|
||||||
|
typeByte, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.FinBit = typeByte&0x40 > 0
|
||||||
|
frame.DataLenPresent = typeByte&0x20 > 0
|
||||||
|
offsetLen := typeByte & 0x1c >> 2
|
||||||
|
if offsetLen != 0 {
|
||||||
|
offsetLen++
|
||||||
|
}
|
||||||
|
streamIDLen := typeByte&0x3 + 1
|
||||||
|
|
||||||
|
sid, err := utils.GetByteOrder(version).ReadUintN(r, streamIDLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.StreamID = protocol.StreamID(sid)
|
||||||
|
|
||||||
|
offset, err := utils.GetByteOrder(version).ReadUintN(r, offsetLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
frame.Offset = protocol.ByteCount(offset)
|
||||||
|
|
||||||
|
var dataLen uint16
|
||||||
|
if frame.DataLenPresent {
|
||||||
|
dataLen, err = utils.GetByteOrder(version).ReadUint16(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 int(dataLen) > r.Len() {
|
||||||
|
return nil, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if !frame.DataLenPresent {
|
||||||
|
// The rest of the packet is data
|
||||||
|
dataLen = uint16(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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxByteCount is the highest value that can be encoded with the IETF QUIC variable integer encoding (2^62-1).
|
||||||
|
// Note that this value is smaller than the maximum value that could be encoded in the gQUIC STREAM frame (2^64-1).
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeLegacy writes a stream frame.
|
||||||
|
func (f *StreamFrame) writeLegacy(b *bytes.Buffer, version protocol.VersionNumber) error {
|
||||||
|
if len(f.Data) == 0 && !f.FinBit {
|
||||||
|
return errors.New("StreamFrame: attempting to write empty frame without FIN")
|
||||||
|
}
|
||||||
|
|
||||||
|
typeByte := uint8(0x80) // sets the leftmost bit to 1
|
||||||
|
if f.FinBit {
|
||||||
|
typeByte ^= 0x40
|
||||||
|
}
|
||||||
|
if f.DataLenPresent {
|
||||||
|
typeByte ^= 0x20
|
||||||
|
}
|
||||||
|
|
||||||
|
offsetLength := f.getOffsetLength()
|
||||||
|
if offsetLength > 0 {
|
||||||
|
typeByte ^= (uint8(offsetLength) - 1) << 2
|
||||||
|
}
|
||||||
|
|
||||||
|
streamIDLen := f.calculateStreamIDLength()
|
||||||
|
typeByte ^= streamIDLen - 1
|
||||||
|
|
||||||
|
b.WriteByte(typeByte)
|
||||||
|
|
||||||
|
switch streamIDLen {
|
||||||
|
case 1:
|
||||||
|
b.WriteByte(uint8(f.StreamID))
|
||||||
|
case 2:
|
||||||
|
utils.GetByteOrder(version).WriteUint16(b, uint16(f.StreamID))
|
||||||
|
case 3:
|
||||||
|
utils.GetByteOrder(version).WriteUint24(b, uint32(f.StreamID))
|
||||||
|
case 4:
|
||||||
|
utils.GetByteOrder(version).WriteUint32(b, uint32(f.StreamID))
|
||||||
|
default:
|
||||||
|
return errInvalidStreamIDLen
|
||||||
|
}
|
||||||
|
|
||||||
|
switch offsetLength {
|
||||||
|
case 0:
|
||||||
|
case 2:
|
||||||
|
utils.GetByteOrder(version).WriteUint16(b, uint16(f.Offset))
|
||||||
|
case 3:
|
||||||
|
utils.GetByteOrder(version).WriteUint24(b, uint32(f.Offset))
|
||||||
|
case 4:
|
||||||
|
utils.GetByteOrder(version).WriteUint32(b, uint32(f.Offset))
|
||||||
|
case 5:
|
||||||
|
utils.GetByteOrder(version).WriteUint40(b, uint64(f.Offset))
|
||||||
|
case 6:
|
||||||
|
utils.GetByteOrder(version).WriteUint48(b, uint64(f.Offset))
|
||||||
|
case 7:
|
||||||
|
utils.GetByteOrder(version).WriteUint56(b, uint64(f.Offset))
|
||||||
|
case 8:
|
||||||
|
utils.GetByteOrder(version).WriteUint64(b, uint64(f.Offset))
|
||||||
|
default:
|
||||||
|
return errInvalidOffsetLen
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.DataLenPresent {
|
||||||
|
utils.GetByteOrder(version).WriteUint16(b, uint16(len(f.Data)))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Write(f.Data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *StreamFrame) calculateStreamIDLength() uint8 {
|
||||||
|
if f.StreamID < (1 << 8) {
|
||||||
|
return 1
|
||||||
|
} else if f.StreamID < (1 << 16) {
|
||||||
|
return 2
|
||||||
|
} else if f.StreamID < (1 << 24) {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *StreamFrame) getOffsetLength() protocol.ByteCount {
|
||||||
|
if f.Offset == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if f.Offset < (1 << 16) {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
if f.Offset < (1 << 24) {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
if f.Offset < (1 << 32) {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
if f.Offset < (1 << 40) {
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
if f.Offset < (1 << 48) {
|
||||||
|
return 6
|
||||||
|
}
|
||||||
|
if f.Offset < (1 << 56) {
|
||||||
|
return 7
|
||||||
|
}
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *StreamFrame) minLengthLegacy(protocol.VersionNumber) (protocol.ByteCount, error) {
|
||||||
|
length := protocol.ByteCount(1) + protocol.ByteCount(f.calculateStreamIDLength()) + f.getOffsetLength()
|
||||||
|
if f.DataLenPresent {
|
||||||
|
length += 2
|
||||||
|
}
|
||||||
|
return length, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataLen gives the length of data in bytes
|
||||||
|
func (f *StreamFrame) DataLen() protocol.ByteCount {
|
||||||
|
return protocol.ByteCount(len(f.Data))
|
||||||
|
}
|
483
internal/wire/stream_frame_legacy_test.go
Normal file
483
internal/wire/stream_frame_legacy_test.go
Normal file
|
@ -0,0 +1,483 @@
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("STREAM frame (for gQUIC)", func() {
|
||||||
|
Context("when parsing", func() {
|
||||||
|
It("accepts a sample frame", func() {
|
||||||
|
// a STREAM frame, plus 3 additional bytes, not belonging to this frame
|
||||||
|
b := bytes.NewReader([]byte{0x80 ^ 0x20,
|
||||||
|
0x1, // stream id
|
||||||
|
0x0, 0x6, // data length
|
||||||
|
'f', 'o', 'o', 'b', 'a', 'r',
|
||||||
|
'f', 'o', 'o', // additional bytes
|
||||||
|
})
|
||||||
|
frame, err := ParseStreamFrame(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(frame.FinBit).To(BeFalse())
|
||||||
|
Expect(frame.StreamID).To(Equal(protocol.StreamID(1)))
|
||||||
|
Expect(frame.Offset).To(BeZero())
|
||||||
|
Expect(frame.DataLenPresent).To(BeTrue())
|
||||||
|
Expect(frame.Data).To(Equal([]byte("foobar")))
|
||||||
|
Expect(b.Len()).To(Equal(3))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("accepts frames with offsets", func() {
|
||||||
|
b := bytes.NewReader([]byte{0x80 ^ 0x20 /* 2 byte offset */ ^ 0x4,
|
||||||
|
0x1, // stream id
|
||||||
|
0x0, 0x42, // offset
|
||||||
|
0x0, 0x6, // data length
|
||||||
|
'f', 'o', 'o', 'b', 'a', 'r',
|
||||||
|
})
|
||||||
|
frame, err := ParseStreamFrame(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(frame.FinBit).To(BeFalse())
|
||||||
|
Expect(frame.StreamID).To(Equal(protocol.StreamID(1)))
|
||||||
|
Expect(frame.Offset).To(Equal(protocol.ByteCount(0x42)))
|
||||||
|
Expect(frame.DataLenPresent).To(BeTrue())
|
||||||
|
Expect(frame.Data).To(Equal([]byte("foobar")))
|
||||||
|
Expect(b.Len()).To(BeZero())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("errors on EOFs", func() {
|
||||||
|
data := []byte{0x80 ^ 0x20 ^ 0x4,
|
||||||
|
0x1, // stream id
|
||||||
|
0x0, 0x2a, // offset
|
||||||
|
0x0, 0x6, // data length,
|
||||||
|
'f', 'o', 'o', 'b', 'a', 'r',
|
||||||
|
}
|
||||||
|
_, err := ParseStreamFrame(bytes.NewReader(data), versionBigEndian)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
for i := range data {
|
||||||
|
_, err := ParseStreamFrame(bytes.NewReader(data[0:i]), versionBigEndian)
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("accepts frame without data length", func() {
|
||||||
|
b := bytes.NewReader([]byte{0x80,
|
||||||
|
0x1, // stream id
|
||||||
|
'f', 'o', 'o', 'b', 'a', 'r',
|
||||||
|
})
|
||||||
|
frame, err := ParseStreamFrame(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(frame.FinBit).To(BeFalse())
|
||||||
|
Expect(frame.StreamID).To(Equal(protocol.StreamID(1)))
|
||||||
|
Expect(frame.Offset).To(BeZero())
|
||||||
|
Expect(frame.DataLenPresent).To(BeFalse())
|
||||||
|
Expect(frame.Data).To(Equal([]byte("foobar")))
|
||||||
|
Expect(b.Len()).To(BeZero())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("accepts an empty frame with FinBit set, with data length set", func() {
|
||||||
|
// the STREAM frame, plus 3 additional bytes, not belonging to this frame
|
||||||
|
b := bytes.NewReader([]byte{0x80 ^ 0x40 ^ 0x20,
|
||||||
|
0x1, // stream id
|
||||||
|
0, 0, // data length
|
||||||
|
'f', 'o', 'o', // additional bytes
|
||||||
|
})
|
||||||
|
frame, err := ParseStreamFrame(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(frame.FinBit).To(BeTrue())
|
||||||
|
Expect(frame.DataLenPresent).To(BeTrue())
|
||||||
|
Expect(frame.Data).To(BeEmpty())
|
||||||
|
Expect(b.Len()).To(Equal(3))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("accepts an empty frame with the FinBit set", func() {
|
||||||
|
b := bytes.NewReader([]byte{0x80 ^ 0x40,
|
||||||
|
0x1, // stream id
|
||||||
|
'f', 'o', 'o', 'b', 'a', 'r',
|
||||||
|
})
|
||||||
|
frame, err := ParseStreamFrame(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(frame.FinBit).To(BeTrue())
|
||||||
|
Expect(frame.DataLenPresent).To(BeFalse())
|
||||||
|
Expect(frame.Data).To(Equal([]byte("foobar")))
|
||||||
|
Expect(b.Len()).To(BeZero())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("errors on empty stream frames that don't have the FinBit set", func() {
|
||||||
|
b := bytes.NewReader([]byte{0x80 ^ 0x20,
|
||||||
|
0x1, // stream id
|
||||||
|
0, 0, // data length
|
||||||
|
})
|
||||||
|
_, err := ParseStreamFrame(b, versionBigEndian)
|
||||||
|
Expect(err).To(MatchError(qerr.EmptyStreamFrameNoFin))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("rejects frames to too large dataLen", func() {
|
||||||
|
b := bytes.NewReader([]byte{0xa0, 0x1, 0xff, 0xff})
|
||||||
|
_, err := ParseStreamFrame(b, versionBigEndian)
|
||||||
|
Expect(err).To(MatchError(io.EOF))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("rejects frames that overflow the offset", func() {
|
||||||
|
// Offset + len(Data) overflows MaxByteCount
|
||||||
|
f := &StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
Offset: protocol.MaxByteCount,
|
||||||
|
Data: []byte{'f'},
|
||||||
|
}
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := f.Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
_, err = ParseStreamFrame(bytes.NewReader(b.Bytes()), versionBigEndian)
|
||||||
|
Expect(err).To(MatchError(qerr.Error(qerr.InvalidStreamData, "data overflows maximum offset")))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("when writing", func() {
|
||||||
|
Context("in big endian", func() {
|
||||||
|
It("writes sample frame", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := (&StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
DataLenPresent: true,
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()).To(Equal([]byte{0x80 ^ 0x20,
|
||||||
|
0x1, // stream id
|
||||||
|
0x0, 0x6, // data length
|
||||||
|
'f', 'o', 'o', 'b', 'a', 'r',
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
It("sets the FinBit", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := (&StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
FinBit: true,
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()[0] & 0x40).To(Equal(byte(0x40)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("errors when length is zero and FIN is not set", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := (&StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(err).To(MatchError("StreamFrame: attempting to write empty frame without FIN"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("has proper min length for a short StreamID and a short offset", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
f := &StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
Data: []byte{},
|
||||||
|
Offset: 0,
|
||||||
|
FinBit: true,
|
||||||
|
}
|
||||||
|
err := f.Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(f.MinLength(0)).To(Equal(protocol.ByteCount(b.Len())))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("has proper min length for a long StreamID and a big offset", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
f := &StreamFrame{
|
||||||
|
StreamID: 0xdecafbad,
|
||||||
|
Data: []byte{},
|
||||||
|
Offset: 0xdeadbeefcafe,
|
||||||
|
FinBit: true,
|
||||||
|
}
|
||||||
|
err := f.Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(f.MinLength(versionBigEndian)).To(Equal(protocol.ByteCount(b.Len())))
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("data length field", func() {
|
||||||
|
It("writes the data length", func() {
|
||||||
|
dataLen := 0x1337
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
f := &StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
Data: bytes.Repeat([]byte{'f'}, dataLen),
|
||||||
|
DataLenPresent: true,
|
||||||
|
Offset: 0,
|
||||||
|
}
|
||||||
|
err := f.Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
minLength, _ := f.MinLength(0)
|
||||||
|
Expect(b.Bytes()[0] & 0x20).To(Equal(uint8(0x20)))
|
||||||
|
Expect(b.Bytes()[minLength-2 : minLength]).To(Equal([]byte{0x13, 0x37}))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
It("omits the data length field", func() {
|
||||||
|
dataLen := 0x1337
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
f := &StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
Data: bytes.Repeat([]byte{'f'}, dataLen),
|
||||||
|
DataLenPresent: false,
|
||||||
|
Offset: 0,
|
||||||
|
}
|
||||||
|
err := f.Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()[0] & 0x20).To(Equal(uint8(0)))
|
||||||
|
Expect(b.Bytes()[1 : b.Len()-dataLen]).ToNot(ContainSubstring(string([]byte{0x37, 0x13})))
|
||||||
|
minLength, _ := f.MinLength(versionBigEndian)
|
||||||
|
f.DataLenPresent = true
|
||||||
|
minLengthWithoutDataLen, _ := f.MinLength(versionBigEndian)
|
||||||
|
Expect(minLength).To(Equal(minLengthWithoutDataLen - 2))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("calculates the correct min-length", func() {
|
||||||
|
f := &StreamFrame{
|
||||||
|
StreamID: 0xcafe,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
DataLenPresent: false,
|
||||||
|
Offset: 0xdeadbeef,
|
||||||
|
}
|
||||||
|
minLengthWithoutDataLen, _ := f.MinLength(versionBigEndian)
|
||||||
|
f.DataLenPresent = true
|
||||||
|
Expect(f.MinLength(versionBigEndian)).To(Equal(minLengthWithoutDataLen + 2))
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("offset lengths", func() {
|
||||||
|
It("does not write an offset if the offset is 0", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := (&StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
Offset: 0,
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x0)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes a 2-byte offset if the offset is larger than 0", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := (&StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
Offset: 0x1337,
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x1 << 2)))
|
||||||
|
Expect(b.Bytes()[2:4]).To(Equal([]byte{0x13, 0x37}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes a 3-byte offset if the offset", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
(&StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
Offset: 0x13cafe,
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x2 << 2)))
|
||||||
|
Expect(b.Bytes()[2:5]).To(Equal([]byte{0x13, 0xca, 0xfe}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes a 4-byte offset if the offset", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := (&StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
Offset: 0xdeadbeef,
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x3 << 2)))
|
||||||
|
Expect(b.Bytes()[2:6]).To(Equal([]byte{0xde, 0xad, 0xbe, 0xef}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes a 5-byte offset if the offset", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := (&StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
Offset: 0x13deadbeef,
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x4 << 2)))
|
||||||
|
Expect(b.Bytes()[2:7]).To(Equal([]byte{0x13, 0xde, 0xad, 0xbe, 0xef}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes a 6-byte offset if the offset", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := (&StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
Offset: 0xdeadbeefcafe,
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x5 << 2)))
|
||||||
|
Expect(b.Bytes()[2:8]).To(Equal([]byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes a 7-byte offset if the offset", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := (&StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
Offset: 0x13deadbeefcafe,
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x6 << 2)))
|
||||||
|
Expect(b.Bytes()[2:9]).To(Equal([]byte{0x13, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes a 8-byte offset if the offset", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := (&StreamFrame{
|
||||||
|
StreamID: 1,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
Offset: 0x1337deadbeefcafe,
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x7 << 2)))
|
||||||
|
Expect(b.Bytes()[2:10]).To(Equal([]byte{0x13, 0x37, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe}))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("lengths of StreamIDs", func() {
|
||||||
|
It("writes a 1 byte StreamID", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := (&StreamFrame{
|
||||||
|
StreamID: 13,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x0)))
|
||||||
|
Expect(b.Bytes()[1]).To(Equal(uint8(13)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes a 2 byte StreamID", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := (&StreamFrame{
|
||||||
|
StreamID: 0xcafe,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x1)))
|
||||||
|
Expect(b.Bytes()[1:3]).To(Equal([]byte{0xca, 0xfe}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes a 3 byte StreamID", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := (&StreamFrame{
|
||||||
|
StreamID: 0x13beef,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x2)))
|
||||||
|
Expect(b.Bytes()[1:4]).To(Equal([]byte{0x13, 0xbe, 0xef}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes a 4 byte StreamID", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := (&StreamFrame{
|
||||||
|
StreamID: 0xdecafbad,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
}).Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x3)))
|
||||||
|
Expect(b.Bytes()[1:5]).To(Equal([]byte{0xde, 0xca, 0xfb, 0xad}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes a multiple byte StreamID, after the Stream length was already determined by MinLenght()", func() {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
frame := &StreamFrame{
|
||||||
|
StreamID: 0xdecafbad,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
}
|
||||||
|
frame.MinLength(0)
|
||||||
|
err := frame.Write(b, versionBigEndian)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x3)))
|
||||||
|
Expect(b.Bytes()[1:5]).To(Equal([]byte{0xde, 0xca, 0xfb, 0xad}))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("shortening of StreamIDs", func() {
|
||||||
|
It("determines the length of a 1 byte StreamID", func() {
|
||||||
|
f := &StreamFrame{StreamID: 0xFF}
|
||||||
|
Expect(f.calculateStreamIDLength()).To(Equal(uint8(1)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("determines the length of a 2 byte StreamID", func() {
|
||||||
|
f := &StreamFrame{StreamID: 0xFFFF}
|
||||||
|
Expect(f.calculateStreamIDLength()).To(Equal(uint8(2)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("determines the length of a 3 byte StreamID", func() {
|
||||||
|
f := &StreamFrame{StreamID: 0xFFFFFF}
|
||||||
|
Expect(f.calculateStreamIDLength()).To(Equal(uint8(3)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("determines the length of a 4 byte StreamID", func() {
|
||||||
|
f := &StreamFrame{StreamID: 0xFFFFFFFF}
|
||||||
|
Expect(f.calculateStreamIDLength()).To(Equal(uint8(4)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("shortening of Offsets", func() {
|
||||||
|
It("determines length 0 of offset 0", func() {
|
||||||
|
f := &StreamFrame{Offset: 0}
|
||||||
|
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(0)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("determines the length of a 2 byte offset", func() {
|
||||||
|
f := &StreamFrame{Offset: 0xFFFF}
|
||||||
|
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(2)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("determines the length of a 2 byte offset, even if it would fit into 1 byte", func() {
|
||||||
|
f := &StreamFrame{Offset: 0x1}
|
||||||
|
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(2)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("determines the length of a 3 byte offset", func() {
|
||||||
|
f := &StreamFrame{Offset: 0xFFFFFF}
|
||||||
|
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(3)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("determines the length of a 4 byte offset", func() {
|
||||||
|
f := &StreamFrame{Offset: 0xFFFFFFFF}
|
||||||
|
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(4)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("determines the length of a 5 byte offset", func() {
|
||||||
|
f := &StreamFrame{Offset: 0xFFFFFFFFFF}
|
||||||
|
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(5)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("determines the length of a 6 byte offset", func() {
|
||||||
|
f := &StreamFrame{Offset: 0xFFFFFFFFFFFF}
|
||||||
|
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(6)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("determines the length of a 7 byte offset", func() {
|
||||||
|
f := &StreamFrame{Offset: 0xFFFFFFFFFFFFFF}
|
||||||
|
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(7)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("determines the length of an 8 byte offset", func() {
|
||||||
|
f := &StreamFrame{Offset: 0xFFFFFFFFFFFFFFFF}
|
||||||
|
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(8)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("DataLen", func() {
|
||||||
|
It("determines the length of the data", func() {
|
||||||
|
frame := StreamFrame{
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
}
|
||||||
|
Expect(frame.DataLen()).To(Equal(protocol.ByteCount(6)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -2,490 +2,210 @@ package wire
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
"github.com/lucas-clemente/quic-go/qerr"
|
"github.com/lucas-clemente/quic-go/qerr"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("StreamFrame", func() {
|
var _ = Describe("STREAM frame (for IETF QUIC)", func() {
|
||||||
Context("when parsing", func() {
|
Context("when parsing", func() {
|
||||||
Context("in big endian", func() {
|
It("parses a frame with OFF bit", func() {
|
||||||
It("accepts a sample frame", func() {
|
data := []byte{0x10 ^ 0x4}
|
||||||
// a STREAM frame, plus 3 additional bytes, not belonging to this frame
|
data = append(data, encodeVarInt(0x12345)...) // stream ID
|
||||||
b := bytes.NewReader([]byte{0x80 ^ 0x20,
|
data = append(data, encodeVarInt(0xdecafbad)...) // offset
|
||||||
0x1, // stream id
|
data = append(data, []byte("foobar")...)
|
||||||
0x0, 0x6, // data length
|
r := bytes.NewReader(data)
|
||||||
'f', 'o', 'o', 'b', 'a', 'r',
|
frame, err := ParseStreamFrame(r, versionIETFFrames)
|
||||||
'f', 'o', 'o', // additional bytes
|
|
||||||
})
|
|
||||||
frame, err := ParseStreamFrame(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(frame.FinBit).To(BeFalse())
|
|
||||||
Expect(frame.StreamID).To(Equal(protocol.StreamID(1)))
|
|
||||||
Expect(frame.Offset).To(BeZero())
|
|
||||||
Expect(frame.DataLenPresent).To(BeTrue())
|
|
||||||
Expect(frame.Data).To(Equal([]byte("foobar")))
|
|
||||||
Expect(b.Len()).To(Equal(3))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("accepts frames with offsets", func() {
|
|
||||||
b := bytes.NewReader([]byte{0x80 ^ 0x20 /* 2 byte offset */ ^ 0x4,
|
|
||||||
0x1, // stream id
|
|
||||||
0x0, 0x42, // offset
|
|
||||||
0x0, 0x6, // data length
|
|
||||||
'f', 'o', 'o', 'b', 'a', 'r',
|
|
||||||
})
|
|
||||||
frame, err := ParseStreamFrame(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(frame.FinBit).To(BeFalse())
|
|
||||||
Expect(frame.StreamID).To(Equal(protocol.StreamID(1)))
|
|
||||||
Expect(frame.Offset).To(Equal(protocol.ByteCount(0x42)))
|
|
||||||
Expect(frame.DataLenPresent).To(BeTrue())
|
|
||||||
Expect(frame.Data).To(Equal([]byte("foobar")))
|
|
||||||
Expect(b.Len()).To(BeZero())
|
|
||||||
})
|
|
||||||
|
|
||||||
It("errors on EOFs", func() {
|
|
||||||
data := []byte{0x80 ^ 0x20 ^ 0x4,
|
|
||||||
0x1, // stream id
|
|
||||||
0x0, 0x2a, // offset
|
|
||||||
0x0, 0x6, // data length,
|
|
||||||
'f', 'o', 'o', 'b', 'a', 'r',
|
|
||||||
}
|
|
||||||
_, err := ParseStreamFrame(bytes.NewReader(data), versionBigEndian)
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
for i := range data {
|
|
||||||
_, err := ParseStreamFrame(bytes.NewReader(data[0:i]), versionBigEndian)
|
|
||||||
Expect(err).To(HaveOccurred())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
It("accepts frame without data length", func() {
|
|
||||||
b := bytes.NewReader([]byte{0x80,
|
|
||||||
0x1, // stream id
|
|
||||||
'f', 'o', 'o', 'b', 'a', 'r',
|
|
||||||
})
|
|
||||||
frame, err := ParseStreamFrame(b, protocol.VersionWhatever)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(frame.StreamID).To(Equal(protocol.StreamID(0x12345)))
|
||||||
|
Expect(frame.Data).To(Equal([]byte("foobar")))
|
||||||
|
Expect(frame.FinBit).To(BeFalse())
|
||||||
|
Expect(frame.Offset).To(Equal(protocol.ByteCount(0xdecafbad)))
|
||||||
|
Expect(r.Len()).To(BeZero())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("respects the LEN when parsing the frame", func() {
|
||||||
|
data := []byte{0x10 ^ 0x2}
|
||||||
|
data = append(data, encodeVarInt(0x12345)...) // stream ID
|
||||||
|
data = append(data, encodeVarInt(4)...) // data length
|
||||||
|
data = append(data, []byte("foobar")...)
|
||||||
|
r := bytes.NewReader(data)
|
||||||
|
frame, err := ParseStreamFrame(r, versionIETFFrames)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(frame.StreamID).To(Equal(protocol.StreamID(0x12345)))
|
||||||
|
Expect(frame.Data).To(Equal([]byte("foob")))
|
||||||
Expect(frame.FinBit).To(BeFalse())
|
Expect(frame.FinBit).To(BeFalse())
|
||||||
Expect(frame.StreamID).To(Equal(protocol.StreamID(1)))
|
|
||||||
Expect(frame.Offset).To(BeZero())
|
Expect(frame.Offset).To(BeZero())
|
||||||
Expect(frame.DataLenPresent).To(BeFalse())
|
Expect(r.Len()).To(Equal(2))
|
||||||
Expect(frame.Data).To(Equal([]byte("foobar")))
|
|
||||||
Expect(b.Len()).To(BeZero())
|
|
||||||
})
|
})
|
||||||
|
|
||||||
It("accepts an empty frame with FinBit set, with data length set", func() {
|
It("parses a frame with FIN bit", func() {
|
||||||
// the STREAM frame, plus 3 additional bytes, not belonging to this frame
|
data := []byte{0x10 ^ 0x1}
|
||||||
b := bytes.NewReader([]byte{0x80 ^ 0x40 ^ 0x20,
|
data = append(data, encodeVarInt(9)...) // stream ID
|
||||||
0x1, // stream id
|
data = append(data, []byte("foobar")...)
|
||||||
0, 0, // data length
|
r := bytes.NewReader(data)
|
||||||
'f', 'o', 'o', // additional bytes
|
frame, err := ParseStreamFrame(r, versionIETFFrames)
|
||||||
})
|
|
||||||
frame, err := ParseStreamFrame(b, protocol.VersionWhatever)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(frame.FinBit).To(BeTrue())
|
Expect(frame.StreamID).To(Equal(protocol.StreamID(9)))
|
||||||
Expect(frame.DataLenPresent).To(BeTrue())
|
|
||||||
Expect(frame.Data).To(BeEmpty())
|
|
||||||
Expect(b.Len()).To(Equal(3))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("accepts an empty frame with the FinBit set", func() {
|
|
||||||
b := bytes.NewReader([]byte{0x80 ^ 0x40,
|
|
||||||
0x1, // stream id
|
|
||||||
'f', 'o', 'o', 'b', 'a', 'r',
|
|
||||||
})
|
|
||||||
frame, err := ParseStreamFrame(b, protocol.VersionWhatever)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(frame.FinBit).To(BeTrue())
|
|
||||||
Expect(frame.DataLenPresent).To(BeFalse())
|
|
||||||
Expect(frame.Data).To(Equal([]byte("foobar")))
|
Expect(frame.Data).To(Equal([]byte("foobar")))
|
||||||
Expect(b.Len()).To(BeZero())
|
Expect(frame.FinBit).To(BeTrue())
|
||||||
|
Expect(frame.Offset).To(BeZero())
|
||||||
|
Expect(r.Len()).To(BeZero())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("errors on empty stream frames that don't have the FinBit set", func() {
|
It("rejects empty frames than don't have the FIN bit set", func() {
|
||||||
b := bytes.NewReader([]byte{0x80 ^ 0x20,
|
data := []byte{0x10}
|
||||||
0x1, // stream id
|
data = append(data, encodeVarInt(0x1337)...) // stream ID
|
||||||
0, 0, // data length
|
r := bytes.NewReader(data)
|
||||||
})
|
_, err := ParseStreamFrame(r, versionIETFFrames)
|
||||||
_, err := ParseStreamFrame(b, protocol.VersionWhatever)
|
|
||||||
Expect(err).To(MatchError(qerr.EmptyStreamFrameNoFin))
|
Expect(err).To(MatchError(qerr.EmptyStreamFrameNoFin))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("rejects frames to too large dataLen", func() {
|
It("rejects frames that overflow the maximum offset", func() {
|
||||||
b := bytes.NewReader([]byte{0xa0, 0x1, 0xff, 0xff})
|
data := []byte{0x10 ^ 0x4}
|
||||||
_, err := ParseStreamFrame(b, protocol.VersionWhatever)
|
data = append(data, encodeVarInt(0x12345)...) // stream ID
|
||||||
Expect(err).To(MatchError(io.EOF))
|
data = append(data, encodeVarInt(uint64(protocol.MaxByteCount-5))...) // offset
|
||||||
|
data = append(data, []byte("foobar")...)
|
||||||
|
r := bytes.NewReader(data)
|
||||||
|
_, err := ParseStreamFrame(r, versionIETFFrames)
|
||||||
|
Expect(err).To(MatchError(qerr.Error(qerr.InvalidStreamData, "data overflows maximum offset")))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("rejects frames that overflow the offset", func() {
|
It("errors on EOFs", func() {
|
||||||
// Offset + len(Data) overflows MaxByteCount
|
data := []byte{0x10 ^ 0x4 ^ 0x2}
|
||||||
f := &StreamFrame{
|
data = append(data, encodeVarInt(0x12345)...) // stream ID
|
||||||
StreamID: 1,
|
data = append(data, encodeVarInt(0xdecafbad)...) // offset
|
||||||
Offset: protocol.MaxByteCount,
|
data = append(data, encodeVarInt(6)...) // data length
|
||||||
Data: []byte{'f'},
|
data = append(data, []byte("foobar")...)
|
||||||
|
_, err := ParseStreamFrame(bytes.NewReader(data), versionIETFFrames)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
for i := range data {
|
||||||
|
_, err := ParseStreamFrame(bytes.NewReader(data[0:i]), versionIETFFrames)
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
}
|
}
|
||||||
b := &bytes.Buffer{}
|
|
||||||
err := f.Write(b, protocol.VersionWhatever)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
_, err = ParseStreamFrame(bytes.NewReader(b.Bytes()), protocol.VersionWhatever)
|
|
||||||
Expect(err).To(MatchError(qerr.Error(qerr.InvalidStreamData, "data overflows maximum offset")))
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("when writing", func() {
|
Context("when writing", func() {
|
||||||
Context("in big endian", func() {
|
It("writes a frame without offset", func() {
|
||||||
It("writes sample frame", func() {
|
f := &StreamFrame{
|
||||||
b := &bytes.Buffer{}
|
StreamID: 0x1337,
|
||||||
err := (&StreamFrame{
|
|
||||||
StreamID: 1,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
DataLenPresent: true,
|
|
||||||
}).Write(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(b.Bytes()).To(Equal([]byte{0x80 ^ 0x20,
|
|
||||||
0x1, // stream id
|
|
||||||
0x0, 0x6, // data length
|
|
||||||
'f', 'o', 'o', 'b', 'a', 'r',
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
It("sets the FinBit", func() {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
err := (&StreamFrame{
|
|
||||||
StreamID: 1,
|
|
||||||
Data: []byte("foobar"),
|
Data: []byte("foobar"),
|
||||||
FinBit: true,
|
}
|
||||||
}).Write(b, protocol.VersionWhatever)
|
b := &bytes.Buffer{}
|
||||||
|
err := f.Write(b, versionIETFFrames)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(b.Bytes()[0] & 0x40).To(Equal(byte(0x40)))
|
expected := []byte{0x10}
|
||||||
|
expected = append(expected, encodeVarInt(0x1337)...) // stream ID
|
||||||
|
expected = append(expected, []byte("foobar")...)
|
||||||
|
Expect(b.Bytes()).To(Equal(expected))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("errors when length is zero and FIN is not set", func() {
|
It("writes a frame with offset", func() {
|
||||||
|
f := &StreamFrame{
|
||||||
|
StreamID: 0x1337,
|
||||||
|
Offset: 0x123456,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
}
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
err := (&StreamFrame{
|
err := f.Write(b, versionIETFFrames)
|
||||||
StreamID: 1,
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}).Write(b, protocol.VersionWhatever)
|
expected := []byte{0x10 ^ 0x4}
|
||||||
|
expected = append(expected, encodeVarInt(0x1337)...) // stream ID
|
||||||
|
expected = append(expected, encodeVarInt(0x123456)...) // offset
|
||||||
|
expected = append(expected, []byte("foobar")...)
|
||||||
|
Expect(b.Bytes()).To(Equal(expected))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes a frame with FIN bit", func() {
|
||||||
|
f := &StreamFrame{
|
||||||
|
StreamID: 0x1337,
|
||||||
|
Offset: 0x123456,
|
||||||
|
FinBit: true,
|
||||||
|
}
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := f.Write(b, versionIETFFrames)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
expected := []byte{0x10 ^ 0x4 ^ 0x1}
|
||||||
|
expected = append(expected, encodeVarInt(0x1337)...) // stream ID
|
||||||
|
expected = append(expected, encodeVarInt(0x123456)...) // offset
|
||||||
|
Expect(b.Bytes()).To(Equal(expected))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes a frame with data length", func() {
|
||||||
|
f := &StreamFrame{
|
||||||
|
StreamID: 0x1337,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
DataLenPresent: true,
|
||||||
|
}
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := f.Write(b, versionIETFFrames)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
expected := []byte{0x10 ^ 0x2}
|
||||||
|
expected = append(expected, encodeVarInt(0x1337)...) // stream ID
|
||||||
|
expected = append(expected, encodeVarInt(6)...) // data length
|
||||||
|
expected = append(expected, []byte("foobar")...)
|
||||||
|
Expect(b.Bytes()).To(Equal(expected))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes a frame with data length and offset", func() {
|
||||||
|
f := &StreamFrame{
|
||||||
|
StreamID: 0x1337,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
DataLenPresent: true,
|
||||||
|
Offset: 0x123456,
|
||||||
|
}
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := f.Write(b, versionIETFFrames)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
expected := []byte{0x10 ^ 0x4 ^ 0x2}
|
||||||
|
expected = append(expected, encodeVarInt(0x1337)...) // stream ID
|
||||||
|
expected = append(expected, encodeVarInt(0x123456)...) // offset
|
||||||
|
expected = append(expected, encodeVarInt(6)...) // data length
|
||||||
|
expected = append(expected, []byte("foobar")...)
|
||||||
|
Expect(b.Bytes()).To(Equal(expected))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("refuses to write an empty frame without FIN", func() {
|
||||||
|
f := &StreamFrame{
|
||||||
|
StreamID: 0x42,
|
||||||
|
Offset: 0x1337,
|
||||||
|
}
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err := f.Write(b, versionIETFFrames)
|
||||||
Expect(err).To(MatchError("StreamFrame: attempting to write empty frame without FIN"))
|
Expect(err).To(MatchError("StreamFrame: attempting to write empty frame without FIN"))
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
It("has proper min length for a short StreamID and a short offset", func() {
|
Context("length", func() {
|
||||||
b := &bytes.Buffer{}
|
It("has the right length for a frame without offset and data length", func() {
|
||||||
f := &StreamFrame{
|
f := &StreamFrame{
|
||||||
StreamID: 1,
|
StreamID: 0x1337,
|
||||||
Data: []byte{},
|
Data: []byte("foobar"),
|
||||||
Offset: 0,
|
|
||||||
FinBit: true,
|
|
||||||
}
|
}
|
||||||
err := f.Write(b, protocol.VersionWhatever)
|
Expect(f.MinLength(versionIETFFrames)).To(Equal(1 + utils.VarIntLen(0x1337)))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(f.MinLength(0)).To(Equal(protocol.ByteCount(b.Len())))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
It("has proper min length for a long StreamID and a big offset", func() {
|
It("has the right length for a frame with offset", func() {
|
||||||
b := &bytes.Buffer{}
|
|
||||||
f := &StreamFrame{
|
f := &StreamFrame{
|
||||||
StreamID: 0xdecafbad,
|
StreamID: 0x1337,
|
||||||
Data: []byte{},
|
Offset: 0x42,
|
||||||
Offset: 0xdeadbeefcafe,
|
Data: []byte("foobar"),
|
||||||
FinBit: true,
|
|
||||||
}
|
}
|
||||||
err := f.Write(b, protocol.VersionWhatever)
|
Expect(f.MinLength(versionIETFFrames)).To(Equal(1 + utils.VarIntLen(0x1337) + utils.VarIntLen(0x42)))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(f.MinLength(0)).To(Equal(protocol.ByteCount(b.Len())))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("data length field", func() {
|
It("has the right length for a frame with data length", func() {
|
||||||
Context("in big endian", func() {
|
f := &StreamFrame{
|
||||||
It("writes the data length", func() {
|
StreamID: 0x1337,
|
||||||
dataLen := 0x1337
|
Offset: 0x1234567,
|
||||||
b := &bytes.Buffer{}
|
DataLenPresent: true,
|
||||||
f := &StreamFrame{
|
Data: []byte("foobar"),
|
||||||
StreamID: 1,
|
|
||||||
Data: bytes.Repeat([]byte{'f'}, dataLen),
|
|
||||||
DataLenPresent: true,
|
|
||||||
Offset: 0,
|
|
||||||
}
|
|
||||||
err := f.Write(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
minLength, _ := f.MinLength(0)
|
|
||||||
Expect(b.Bytes()[0] & 0x20).To(Equal(uint8(0x20)))
|
|
||||||
Expect(b.Bytes()[minLength-2 : minLength]).To(Equal([]byte{0x13, 0x37}))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
It("omits the data length field", func() {
|
|
||||||
dataLen := 0x1337
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
f := &StreamFrame{
|
|
||||||
StreamID: 1,
|
|
||||||
Data: bytes.Repeat([]byte{'f'}, dataLen),
|
|
||||||
DataLenPresent: false,
|
|
||||||
Offset: 0,
|
|
||||||
}
|
|
||||||
err := f.Write(b, protocol.VersionWhatever)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(b.Bytes()[0] & 0x20).To(Equal(uint8(0)))
|
|
||||||
Expect(b.Bytes()[1 : b.Len()-dataLen]).ToNot(ContainSubstring(string([]byte{0x37, 0x13})))
|
|
||||||
minLength, _ := f.MinLength(0)
|
|
||||||
f.DataLenPresent = true
|
|
||||||
minLengthWithoutDataLen, _ := f.MinLength(0)
|
|
||||||
Expect(minLength).To(Equal(minLengthWithoutDataLen - 2))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("calculates the correcct min-length", func() {
|
|
||||||
f := &StreamFrame{
|
|
||||||
StreamID: 0xCAFE,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
DataLenPresent: false,
|
|
||||||
Offset: 0xDEADBEEF,
|
|
||||||
}
|
|
||||||
minLengthWithoutDataLen, _ := f.MinLength(0)
|
|
||||||
f.DataLenPresent = true
|
|
||||||
Expect(f.MinLength(0)).To(Equal(minLengthWithoutDataLen + 2))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("offset lengths", func() {
|
|
||||||
Context("in big endian", func() {
|
|
||||||
It("does not write an offset if the offset is 0", func() {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
err := (&StreamFrame{
|
|
||||||
StreamID: 1,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
Offset: 0,
|
|
||||||
}).Write(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x0)))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("writes a 2-byte offset if the offset is larger than 0", func() {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
err := (&StreamFrame{
|
|
||||||
StreamID: 1,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
Offset: 0x1337,
|
|
||||||
}).Write(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x1 << 2)))
|
|
||||||
Expect(b.Bytes()[2:4]).To(Equal([]byte{0x13, 0x37}))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("writes a 3-byte offset if the offset", func() {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
(&StreamFrame{
|
|
||||||
StreamID: 1,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
Offset: 0x13cafe,
|
|
||||||
}).Write(b, versionBigEndian)
|
|
||||||
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x2 << 2)))
|
|
||||||
Expect(b.Bytes()[2:5]).To(Equal([]byte{0x13, 0xca, 0xfe}))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("writes a 4-byte offset if the offset", func() {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
err := (&StreamFrame{
|
|
||||||
StreamID: 1,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
Offset: 0xdeadbeef,
|
|
||||||
}).Write(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x3 << 2)))
|
|
||||||
Expect(b.Bytes()[2:6]).To(Equal([]byte{0xde, 0xad, 0xbe, 0xef}))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("writes a 5-byte offset if the offset", func() {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
err := (&StreamFrame{
|
|
||||||
StreamID: 1,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
Offset: 0x13deadbeef,
|
|
||||||
}).Write(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x4 << 2)))
|
|
||||||
Expect(b.Bytes()[2:7]).To(Equal([]byte{0x13, 0xde, 0xad, 0xbe, 0xef}))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("writes a 6-byte offset if the offset", func() {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
err := (&StreamFrame{
|
|
||||||
StreamID: 1,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
Offset: 0xdeadbeefcafe,
|
|
||||||
}).Write(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x5 << 2)))
|
|
||||||
Expect(b.Bytes()[2:8]).To(Equal([]byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe}))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("writes a 7-byte offset if the offset", func() {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
err := (&StreamFrame{
|
|
||||||
StreamID: 1,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
Offset: 0x13deadbeefcafe,
|
|
||||||
}).Write(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x6 << 2)))
|
|
||||||
Expect(b.Bytes()[2:9]).To(Equal([]byte{0x13, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe}))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("writes a 8-byte offset if the offset", func() {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
err := (&StreamFrame{
|
|
||||||
StreamID: 1,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
Offset: 0x1337deadbeefcafe,
|
|
||||||
}).Write(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x7 << 2)))
|
|
||||||
Expect(b.Bytes()[2:10]).To(Equal([]byte{0x13, 0x37, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe}))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("lengths of StreamIDs", func() {
|
|
||||||
Context("in big endian", func() {
|
|
||||||
It("writes a 1 byte StreamID", func() {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
err := (&StreamFrame{
|
|
||||||
StreamID: 13,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
}).Write(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x0)))
|
|
||||||
Expect(b.Bytes()[1]).To(Equal(uint8(13)))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("writes a 2 byte StreamID", func() {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
err := (&StreamFrame{
|
|
||||||
StreamID: 0xcafe,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
}).Write(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x1)))
|
|
||||||
Expect(b.Bytes()[1:3]).To(Equal([]byte{0xca, 0xfe}))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("writes a 3 byte StreamID", func() {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
err := (&StreamFrame{
|
|
||||||
StreamID: 0x13beef,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
}).Write(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x2)))
|
|
||||||
Expect(b.Bytes()[1:4]).To(Equal([]byte{0x13, 0xbe, 0xef}))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("writes a 4 byte StreamID", func() {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
err := (&StreamFrame{
|
|
||||||
StreamID: 0xdecafbad,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
}).Write(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x3)))
|
|
||||||
Expect(b.Bytes()[1:5]).To(Equal([]byte{0xde, 0xca, 0xfb, 0xad}))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("writes a multiple byte StreamID, after the Stream length was already determined by MinLenght()", func() {
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
frame := &StreamFrame{
|
|
||||||
StreamID: 0xdecafbad,
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
}
|
|
||||||
frame.MinLength(0)
|
|
||||||
err := frame.Write(b, versionBigEndian)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x3)))
|
|
||||||
Expect(b.Bytes()[1:5]).To(Equal([]byte{0xde, 0xca, 0xfb, 0xad}))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("shortening of StreamIDs", func() {
|
|
||||||
It("determines the length of a 1 byte StreamID", func() {
|
|
||||||
f := &StreamFrame{StreamID: 0xFF}
|
|
||||||
Expect(f.calculateStreamIDLength()).To(Equal(uint8(1)))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("determines the length of a 2 byte StreamID", func() {
|
|
||||||
f := &StreamFrame{StreamID: 0xFFFF}
|
|
||||||
Expect(f.calculateStreamIDLength()).To(Equal(uint8(2)))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("determines the length of a 3 byte StreamID", func() {
|
|
||||||
f := &StreamFrame{StreamID: 0xFFFFFF}
|
|
||||||
Expect(f.calculateStreamIDLength()).To(Equal(uint8(3)))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("determines the length of a 4 byte StreamID", func() {
|
|
||||||
f := &StreamFrame{StreamID: 0xFFFFFFFF}
|
|
||||||
Expect(f.calculateStreamIDLength()).To(Equal(uint8(4)))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("shortening of Offsets", func() {
|
|
||||||
It("determines length 0 of offset 0", func() {
|
|
||||||
f := &StreamFrame{Offset: 0}
|
|
||||||
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(0)))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("determines the length of a 2 byte offset", func() {
|
|
||||||
f := &StreamFrame{Offset: 0xFFFF}
|
|
||||||
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(2)))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("determines the length of a 2 byte offset, even if it would fit into 1 byte", func() {
|
|
||||||
f := &StreamFrame{Offset: 0x1}
|
|
||||||
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(2)))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("determines the length of a 3 byte offset", func() {
|
|
||||||
f := &StreamFrame{Offset: 0xFFFFFF}
|
|
||||||
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(3)))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("determines the length of a 4 byte offset", func() {
|
|
||||||
f := &StreamFrame{Offset: 0xFFFFFFFF}
|
|
||||||
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(4)))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("determines the length of a 5 byte offset", func() {
|
|
||||||
f := &StreamFrame{Offset: 0xFFFFFFFFFF}
|
|
||||||
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(5)))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("determines the length of a 6 byte offset", func() {
|
|
||||||
f := &StreamFrame{Offset: 0xFFFFFFFFFFFF}
|
|
||||||
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(6)))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("determines the length of a 7 byte offset", func() {
|
|
||||||
f := &StreamFrame{Offset: 0xFFFFFFFFFFFFFF}
|
|
||||||
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(7)))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("determines the length of an 8 byte offset", func() {
|
|
||||||
f := &StreamFrame{Offset: 0xFFFFFFFFFFFFFFFF}
|
|
||||||
Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(8)))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("DataLen", func() {
|
|
||||||
It("determines the length of the data", func() {
|
|
||||||
frame := StreamFrame{
|
|
||||||
Data: []byte("foobar"),
|
|
||||||
}
|
}
|
||||||
Expect(frame.DataLen()).To(Equal(protocol.ByteCount(6)))
|
Expect(f.MinLength(versionIETFFrames)).To(Equal(1 + utils.VarIntLen(0x1337) + utils.VarIntLen(0x1234567) + utils.VarIntLen(6)))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package wire
|
package wire
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
|
@ -21,6 +23,12 @@ const (
|
||||||
versionIETFFrames = protocol.VersionTLS
|
versionIETFFrames = protocol.VersionTLS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func encodeVarInt(i uint64) []byte {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
utils.WriteVarInt(b, i)
|
||||||
|
return b.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
var _ = BeforeSuite(func() {
|
var _ = BeforeSuite(func() {
|
||||||
Expect(utils.GetByteOrder(versionBigEndian)).To(Equal(utils.BigEndian))
|
Expect(utils.GetByteOrder(versionBigEndian)).To(Equal(utils.BigEndian))
|
||||||
Expect(utils.GetByteOrder(versionIETFFrames)).To(Equal(utils.BigEndian))
|
Expect(utils.GetByteOrder(versionIETFFrames)).To(Equal(utils.BigEndian))
|
||||||
|
|
|
@ -236,10 +236,16 @@ func (p *packetPacker) composeNextPacket(
|
||||||
return payloadFrames, nil
|
return payloadFrames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// temporarily increase the maxFrameSize by 2 bytes
|
// temporarily increase the maxFrameSize by the (minimum) length of the DataLen field
|
||||||
// this leads to a properly sized packet in all cases, since we do all the packet length calculations with StreamFrames that have the DataLen set
|
// this leads to a properly sized packet in all cases, since we do all the packet length calculations with StreamFrames that have the DataLen set
|
||||||
// however, for the last StreamFrame in the packet, we can omit the DataLen, thus saving 2 bytes and yielding a packet of exactly the correct size
|
// however, for the last StreamFrame in the packet, we can omit the DataLen, thus yielding a packet of exactly the correct size
|
||||||
maxFrameSize += 2
|
// for gQUIC STREAM frames, DataLen is always 2 bytes
|
||||||
|
// for IETF draft style STREAM frames, the length is encoded to either 1 or 2 bytes
|
||||||
|
if p.version.UsesIETFFrameFormat() {
|
||||||
|
maxFrameSize++
|
||||||
|
} else {
|
||||||
|
maxFrameSize += 2
|
||||||
|
}
|
||||||
|
|
||||||
fs := p.streamFramer.PopStreamFrames(maxFrameSize - payloadLength)
|
fs := p.streamFramer.PopStreamFrames(maxFrameSize - payloadLength)
|
||||||
if len(fs) != 0 {
|
if len(fs) != 0 {
|
||||||
|
|
|
@ -60,9 +60,10 @@ var _ = Describe("Packet packer", func() {
|
||||||
)
|
)
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
cryptoStream = &stream{flowController: flowcontrol.NewStreamFlowController(1, false, flowcontrol.NewConnectionFlowController(1000, 1000, nil), 1000, 1000, 1000, nil)}
|
version := versionGQUICFrames
|
||||||
streamsMap := newStreamsMap(nil, protocol.PerspectiveServer, protocol.VersionWhatever)
|
cryptoStream = &stream{streamID: version.CryptoStreamID(), flowController: flowcontrol.NewStreamFlowController(version.CryptoStreamID(), false, flowcontrol.NewConnectionFlowController(1000, 1000, nil), 1000, 1000, 1000, nil)}
|
||||||
streamFramer = newStreamFramer(cryptoStream, streamsMap, nil)
|
streamsMap := newStreamsMap(nil, protocol.PerspectiveServer, versionGQUICFrames)
|
||||||
|
streamFramer = newStreamFramer(cryptoStream, streamsMap, nil, versionGQUICFrames)
|
||||||
|
|
||||||
packer = &packetPacker{
|
packer = &packetPacker{
|
||||||
cryptoSetup: &mockCryptoSetup{encLevelSeal: protocol.EncryptionForwardSecure},
|
cryptoSetup: &mockCryptoSetup{encLevelSeal: protocol.EncryptionForwardSecure},
|
||||||
|
@ -73,8 +74,8 @@ var _ = Describe("Packet packer", func() {
|
||||||
}
|
}
|
||||||
publicHeaderLen = 1 + 8 + 2 // 1 flag byte, 8 connection ID, 2 packet number
|
publicHeaderLen = 1 + 8 + 2 // 1 flag byte, 8 connection ID, 2 packet number
|
||||||
maxFrameSize = protocol.MaxPacketSize - protocol.ByteCount((&mockSealer{}).Overhead()) - publicHeaderLen
|
maxFrameSize = protocol.MaxPacketSize - protocol.ByteCount((&mockSealer{}).Overhead()) - publicHeaderLen
|
||||||
packer.version = protocol.VersionWhatever
|
|
||||||
packer.hasSentPacket = true
|
packer.hasSentPacket = true
|
||||||
|
packer.version = version
|
||||||
})
|
})
|
||||||
|
|
||||||
It("returns nil when no packet is queued", func() {
|
It("returns nil when no packet is queued", func() {
|
||||||
|
@ -93,7 +94,7 @@ var _ = Describe("Packet packer", func() {
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(p).ToNot(BeNil())
|
Expect(p).ToNot(BeNil())
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
f.Write(b, 0)
|
f.Write(b, packer.version)
|
||||||
Expect(p.frames).To(HaveLen(1))
|
Expect(p.frames).To(HaveLen(1))
|
||||||
Expect(p.raw).To(ContainSubstring(string(b.Bytes())))
|
Expect(p.raw).To(ContainSubstring(string(b.Bytes())))
|
||||||
})
|
})
|
||||||
|
@ -327,7 +328,7 @@ var _ = Describe("Packet packer", func() {
|
||||||
|
|
||||||
It("packs a lot of control frames into 2 packets if they don't fit into one", func() {
|
It("packs a lot of control frames into 2 packets if they don't fit into one", func() {
|
||||||
blockedFrame := &wire.BlockedFrame{}
|
blockedFrame := &wire.BlockedFrame{}
|
||||||
minLength, _ := blockedFrame.MinLength(0)
|
minLength, _ := blockedFrame.MinLength(packer.version)
|
||||||
maxFramesPerPacket := int(maxFrameSize) / int(minLength)
|
maxFramesPerPacket := int(maxFrameSize) / int(minLength)
|
||||||
var controlFrames []wire.Frame
|
var controlFrames []wire.Frame
|
||||||
for i := 0; i < maxFramesPerPacket+10; i++ {
|
for i := 0; i < maxFramesPerPacket+10; i++ {
|
||||||
|
@ -360,14 +361,14 @@ var _ = Describe("Packet packer", func() {
|
||||||
Expect(packer.packetNumberGenerator.Peek()).To(Equal(protocol.PacketNumber(2)))
|
Expect(packer.packetNumberGenerator.Peek()).To(Equal(protocol.PacketNumber(2)))
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("Stream Frame handling", func() {
|
Context("STREAM Frame handling", func() {
|
||||||
It("does not splits a stream frame with maximum size", func() {
|
It("does not splits a STREAM frame with maximum size, for gQUIC frames", func() {
|
||||||
f := &wire.StreamFrame{
|
f := &wire.StreamFrame{
|
||||||
Offset: 1,
|
Offset: 1,
|
||||||
StreamID: 5,
|
StreamID: 5,
|
||||||
DataLenPresent: false,
|
DataLenPresent: false,
|
||||||
}
|
}
|
||||||
minLength, _ := f.MinLength(0)
|
minLength, _ := f.MinLength(packer.version)
|
||||||
maxStreamFrameDataLen := maxFrameSize - minLength
|
maxStreamFrameDataLen := maxFrameSize - minLength
|
||||||
f.Data = bytes.Repeat([]byte{'f'}, int(maxStreamFrameDataLen))
|
f.Data = bytes.Repeat([]byte{'f'}, int(maxStreamFrameDataLen))
|
||||||
streamFramer.AddFrameForRetransmission(f)
|
streamFramer.AddFrameForRetransmission(f)
|
||||||
|
@ -380,7 +381,30 @@ var _ = Describe("Packet packer", func() {
|
||||||
Expect(payloadFrames).To(BeEmpty())
|
Expect(payloadFrames).To(BeEmpty())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("correctly handles a stream frame with one byte less than maximum size", func() {
|
It("does not splits a STREAM frame with maximum size, for IETF draft style frame", func() {
|
||||||
|
packer.version = versionIETFFrames
|
||||||
|
streamFramer.version = versionIETFFrames
|
||||||
|
f := &wire.StreamFrame{
|
||||||
|
Offset: 1,
|
||||||
|
StreamID: 5,
|
||||||
|
DataLenPresent: true,
|
||||||
|
}
|
||||||
|
minLength, _ := f.MinLength(packer.version)
|
||||||
|
// for IETF draft style STREAM frames, we don't know the size of the DataLen, because it is a variable length integer
|
||||||
|
// in the general case, we therefore use a STREAM frame that is 1 byte smaller than the maximum size
|
||||||
|
maxStreamFrameDataLen := maxFrameSize - minLength - 1
|
||||||
|
f.Data = bytes.Repeat([]byte{'f'}, int(maxStreamFrameDataLen))
|
||||||
|
streamFramer.AddFrameForRetransmission(f)
|
||||||
|
payloadFrames, err := packer.composeNextPacket(maxFrameSize, true)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(payloadFrames).To(HaveLen(1))
|
||||||
|
Expect(payloadFrames[0].(*wire.StreamFrame).DataLenPresent).To(BeFalse())
|
||||||
|
payloadFrames, err = packer.composeNextPacket(maxFrameSize, true)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(payloadFrames).To(BeEmpty())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("correctly handles a STREAM frame with one byte less than maximum size", func() {
|
||||||
maxStreamFrameDataLen := maxFrameSize - (1 + 1 + 2) - 1
|
maxStreamFrameDataLen := maxFrameSize - (1 + 1 + 2) - 1
|
||||||
f1 := &wire.StreamFrame{
|
f1 := &wire.StreamFrame{
|
||||||
StreamID: 5,
|
StreamID: 5,
|
||||||
|
@ -405,7 +429,7 @@ var _ = Describe("Packet packer", func() {
|
||||||
Expect(p.frames[0].(*wire.StreamFrame).DataLenPresent).To(BeFalse())
|
Expect(p.frames[0].(*wire.StreamFrame).DataLenPresent).To(BeFalse())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("packs multiple small stream frames into single packet", func() {
|
It("packs multiple small STREAM frames into single packet", func() {
|
||||||
f1 := &wire.StreamFrame{
|
f1 := &wire.StreamFrame{
|
||||||
StreamID: 5,
|
StreamID: 5,
|
||||||
Data: []byte{0xDE, 0xCA, 0xFB, 0xAD},
|
Data: []byte{0xDE, 0xCA, 0xFB, 0xAD},
|
||||||
|
@ -437,12 +461,12 @@ var _ = Describe("Packet packer", func() {
|
||||||
Expect(p.raw).To(ContainSubstring(string(f3.Data)))
|
Expect(p.raw).To(ContainSubstring(string(f3.Data)))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("splits one stream frame larger than maximum size", func() {
|
It("splits one STREAM frame larger than maximum size", func() {
|
||||||
f := &wire.StreamFrame{
|
f := &wire.StreamFrame{
|
||||||
StreamID: 7,
|
StreamID: 7,
|
||||||
Offset: 1,
|
Offset: 1,
|
||||||
}
|
}
|
||||||
minLength, _ := f.MinLength(0)
|
minLength, _ := f.MinLength(packer.version)
|
||||||
maxStreamFrameDataLen := maxFrameSize - minLength
|
maxStreamFrameDataLen := maxFrameSize - minLength
|
||||||
f.Data = bytes.Repeat([]byte{'f'}, int(maxStreamFrameDataLen)+200)
|
f.Data = bytes.Repeat([]byte{'f'}, int(maxStreamFrameDataLen)+200)
|
||||||
streamFramer.AddFrameForRetransmission(f)
|
streamFramer.AddFrameForRetransmission(f)
|
||||||
|
@ -461,7 +485,7 @@ var _ = Describe("Packet packer", func() {
|
||||||
Expect(payloadFrames).To(BeEmpty())
|
Expect(payloadFrames).To(BeEmpty())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("packs 2 stream frames that are too big for one packet correctly", func() {
|
It("packs 2 STREAM frames that are too big for one packet correctly", func() {
|
||||||
maxStreamFrameDataLen := maxFrameSize - (1 + 1 + 2)
|
maxStreamFrameDataLen := maxFrameSize - (1 + 1 + 2)
|
||||||
f1 := &wire.StreamFrame{
|
f1 := &wire.StreamFrame{
|
||||||
StreamID: 5,
|
StreamID: 5,
|
||||||
|
@ -496,12 +520,12 @@ var _ = Describe("Packet packer", func() {
|
||||||
Expect(p).To(BeNil())
|
Expect(p).To(BeNil())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("packs a packet that has the maximum packet size when given a large enough stream frame", func() {
|
It("packs a packet that has the maximum packet size when given a large enough STREAM frame", func() {
|
||||||
f := &wire.StreamFrame{
|
f := &wire.StreamFrame{
|
||||||
StreamID: 5,
|
StreamID: 5,
|
||||||
Offset: 1,
|
Offset: 1,
|
||||||
}
|
}
|
||||||
minLength, _ := f.MinLength(0)
|
minLength, _ := f.MinLength(packer.version)
|
||||||
f.Data = bytes.Repeat([]byte{'f'}, int(maxFrameSize-minLength+1)) // + 1 since MinceLength is 1 bigger than the actual StreamFrame header
|
f.Data = bytes.Repeat([]byte{'f'}, int(maxFrameSize-minLength+1)) // + 1 since MinceLength is 1 bigger than the actual StreamFrame header
|
||||||
streamFramer.AddFrameForRetransmission(f)
|
streamFramer.AddFrameForRetransmission(f)
|
||||||
p, err := packer.PackPacket()
|
p, err := packer.PackPacket()
|
||||||
|
@ -510,12 +534,12 @@ var _ = Describe("Packet packer", func() {
|
||||||
Expect(p.raw).To(HaveLen(int(protocol.MaxPacketSize)))
|
Expect(p.raw).To(HaveLen(int(protocol.MaxPacketSize)))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("splits a stream frame larger than the maximum size", func() {
|
It("splits a STREAM frame larger than the maximum size", func() {
|
||||||
f := &wire.StreamFrame{
|
f := &wire.StreamFrame{
|
||||||
StreamID: 5,
|
StreamID: 5,
|
||||||
Offset: 1,
|
Offset: 1,
|
||||||
}
|
}
|
||||||
minLength, _ := f.MinLength(0)
|
minLength, _ := f.MinLength(packer.version)
|
||||||
f.Data = bytes.Repeat([]byte{'f'}, int(maxFrameSize-minLength+2)) // + 2 since MinceLength is 1 bigger than the actual StreamFrame header
|
f.Data = bytes.Repeat([]byte{'f'}, int(maxFrameSize-minLength+2)) // + 2 since MinceLength is 1 bigger than the actual StreamFrame header
|
||||||
|
|
||||||
streamFramer.AddFrameForRetransmission(f)
|
streamFramer.AddFrameForRetransmission(f)
|
||||||
|
|
|
@ -77,8 +77,7 @@ func (u *packetUnpacker) parseFrame(r *bytes.Reader, typeByte byte, hdr *wire.He
|
||||||
func (u *packetUnpacker) parseIETFFrame(r *bytes.Reader, typeByte byte, hdr *wire.Header) (wire.Frame, error) {
|
func (u *packetUnpacker) parseIETFFrame(r *bytes.Reader, typeByte byte, hdr *wire.Header) (wire.Frame, error) {
|
||||||
var frame wire.Frame
|
var frame wire.Frame
|
||||||
var err error
|
var err error
|
||||||
// TODO: implement the IETF STREAM frame
|
if typeByte&0xf8 == 0x10 {
|
||||||
if typeByte&0x80 == 0x80 {
|
|
||||||
frame, err = wire.ParseStreamFrame(r, u.version)
|
frame, err = wire.ParseStreamFrame(r, u.version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = qerr.Error(qerr.InvalidStreamData, err.Error())
|
err = qerr.Error(qerr.InvalidStreamData, err.Error())
|
||||||
|
|
|
@ -377,11 +377,55 @@ var _ = Describe("Packet unpacker", func() {
|
||||||
0x04: qerr.InvalidWindowUpdateData,
|
0x04: qerr.InvalidWindowUpdateData,
|
||||||
0x05: qerr.InvalidWindowUpdateData,
|
0x05: qerr.InvalidWindowUpdateData,
|
||||||
0x09: qerr.InvalidBlockedData,
|
0x09: qerr.InvalidBlockedData,
|
||||||
|
0x10: qerr.InvalidStreamData,
|
||||||
} {
|
} {
|
||||||
setData([]byte{b})
|
setData([]byte{b})
|
||||||
_, err := unpacker.Unpack(hdrBin, hdr, data)
|
_, err := unpacker.Unpack(hdrBin, hdr, data)
|
||||||
Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(e))
|
Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(e))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Context("unpacking STREAM frames", func() {
|
||||||
|
It("unpacks unencrypted STREAM frames on the crypto stream", func() {
|
||||||
|
unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionUnencrypted
|
||||||
|
f := &wire.StreamFrame{
|
||||||
|
StreamID: versionIETFFrames.CryptoStreamID(),
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
}
|
||||||
|
err := f.Write(buf, versionIETFFrames)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
setData(buf.Bytes())
|
||||||
|
packet, err := unpacker.Unpack(hdrBin, hdr, data)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(packet.frames).To(Equal([]wire.Frame{f}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("unpacks encrypted STREAM frames on the crypto stream", func() {
|
||||||
|
unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionSecure
|
||||||
|
f := &wire.StreamFrame{
|
||||||
|
StreamID: versionIETFFrames.CryptoStreamID(),
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
}
|
||||||
|
err := f.Write(buf, versionIETFFrames)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
setData(buf.Bytes())
|
||||||
|
packet, err := unpacker.Unpack(hdrBin, hdr, data)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(packet.frames).To(Equal([]wire.Frame{f}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("does not unpack unencrypted STREAM frames on higher streams", func() {
|
||||||
|
unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionUnencrypted
|
||||||
|
f := &wire.StreamFrame{
|
||||||
|
StreamID: 3,
|
||||||
|
Data: []byte("foobar"),
|
||||||
|
}
|
||||||
|
err := f.Write(buf, versionIETFFrames)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
setData(buf.Bytes())
|
||||||
|
_, err = unpacker.Unpack(hdrBin, hdr, data)
|
||||||
|
Expect(err).To(MatchError(qerr.Error(qerr.UnencryptedStreamData, "received unencrypted stream data on stream 3")))
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -320,7 +320,7 @@ func (s *session) postSetup(initialPacketNumber protocol.PacketNumber) error {
|
||||||
s.receivedPacketHandler = ackhandler.NewReceivedPacketHandler(s.version)
|
s.receivedPacketHandler = ackhandler.NewReceivedPacketHandler(s.version)
|
||||||
|
|
||||||
s.streamsMap = newStreamsMap(s.newStream, s.perspective, s.version)
|
s.streamsMap = newStreamsMap(s.newStream, s.perspective, s.version)
|
||||||
s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.connFlowController)
|
s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.connFlowController, s.version)
|
||||||
|
|
||||||
s.packer = newPacketPacker(s.connectionID,
|
s.packer = newPacketPacker(s.connectionID,
|
||||||
initialPacketNumber,
|
initialPacketNumber,
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
type streamFramer struct {
|
type streamFramer struct {
|
||||||
streamsMap *streamsMap
|
streamsMap *streamsMap
|
||||||
cryptoStream streamI
|
cryptoStream streamI
|
||||||
|
version protocol.VersionNumber
|
||||||
|
|
||||||
connFlowController flowcontrol.ConnectionFlowController
|
connFlowController flowcontrol.ConnectionFlowController
|
||||||
|
|
||||||
|
@ -20,11 +21,13 @@ func newStreamFramer(
|
||||||
cryptoStream streamI,
|
cryptoStream streamI,
|
||||||
streamsMap *streamsMap,
|
streamsMap *streamsMap,
|
||||||
cfc flowcontrol.ConnectionFlowController,
|
cfc flowcontrol.ConnectionFlowController,
|
||||||
|
v protocol.VersionNumber,
|
||||||
) *streamFramer {
|
) *streamFramer {
|
||||||
return &streamFramer{
|
return &streamFramer{
|
||||||
streamsMap: streamsMap,
|
streamsMap: streamsMap,
|
||||||
cryptoStream: cryptoStream,
|
cryptoStream: cryptoStream,
|
||||||
connFlowController: cfc,
|
connFlowController: cfc,
|
||||||
|
version: v,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +66,7 @@ func (f *streamFramer) PopCryptoStreamFrame(maxLen protocol.ByteCount) *wire.Str
|
||||||
StreamID: f.cryptoStream.StreamID(),
|
StreamID: f.cryptoStream.StreamID(),
|
||||||
Offset: f.cryptoStream.GetWriteOffset(),
|
Offset: f.cryptoStream.GetWriteOffset(),
|
||||||
}
|
}
|
||||||
frameHeaderBytes, _ := frame.MinLength(protocol.VersionWhatever) // can never error
|
frameHeaderBytes, _ := frame.MinLength(f.version) // can never error
|
||||||
frame.Data = f.cryptoStream.GetDataForWriting(maxLen - frameHeaderBytes)
|
frame.Data = f.cryptoStream.GetDataForWriting(maxLen - frameHeaderBytes)
|
||||||
return frame
|
return frame
|
||||||
}
|
}
|
||||||
|
@ -73,7 +76,7 @@ func (f *streamFramer) maybePopFramesForRetransmission(maxLen protocol.ByteCount
|
||||||
frame := f.retransmissionQueue[0]
|
frame := f.retransmissionQueue[0]
|
||||||
frame.DataLenPresent = true
|
frame.DataLenPresent = true
|
||||||
|
|
||||||
frameHeaderLen, _ := frame.MinLength(protocol.VersionWhatever) // can never error
|
frameHeaderLen, _ := frame.MinLength(f.version) // can never error
|
||||||
if currentLen+frameHeaderLen >= maxLen {
|
if currentLen+frameHeaderLen >= maxLen {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -106,7 +109,7 @@ func (f *streamFramer) maybePopNormalFrames(maxBytes protocol.ByteCount) (res []
|
||||||
frame.StreamID = s.StreamID()
|
frame.StreamID = s.StreamID()
|
||||||
frame.Offset = s.GetWriteOffset()
|
frame.Offset = s.GetWriteOffset()
|
||||||
// not perfect, but thread-safe since writeOffset is only written when getting data
|
// not perfect, but thread-safe since writeOffset is only written when getting data
|
||||||
frameHeaderBytes, _ := frame.MinLength(protocol.VersionWhatever) // can never error
|
frameHeaderBytes, _ := frame.MinLength(f.version) // can never error
|
||||||
if currentLen+frameHeaderBytes > maxBytes {
|
if currentLen+frameHeaderBytes > maxBytes {
|
||||||
return false, nil // theoretically, we could find another stream that fits, but this is quite unlikely, so we stop here
|
return false, nil // theoretically, we could find another stream that fits, but this is quite unlikely, so we stop here
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,12 +41,12 @@ var _ = Describe("Stream Framer", func() {
|
||||||
stream2 = mocks.NewMockStreamI(mockCtrl)
|
stream2 = mocks.NewMockStreamI(mockCtrl)
|
||||||
stream2.EXPECT().StreamID().Return(protocol.StreamID(6)).AnyTimes()
|
stream2.EXPECT().StreamID().Return(protocol.StreamID(6)).AnyTimes()
|
||||||
|
|
||||||
streamsMap = newStreamsMap(nil, protocol.PerspectiveServer, protocol.VersionWhatever)
|
streamsMap = newStreamsMap(nil, protocol.PerspectiveServer, versionGQUICFrames)
|
||||||
streamsMap.putStream(stream1)
|
streamsMap.putStream(stream1)
|
||||||
streamsMap.putStream(stream2)
|
streamsMap.putStream(stream2)
|
||||||
|
|
||||||
connFC = mocks.NewMockConnectionFlowController(mockCtrl)
|
connFC = mocks.NewMockConnectionFlowController(mockCtrl)
|
||||||
framer = newStreamFramer(nil, streamsMap, connFC)
|
framer = newStreamFramer(nil, streamsMap, connFC, versionGQUICFrames)
|
||||||
})
|
})
|
||||||
|
|
||||||
setNoData := func(str *mocks.MockStreamI) {
|
setNoData := func(str *mocks.MockStreamI) {
|
||||||
|
@ -227,7 +227,7 @@ var _ = Describe("Stream Framer", func() {
|
||||||
origlen := retransmittedFrame2.DataLen()
|
origlen := retransmittedFrame2.DataLen()
|
||||||
fs := framer.PopStreamFrames(6)
|
fs := framer.PopStreamFrames(6)
|
||||||
Expect(fs).To(HaveLen(1))
|
Expect(fs).To(HaveLen(1))
|
||||||
minLength, _ := fs[0].MinLength(0)
|
minLength, _ := fs[0].MinLength(framer.version)
|
||||||
Expect(minLength + fs[0].DataLen()).To(Equal(protocol.ByteCount(6)))
|
Expect(minLength + fs[0].DataLen()).To(Equal(protocol.ByteCount(6)))
|
||||||
Expect(framer.retransmissionQueue[0].Data).To(HaveLen(int(origlen - fs[0].DataLen())))
|
Expect(framer.retransmissionQueue[0].Data).To(HaveLen(int(origlen - fs[0].DataLen())))
|
||||||
Expect(framer.retransmissionQueue[0].Offset).To(Equal(fs[0].DataLen()))
|
Expect(framer.retransmissionQueue[0].Offset).To(Equal(fs[0].DataLen()))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue