forked from quic-go/quic-go
ParseOtherFrames-16 148ns ± 4% 150ns ± 3% ~ (p=0.223 n=8+8) ParseAckFrame-16 302ns ± 2% 298ns ± 3% ~ (p=0.246 n=8+8) ParseStreamFrame-16 262ns ± 3% 213ns ± 2% -18.61% (p=0.000 n=8+8) ParseDatagramFrame-16 561ns ± 5% 547ns ± 4% ~ (p=0.105 n=8+8)
477 lines
16 KiB
Go
477 lines
16 KiB
Go
package wire
|
|
|
|
import (
|
|
"io"
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/quic-go/quic-go/internal/protocol"
|
|
"github.com/quic-go/quic-go/quicvarint"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestParseACKWithoutRanges(t *testing.T) {
|
|
data := encodeVarInt(100) // largest acked
|
|
data = append(data, encodeVarInt(0)...) // delay
|
|
data = append(data, encodeVarInt(0)...) // num blocks
|
|
data = append(data, encodeVarInt(10)...) // first ack block
|
|
var frame AckFrame
|
|
n, err := parseAckFrame(&frame, data, FrameTypeAck, protocol.AckDelayExponent, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(data), n)
|
|
require.Equal(t, protocol.PacketNumber(100), frame.LargestAcked())
|
|
require.Equal(t, protocol.PacketNumber(90), frame.LowestAcked())
|
|
require.False(t, frame.HasMissingRanges())
|
|
}
|
|
|
|
func TestParseACKSinglePacket(t *testing.T) {
|
|
data := encodeVarInt(55) // largest acked
|
|
data = append(data, encodeVarInt(0)...) // delay
|
|
data = append(data, encodeVarInt(0)...) // num blocks
|
|
data = append(data, encodeVarInt(0)...) // first ack block
|
|
var frame AckFrame
|
|
n, err := parseAckFrame(&frame, data, FrameTypeAck, protocol.AckDelayExponent, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(data), n)
|
|
require.Equal(t, protocol.PacketNumber(55), frame.LargestAcked())
|
|
require.Equal(t, protocol.PacketNumber(55), frame.LowestAcked())
|
|
require.False(t, frame.HasMissingRanges())
|
|
}
|
|
|
|
func TestParseACKAllPacketsFrom0ToLargest(t *testing.T) {
|
|
data := encodeVarInt(20) // largest acked
|
|
data = append(data, encodeVarInt(0)...) // delay
|
|
data = append(data, encodeVarInt(0)...) // num blocks
|
|
data = append(data, encodeVarInt(20)...) // first ack block
|
|
var frame AckFrame
|
|
n, err := parseAckFrame(&frame, data, FrameTypeAck, protocol.AckDelayExponent, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(data), n)
|
|
require.Equal(t, protocol.PacketNumber(20), frame.LargestAcked())
|
|
require.Equal(t, protocol.PacketNumber(0), frame.LowestAcked())
|
|
require.False(t, frame.HasMissingRanges())
|
|
}
|
|
|
|
func TestParseACKRejectFirstBlockLargerThanLargestAcked(t *testing.T) {
|
|
data := encodeVarInt(20) // largest acked
|
|
data = append(data, encodeVarInt(0)...) // delay
|
|
data = append(data, encodeVarInt(0)...) // num blocks
|
|
data = append(data, encodeVarInt(21)...) // first ack block
|
|
var frame AckFrame
|
|
_, err := parseAckFrame(&frame, data, FrameTypeAck, protocol.AckDelayExponent, protocol.Version1)
|
|
require.EqualError(t, err, "invalid first ACK range")
|
|
}
|
|
|
|
func TestParseACKWithSingleBlock(t *testing.T) {
|
|
data := encodeVarInt(1000) // largest acked
|
|
data = append(data, encodeVarInt(0)...) // delay
|
|
data = append(data, encodeVarInt(1)...) // num blocks
|
|
data = append(data, encodeVarInt(100)...) // first ack block
|
|
data = append(data, encodeVarInt(98)...) // gap
|
|
data = append(data, encodeVarInt(50)...) // ack block
|
|
var frame AckFrame
|
|
n, err := parseAckFrame(&frame, data, FrameTypeAck, protocol.AckDelayExponent, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(data), n)
|
|
require.Equal(t, protocol.PacketNumber(1000), frame.LargestAcked())
|
|
require.Equal(t, protocol.PacketNumber(750), frame.LowestAcked())
|
|
require.True(t, frame.HasMissingRanges())
|
|
require.Equal(t, []AckRange{
|
|
{Largest: 1000, Smallest: 900},
|
|
{Largest: 800, Smallest: 750},
|
|
}, frame.AckRanges)
|
|
}
|
|
|
|
func TestParseACKWithMultipleBlocks(t *testing.T) {
|
|
data := encodeVarInt(100) // largest acked
|
|
data = append(data, encodeVarInt(0)...) // delay
|
|
data = append(data, encodeVarInt(2)...) // num blocks
|
|
data = append(data, encodeVarInt(0)...) // first ack block
|
|
data = append(data, encodeVarInt(0)...) // gap
|
|
data = append(data, encodeVarInt(0)...) // ack block
|
|
data = append(data, encodeVarInt(1)...) // gap
|
|
data = append(data, encodeVarInt(1)...) // ack block
|
|
var frame AckFrame
|
|
n, err := parseAckFrame(&frame, data, FrameTypeAck, protocol.AckDelayExponent, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(data), n)
|
|
require.Equal(t, protocol.PacketNumber(100), frame.LargestAcked())
|
|
require.Equal(t, protocol.PacketNumber(94), frame.LowestAcked())
|
|
require.True(t, frame.HasMissingRanges())
|
|
require.Equal(t, []AckRange{
|
|
{Largest: 100, Smallest: 100},
|
|
{Largest: 98, Smallest: 98},
|
|
{Largest: 95, Smallest: 94},
|
|
}, frame.AckRanges)
|
|
}
|
|
|
|
func TestParseACKUseAckDelayExponent(t *testing.T) {
|
|
const delayTime = 1 << 10 * time.Millisecond
|
|
f := &AckFrame{
|
|
AckRanges: []AckRange{{Smallest: 1, Largest: 1}},
|
|
DelayTime: delayTime,
|
|
}
|
|
b, err := f.Append(nil, protocol.Version1)
|
|
require.NoError(t, err)
|
|
for i := uint8(0); i < 8; i++ {
|
|
typ, l, err := quicvarint.Parse(b)
|
|
require.NoError(t, err)
|
|
var frame AckFrame
|
|
n, err := parseAckFrame(&frame, b[l:], FrameType(typ), protocol.AckDelayExponent+i, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(b[l:]), n)
|
|
require.Equal(t, delayTime*(1<<i), frame.DelayTime)
|
|
}
|
|
}
|
|
|
|
func TestParseACKHandleDelayTimeOverflow(t *testing.T) {
|
|
data := encodeVarInt(100) // largest acked
|
|
data = append(data, encodeVarInt(math.MaxUint64/5)...) // delay
|
|
data = append(data, encodeVarInt(0)...) // num blocks
|
|
data = append(data, encodeVarInt(0)...) // first ack block
|
|
var frame AckFrame
|
|
_, err := parseAckFrame(&frame, data, FrameTypeAck, protocol.AckDelayExponent, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Greater(t, frame.DelayTime, time.Duration(0))
|
|
// The maximum encodable duration is ~292 years.
|
|
require.InDelta(t, 292*365*24, frame.DelayTime.Hours(), 365*24)
|
|
}
|
|
|
|
func TestParseACKErrorOnEOF(t *testing.T) {
|
|
data := encodeVarInt(1000) // largest acked
|
|
data = append(data, encodeVarInt(0)...) // delay
|
|
data = append(data, encodeVarInt(1)...) // num blocks
|
|
data = append(data, encodeVarInt(100)...) // first ack block
|
|
data = append(data, encodeVarInt(98)...) // gap
|
|
data = append(data, encodeVarInt(50)...) // ack block
|
|
var frame AckFrame
|
|
_, err := parseAckFrame(&frame, data, FrameTypeAck, protocol.AckDelayExponent, protocol.Version1)
|
|
require.NoError(t, err)
|
|
for i := range data {
|
|
var frame AckFrame
|
|
_, err := parseAckFrame(&frame, data[:i], FrameTypeAck, protocol.AckDelayExponent, protocol.Version1)
|
|
require.Equal(t, io.EOF, err)
|
|
}
|
|
}
|
|
|
|
func TestParseACKECN(t *testing.T) {
|
|
data := encodeVarInt(100) // largest acked
|
|
data = append(data, encodeVarInt(0)...) // delay
|
|
data = append(data, encodeVarInt(0)...) // num blocks
|
|
data = append(data, encodeVarInt(10)...) // first ack block
|
|
data = append(data, encodeVarInt(0x42)...) // ECT(0)
|
|
data = append(data, encodeVarInt(0x12345)...) // ECT(1)
|
|
data = append(data, encodeVarInt(0x12345678)...) // ECN-CE
|
|
var frame AckFrame
|
|
n, err := parseAckFrame(&frame, data, FrameTypeAckECN, protocol.AckDelayExponent, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(data), n)
|
|
require.Equal(t, protocol.PacketNumber(100), frame.LargestAcked())
|
|
require.Equal(t, protocol.PacketNumber(90), frame.LowestAcked())
|
|
require.False(t, frame.HasMissingRanges())
|
|
require.Equal(t, uint64(0x42), frame.ECT0)
|
|
require.Equal(t, uint64(0x12345), frame.ECT1)
|
|
require.Equal(t, uint64(0x12345678), frame.ECNCE)
|
|
}
|
|
|
|
func TestParseACKECNErrorOnEOF(t *testing.T) {
|
|
data := encodeVarInt(1000) // largest acked
|
|
data = append(data, encodeVarInt(0)...) // delay
|
|
data = append(data, encodeVarInt(1)...) // num blocks
|
|
data = append(data, encodeVarInt(100)...) // first ack block
|
|
data = append(data, encodeVarInt(98)...) // gap
|
|
data = append(data, encodeVarInt(50)...) // ack block
|
|
data = append(data, encodeVarInt(0x42)...) // ECT(0)
|
|
data = append(data, encodeVarInt(0x12345)...) // ECT(1)
|
|
data = append(data, encodeVarInt(0x12345678)...) // ECN-CE
|
|
var frame AckFrame
|
|
n, err := parseAckFrame(&frame, data, FrameTypeAckECN, protocol.AckDelayExponent, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(data), n)
|
|
for i := range data {
|
|
var frame AckFrame
|
|
_, err := parseAckFrame(&frame, data[:i], FrameTypeAckECN, protocol.AckDelayExponent, protocol.Version1)
|
|
require.Equal(t, io.EOF, err)
|
|
}
|
|
}
|
|
|
|
func TestWriteACKSimpleFrame(t *testing.T) {
|
|
f := &AckFrame{
|
|
AckRanges: []AckRange{{Smallest: 100, Largest: 1337}},
|
|
}
|
|
b, err := f.Append(nil, protocol.Version1)
|
|
require.NoError(t, err)
|
|
expected := []byte{byte(FrameTypeAck)}
|
|
expected = append(expected, encodeVarInt(1337)...) // largest acked
|
|
expected = append(expected, 0) // delay
|
|
expected = append(expected, encodeVarInt(0)...) // num ranges
|
|
expected = append(expected, encodeVarInt(1337-100)...)
|
|
require.Equal(t, expected, b)
|
|
}
|
|
|
|
func TestWriteACKECNFrame(t *testing.T) {
|
|
f := &AckFrame{
|
|
AckRanges: []AckRange{{Smallest: 10, Largest: 2000}},
|
|
ECT0: 13,
|
|
ECT1: 37,
|
|
ECNCE: 12345,
|
|
}
|
|
b, err := f.Append(nil, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Len(t, b, int(f.Length(protocol.Version1)))
|
|
expected := []byte{byte(FrameTypeAckECN)}
|
|
expected = append(expected, encodeVarInt(2000)...) // largest acked
|
|
expected = append(expected, 0) // delay
|
|
expected = append(expected, encodeVarInt(0)...) // num ranges
|
|
expected = append(expected, encodeVarInt(2000-10)...)
|
|
expected = append(expected, encodeVarInt(13)...)
|
|
expected = append(expected, encodeVarInt(37)...)
|
|
expected = append(expected, encodeVarInt(12345)...)
|
|
require.Equal(t, expected, b)
|
|
}
|
|
|
|
func TestWriteACKSinglePacket(t *testing.T) {
|
|
f := &AckFrame{
|
|
AckRanges: []AckRange{{Smallest: 0x2eadbeef, Largest: 0x2eadbeef}},
|
|
DelayTime: 18 * time.Millisecond,
|
|
}
|
|
b, err := f.Append(nil, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Len(t, b, int(f.Length(protocol.Version1)))
|
|
typ, l, err := quicvarint.Parse(b)
|
|
require.NoError(t, err)
|
|
b = b[l:]
|
|
var frame AckFrame
|
|
n, err := parseAckFrame(&frame, b, FrameType(typ), protocol.AckDelayExponent, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(b), n)
|
|
require.Equal(t, f, &frame)
|
|
require.False(t, frame.HasMissingRanges())
|
|
require.Equal(t, f.DelayTime, frame.DelayTime)
|
|
}
|
|
|
|
func TestWriteACKManyPackets(t *testing.T) {
|
|
f := &AckFrame{
|
|
AckRanges: []AckRange{{Smallest: 0x1337, Largest: 0x2eadbeef}},
|
|
}
|
|
b, err := f.Append(nil, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Len(t, b, int(f.Length(protocol.Version1)))
|
|
typ, l, err := quicvarint.Parse(b)
|
|
require.NoError(t, err)
|
|
b = b[l:]
|
|
var frame AckFrame
|
|
n, err := parseAckFrame(&frame, b, FrameType(typ), protocol.AckDelayExponent, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(b), n)
|
|
require.Equal(t, f, &frame)
|
|
require.False(t, frame.HasMissingRanges())
|
|
}
|
|
|
|
func TestWriteACKSingleGap(t *testing.T) {
|
|
f := &AckFrame{
|
|
AckRanges: []AckRange{
|
|
{Smallest: 400, Largest: 1000},
|
|
{Smallest: 100, Largest: 200},
|
|
},
|
|
}
|
|
require.True(t, f.validateAckRanges())
|
|
b, err := f.Append(nil, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Len(t, b, int(f.Length(protocol.Version1)))
|
|
typ, l, err := quicvarint.Parse(b)
|
|
require.NoError(t, err)
|
|
b = b[l:]
|
|
var frame AckFrame
|
|
n, err := parseAckFrame(&frame, b, FrameType(typ), protocol.AckDelayExponent, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(b), n)
|
|
require.Equal(t, f, &frame)
|
|
require.True(t, frame.HasMissingRanges())
|
|
}
|
|
|
|
func TestWriteACKMultipleRanges(t *testing.T) {
|
|
f := &AckFrame{
|
|
AckRanges: []AckRange{
|
|
{Smallest: 10, Largest: 10},
|
|
{Smallest: 8, Largest: 8},
|
|
{Smallest: 5, Largest: 6},
|
|
{Smallest: 1, Largest: 3},
|
|
},
|
|
}
|
|
require.True(t, f.validateAckRanges())
|
|
b, err := f.Append(nil, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Len(t, b, int(f.Length(protocol.Version1)))
|
|
typ, l, err := quicvarint.Parse(b)
|
|
require.NoError(t, err)
|
|
b = b[l:]
|
|
var frame AckFrame
|
|
n, err := parseAckFrame(&frame, b, FrameType(typ), protocol.AckDelayExponent, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(b), n)
|
|
require.Equal(t, f, &frame)
|
|
require.True(t, frame.HasMissingRanges())
|
|
}
|
|
|
|
func TestWriteACKLimitMaxSize(t *testing.T) {
|
|
const numRanges = 1000
|
|
ackRanges := make([]AckRange, numRanges)
|
|
for i := protocol.PacketNumber(1); i <= numRanges; i++ {
|
|
ackRanges[numRanges-i] = AckRange{Smallest: 2 * i, Largest: 2 * i}
|
|
}
|
|
f := &AckFrame{AckRanges: ackRanges}
|
|
require.True(t, f.validateAckRanges())
|
|
b, err := f.Append(nil, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Len(t, b, int(f.Length(protocol.Version1)))
|
|
// make sure the ACK frame is *a little bit* smaller than the MaxAckFrameSize
|
|
require.Greater(t, protocol.ByteCount(len(b)), protocol.MaxAckFrameSize-5)
|
|
require.LessOrEqual(t, protocol.ByteCount(len(b)), protocol.MaxAckFrameSize)
|
|
typ, l, err := quicvarint.Parse(b)
|
|
require.NoError(t, err)
|
|
b = b[l:]
|
|
var frame AckFrame
|
|
n, err := parseAckFrame(&frame, b, FrameType(typ), protocol.AckDelayExponent, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(b), n)
|
|
require.True(t, frame.HasMissingRanges())
|
|
require.Less(t, len(frame.AckRanges), numRanges) // make sure we dropped some ranges
|
|
}
|
|
|
|
func TestAckRangeValidator(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
ackRanges []AckRange
|
|
valid bool
|
|
}{
|
|
{
|
|
name: "rejects ACKs without ranges",
|
|
ackRanges: nil,
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "accepts an ACK without NACK Ranges",
|
|
ackRanges: []AckRange{{Smallest: 1, Largest: 7}},
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "rejects ACK ranges with Smallest greater than Largest",
|
|
ackRanges: []AckRange{
|
|
{Smallest: 8, Largest: 10},
|
|
{Smallest: 4, Largest: 3},
|
|
},
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "rejects ACK ranges in the wrong order",
|
|
ackRanges: []AckRange{
|
|
{Smallest: 2, Largest: 2},
|
|
{Smallest: 6, Largest: 7},
|
|
},
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "rejects with overlapping ACK ranges",
|
|
ackRanges: []AckRange{
|
|
{Smallest: 5, Largest: 7},
|
|
{Smallest: 2, Largest: 5},
|
|
},
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "rejects ACK ranges that are part of a larger ACK range",
|
|
ackRanges: []AckRange{
|
|
{Smallest: 4, Largest: 7},
|
|
{Smallest: 5, Largest: 6},
|
|
},
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "rejects with directly adjacent ACK ranges",
|
|
ackRanges: []AckRange{
|
|
{Smallest: 5, Largest: 7},
|
|
{Smallest: 2, Largest: 4},
|
|
},
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "accepts an ACK with one lost packet",
|
|
ackRanges: []AckRange{
|
|
{Smallest: 5, Largest: 10},
|
|
{Smallest: 1, Largest: 3},
|
|
},
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "accepts an ACK with multiple lost packets",
|
|
ackRanges: []AckRange{
|
|
{Smallest: 15, Largest: 20},
|
|
{Smallest: 10, Largest: 12},
|
|
{Smallest: 1, Largest: 3},
|
|
},
|
|
valid: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ack := AckFrame{AckRanges: tt.ackRanges}
|
|
result := ack.validateAckRanges()
|
|
require.Equal(t, tt.valid, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAckFrameAcksPacketWithoutRanges(t *testing.T) {
|
|
f := AckFrame{
|
|
AckRanges: []AckRange{{Smallest: 5, Largest: 10}},
|
|
}
|
|
require.False(t, f.AcksPacket(1))
|
|
require.False(t, f.AcksPacket(4))
|
|
require.True(t, f.AcksPacket(5))
|
|
require.True(t, f.AcksPacket(8))
|
|
require.True(t, f.AcksPacket(10))
|
|
require.False(t, f.AcksPacket(11))
|
|
require.False(t, f.AcksPacket(20))
|
|
}
|
|
|
|
func TestAckFrameAcksPacketWithMultipleRanges(t *testing.T) {
|
|
f := AckFrame{
|
|
AckRanges: []AckRange{
|
|
{Smallest: 15, Largest: 20},
|
|
{Smallest: 5, Largest: 8},
|
|
},
|
|
}
|
|
require.False(t, f.AcksPacket(4))
|
|
require.True(t, f.AcksPacket(5))
|
|
require.True(t, f.AcksPacket(6))
|
|
require.True(t, f.AcksPacket(7))
|
|
require.True(t, f.AcksPacket(8))
|
|
require.False(t, f.AcksPacket(9))
|
|
require.False(t, f.AcksPacket(14))
|
|
require.True(t, f.AcksPacket(15))
|
|
require.True(t, f.AcksPacket(18))
|
|
require.True(t, f.AcksPacket(19))
|
|
require.True(t, f.AcksPacket(20))
|
|
require.False(t, f.AcksPacket(21))
|
|
}
|
|
|
|
func TestAckFrameReset(t *testing.T) {
|
|
f := &AckFrame{
|
|
DelayTime: time.Second,
|
|
AckRanges: []AckRange{{Smallest: 1, Largest: 3}},
|
|
ECT0: 1,
|
|
ECT1: 2,
|
|
ECNCE: 3,
|
|
}
|
|
f.Reset()
|
|
require.Empty(t, f.AckRanges)
|
|
require.Equal(t, 1, cap(f.AckRanges))
|
|
require.Zero(t, f.DelayTime)
|
|
require.Zero(t, f.ECT0)
|
|
require.Zero(t, f.ECT1)
|
|
require.Zero(t, f.ECNCE)
|
|
}
|