mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-04 12:47:36 +03:00
931 lines
46 KiB
Go
931 lines
46 KiB
Go
package quic
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/rand"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/quic-go/quic-go/internal/ackhandler"
|
|
"github.com/quic-go/quic-go/internal/handshake"
|
|
"github.com/quic-go/quic-go/internal/mocks"
|
|
mockackhandler "github.com/quic-go/quic-go/internal/mocks/ackhandler"
|
|
"github.com/quic-go/quic-go/internal/protocol"
|
|
"github.com/quic-go/quic-go/internal/qerr"
|
|
"github.com/quic-go/quic-go/internal/utils"
|
|
"github.com/quic-go/quic-go/internal/wire"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/mock/gomock"
|
|
)
|
|
|
|
const testPackerConnIDLen = 4
|
|
|
|
type testPacketPacker struct {
|
|
packer *packetPacker
|
|
initialStream, handshakeStream *cryptoStream
|
|
datagramQueue *datagramQueue
|
|
pnManager *mockackhandler.MockSentPacketHandler
|
|
sealingManager *MockSealingManager
|
|
framer *MockFrameSource
|
|
ackFramer *MockAckFrameSource
|
|
retransmissionQueue *retransmissionQueue
|
|
}
|
|
|
|
func newTestPacketPacker(t *testing.T, mockCtrl *gomock.Controller, pers protocol.Perspective) *testPacketPacker {
|
|
destConnID := protocol.ParseConnectionID([]byte{1, 2, 3, 4})
|
|
require.Equal(t, testPackerConnIDLen, destConnID.Len())
|
|
initialStream := newCryptoStream()
|
|
handshakeStream := newCryptoStream()
|
|
pnManager := mockackhandler.NewMockSentPacketHandler(mockCtrl)
|
|
framer := NewMockFrameSource(mockCtrl)
|
|
ackFramer := NewMockAckFrameSource(mockCtrl)
|
|
sealingManager := NewMockSealingManager(mockCtrl)
|
|
datagramQueue := newDatagramQueue(func() {}, utils.DefaultLogger)
|
|
retransmissionQueue := newRetransmissionQueue()
|
|
return &testPacketPacker{
|
|
pnManager: pnManager,
|
|
initialStream: initialStream,
|
|
handshakeStream: handshakeStream,
|
|
sealingManager: sealingManager,
|
|
framer: framer,
|
|
ackFramer: ackFramer,
|
|
datagramQueue: datagramQueue,
|
|
retransmissionQueue: retransmissionQueue,
|
|
packer: newPacketPacker(
|
|
protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}),
|
|
func() protocol.ConnectionID { return destConnID },
|
|
initialStream,
|
|
handshakeStream,
|
|
pnManager,
|
|
retransmissionQueue,
|
|
sealingManager,
|
|
framer,
|
|
ackFramer,
|
|
datagramQueue,
|
|
pers,
|
|
),
|
|
}
|
|
}
|
|
|
|
// newMockShortHeaderSealer returns a mock short header sealer that seals a short header packet
|
|
func newMockShortHeaderSealer(mockCtrl *gomock.Controller) *mocks.MockShortHeaderSealer {
|
|
sealer := mocks.NewMockShortHeaderSealer(mockCtrl)
|
|
sealer.EXPECT().KeyPhase().Return(protocol.KeyPhaseOne).AnyTimes()
|
|
sealer.EXPECT().Overhead().Return(7).AnyTimes()
|
|
sealer.EXPECT().EncryptHeader(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
|
sealer.EXPECT().Seal(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(dst, src []byte, pn protocol.PacketNumber, associatedData []byte) []byte {
|
|
return append(src, bytes.Repeat([]byte{'s'}, sealer.Overhead())...)
|
|
}).AnyTimes()
|
|
return sealer
|
|
}
|
|
|
|
func parsePacket(t *testing.T, data []byte) (hdrs []*wire.ExtendedHeader, more []byte) {
|
|
t.Helper()
|
|
for len(data) > 0 {
|
|
if !wire.IsLongHeaderPacket(data[0]) {
|
|
break
|
|
}
|
|
hdr, _, more, err := wire.ParsePacket(data)
|
|
require.NoError(t, err)
|
|
extHdr, err := hdr.ParseExtended(data)
|
|
require.NoError(t, err)
|
|
require.GreaterOrEqual(t, extHdr.Length+protocol.ByteCount(extHdr.PacketNumberLen), protocol.ByteCount(4))
|
|
data = more
|
|
hdrs = append(hdrs, extHdr)
|
|
}
|
|
return hdrs, data
|
|
}
|
|
|
|
func parseShortHeaderPacket(t *testing.T, data []byte, connIDLen int) {
|
|
t.Helper()
|
|
l, _, pnLen, _, err := wire.ParseShortHeader(data, connIDLen)
|
|
require.NoError(t, err)
|
|
require.GreaterOrEqual(t, len(data)-l+int(pnLen), 4)
|
|
}
|
|
|
|
func expectAppendFrames(framer *MockFrameSource, controlFrames []ackhandler.Frame, streamFrames []ackhandler.StreamFrame) {
|
|
framer.EXPECT().Append(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
|
|
func(cf []ackhandler.Frame, sf []ackhandler.StreamFrame, _ protocol.ByteCount, _ time.Time, v protocol.Version) ([]ackhandler.Frame, []ackhandler.StreamFrame, protocol.ByteCount) {
|
|
var length protocol.ByteCount
|
|
for _, f := range controlFrames {
|
|
length += f.Frame.Length(v)
|
|
}
|
|
for _, f := range streamFrames {
|
|
length += f.Frame.Length(v)
|
|
}
|
|
return append(cf, controlFrames...), append(sf, streamFrames...), length
|
|
},
|
|
)
|
|
}
|
|
|
|
func TestPackLongHeaders(t *testing.T) {
|
|
const maxPacketSize protocol.ByteCount = 1234
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveClient)
|
|
token := make([]byte, 20)
|
|
rand.Read(token)
|
|
tp.packer.SetToken(token)
|
|
now := time.Now()
|
|
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x24), protocol.PacketNumberLen3)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x24))
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen4)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42))
|
|
tp.sealingManager.EXPECT().GetInitialSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.sealingManager.EXPECT().GetHandshakeSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.sealingManager.EXPECT().Get0RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, now, false)
|
|
// don't EXPECT any calls for a Handshake ACK frame
|
|
tp.initialStream.Write([]byte("initial"))
|
|
tp.packer.retransmissionQueue.addHandshake(&wire.PingFrame{})
|
|
|
|
p, err := tp.packer.PackCoalescedPacket(false, maxPacketSize, now, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, maxPacketSize, p.buffer.Len())
|
|
require.Len(t, p.longHdrPackets, 2)
|
|
require.Nil(t, p.shortHdrPacket)
|
|
require.Equal(t, protocol.EncryptionInitial, p.longHdrPackets[0].EncryptionLevel())
|
|
require.Len(t, p.longHdrPackets[0].frames, 1)
|
|
require.Equal(t, []byte("initial"), p.longHdrPackets[0].frames[0].Frame.(*wire.CryptoFrame).Data)
|
|
require.Equal(t, protocol.EncryptionHandshake, p.longHdrPackets[1].EncryptionLevel())
|
|
require.Len(t, p.longHdrPackets[1].frames, 1)
|
|
require.IsType(t, &wire.PingFrame{}, p.longHdrPackets[1].frames[0].Frame)
|
|
|
|
hdrs, more := parsePacket(t, p.buffer.Data)
|
|
require.Len(t, hdrs, 2)
|
|
require.Equal(t, protocol.PacketTypeInitial, hdrs[0].Type)
|
|
require.Equal(t, token, hdrs[0].Token)
|
|
require.Equal(t, protocol.PacketNumber(0x24), hdrs[0].PacketNumber)
|
|
require.Equal(t, protocol.PacketNumberLen3, hdrs[0].PacketNumberLen)
|
|
require.Equal(t, protocol.PacketTypeHandshake, hdrs[1].Type)
|
|
require.Nil(t, hdrs[1].Token)
|
|
require.Equal(t, protocol.PacketNumber(0x42), hdrs[1].PacketNumber)
|
|
require.Equal(t, protocol.PacketNumberLen4, hdrs[1].PacketNumberLen)
|
|
require.Empty(t, more)
|
|
}
|
|
|
|
func TestPackCoalescedAckOnlyPacketNothingToSend(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveClient)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
// the packet number is not popped
|
|
tp.sealingManager.EXPECT().GetInitialSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.sealingManager.EXPECT().GetHandshakeSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, gomock.Any(), true)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, gomock.Any(), true)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), true)
|
|
p, err := tp.packer.PackCoalescedPacket(true, 1234, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Nil(t, p)
|
|
}
|
|
|
|
func TestPackInitialAckOnlyPacket(t *testing.T) {
|
|
t.Run("client", func(t *testing.T) { testPackInitialAckOnlyPacket(t, protocol.PerspectiveClient) })
|
|
t.Run("server", func(t *testing.T) { testPackInitialAckOnlyPacket(t, protocol.PerspectiveServer) })
|
|
}
|
|
|
|
func testPackInitialAckOnlyPacket(t *testing.T, pers protocol.Perspective) {
|
|
const maxPacketSize protocol.ByteCount = 1234
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, pers)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42))
|
|
tp.sealingManager.EXPECT().GetInitialSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 10}}}
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, gomock.Any(), true).Return(ack)
|
|
p, err := tp.packer.PackCoalescedPacket(true, maxPacketSize, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, p)
|
|
require.Len(t, p.longHdrPackets, 1)
|
|
require.Equal(t, protocol.EncryptionInitial, p.longHdrPackets[0].EncryptionLevel())
|
|
require.Equal(t, ack, p.longHdrPackets[0].ack)
|
|
require.Empty(t, p.longHdrPackets[0].frames)
|
|
// only the client needs to pad Initial packets
|
|
switch pers {
|
|
case protocol.PerspectiveClient:
|
|
require.Equal(t, maxPacketSize, p.buffer.Len())
|
|
case protocol.PerspectiveServer:
|
|
require.Less(t, p.buffer.Len(), protocol.ByteCount(100))
|
|
}
|
|
hdrs, more := parsePacket(t, p.buffer.Data)
|
|
require.Empty(t, more)
|
|
require.Len(t, hdrs, 1)
|
|
require.Equal(t, protocol.PacketTypeInitial, hdrs[0].Type)
|
|
}
|
|
|
|
func TestPack1RTTAckOnlyPacket(t *testing.T) {
|
|
const maxPacketSize protocol.ByteCount = 1300
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveClient)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42))
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 10}}}
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), true).Return(ack)
|
|
p, buffer, err := tp.packer.PackAckOnlyPacket(maxPacketSize, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, ack, p.Ack)
|
|
require.Empty(t, p.Frames)
|
|
parsePacket(t, buffer.Data)
|
|
}
|
|
|
|
func TestPack0RTTPacket(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveClient)
|
|
tp.sealingManager.EXPECT().GetInitialSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.sealingManager.EXPECT().Get0RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysNotYetAvailable)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, gomock.Any(), true)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption0RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption0RTT).Return(protocol.PacketNumber(0x42))
|
|
cf := ackhandler.Frame{Frame: &wire.MaxDataFrame{MaximumData: 0x1337}}
|
|
tp.framer.EXPECT().HasData().Return(true)
|
|
// TODO: check sizes
|
|
tp.framer.EXPECT().Append(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
|
|
func(fs []ackhandler.Frame, sf []ackhandler.StreamFrame, _ protocol.ByteCount, _ time.Time, _ protocol.Version) ([]ackhandler.Frame, []ackhandler.StreamFrame, protocol.ByteCount) {
|
|
return append(fs, cf), sf, cf.Frame.Length(protocol.Version1)
|
|
},
|
|
)
|
|
p, err := tp.packer.PackCoalescedPacket(false, protocol.MaxByteCount, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, p)
|
|
require.Len(t, p.longHdrPackets, 1)
|
|
require.Equal(t, protocol.PacketType0RTT, p.longHdrPackets[0].header.Type)
|
|
require.Equal(t, protocol.Encryption0RTT, p.longHdrPackets[0].EncryptionLevel())
|
|
require.Len(t, p.longHdrPackets[0].frames, 1)
|
|
require.Equal(t, cf.Frame, p.longHdrPackets[0].frames[0].Frame)
|
|
require.NotNil(t, p.longHdrPackets[0].frames[0].Handler)
|
|
}
|
|
|
|
// ACK frames can't be sent in 0-RTT packets
|
|
func TestPack0RTTPacketNoACK(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveClient)
|
|
tp.sealingManager.EXPECT().GetInitialSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysNotYetAvailable)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, gomock.Any(), true)
|
|
// no further calls to get an ACK frame
|
|
p, err := tp.packer.PackCoalescedPacket(true, protocol.MaxByteCount, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Nil(t, p)
|
|
}
|
|
|
|
func TestPackCoalescedAppData(t *testing.T) {
|
|
const maxPacketSize protocol.ByteCount = 1234
|
|
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x24), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x24))
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42))
|
|
tp.sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped)
|
|
tp.sealingManager.EXPECT().GetHandshakeSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.framer.EXPECT().HasData().Return(true)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, gomock.Any(), false)
|
|
// don't expect any calls for a 1-RTT ACK frame
|
|
tp.handshakeStream.Write([]byte("handshake"))
|
|
expectAppendFrames(tp.framer, nil, []ackhandler.StreamFrame{{Frame: &wire.StreamFrame{Data: []byte("foobar")}}})
|
|
|
|
p, err := tp.packer.PackCoalescedPacket(false, maxPacketSize, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Less(t, p.buffer.Len(), protocol.ByteCount(100))
|
|
require.Len(t, p.longHdrPackets, 1)
|
|
require.Equal(t, protocol.EncryptionHandshake, p.longHdrPackets[0].EncryptionLevel())
|
|
require.Len(t, p.longHdrPackets[0].frames, 1)
|
|
require.Equal(t, []byte("handshake"), p.longHdrPackets[0].frames[0].Frame.(*wire.CryptoFrame).Data)
|
|
require.NotNil(t, p.shortHdrPacket)
|
|
require.Empty(t, p.shortHdrPacket.Frames)
|
|
require.Len(t, p.shortHdrPacket.StreamFrames, 1)
|
|
require.Equal(t, []byte("foobar"), p.shortHdrPacket.StreamFrames[0].Frame.Data)
|
|
|
|
hdrs, more := parsePacket(t, p.buffer.Data)
|
|
require.Len(t, hdrs, 1)
|
|
require.Equal(t, protocol.PacketTypeHandshake, hdrs[0].Type)
|
|
require.NotEmpty(t, more)
|
|
parseShortHeaderPacket(t, more, testPackerConnIDLen)
|
|
}
|
|
|
|
func TestPackConnectionCloseCoalesced(t *testing.T) {
|
|
t.Run("client", func(t *testing.T) { testPackConnectionCloseCoalesced(t, protocol.PerspectiveClient) })
|
|
t.Run("server", func(t *testing.T) { testPackConnectionCloseCoalesced(t, protocol.PerspectiveServer) })
|
|
}
|
|
|
|
func testPackConnectionCloseCoalesced(t *testing.T, pers protocol.Perspective) {
|
|
const maxPacketSize protocol.ByteCount = 1234
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, pers)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(1), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(1))
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(2), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(2))
|
|
tp.sealingManager.EXPECT().GetInitialSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.sealingManager.EXPECT().GetHandshakeSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
switch pers {
|
|
case protocol.PerspectiveClient:
|
|
tp.sealingManager.EXPECT().Get0RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption0RTT).Return(protocol.PacketNumber(3), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption0RTT).Return(protocol.PacketNumber(3))
|
|
case protocol.PerspectiveServer:
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(3), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(3))
|
|
}
|
|
p, err := tp.packer.PackApplicationClose(&qerr.ApplicationError{
|
|
ErrorCode: 0x1337,
|
|
ErrorMessage: "test error",
|
|
}, maxPacketSize, protocol.Version1)
|
|
require.NoError(t, err)
|
|
switch pers {
|
|
case protocol.PerspectiveClient:
|
|
require.Len(t, p.longHdrPackets, 3)
|
|
require.Nil(t, p.shortHdrPacket)
|
|
case protocol.PerspectiveServer:
|
|
require.Len(t, p.longHdrPackets, 2)
|
|
require.NotNil(t, p.shortHdrPacket)
|
|
}
|
|
// for Initial packets, the error code is replace with a transport error of type APPLICATION_ERROR
|
|
require.Equal(t, protocol.PacketTypeInitial, p.longHdrPackets[0].header.Type)
|
|
require.Equal(t, protocol.PacketNumber(1), p.longHdrPackets[0].header.PacketNumber)
|
|
require.Len(t, p.longHdrPackets[0].frames, 1)
|
|
require.IsType(t, &wire.ConnectionCloseFrame{}, p.longHdrPackets[0].frames[0].Frame)
|
|
ccf := p.longHdrPackets[0].frames[0].Frame.(*wire.ConnectionCloseFrame)
|
|
require.False(t, ccf.IsApplicationError)
|
|
require.Equal(t, uint64(qerr.ApplicationErrorErrorCode), ccf.ErrorCode)
|
|
require.Empty(t, ccf.ReasonPhrase)
|
|
// for Handshake packets, the error code is replace with a transport error of type APPLICATION_ERROR
|
|
require.Equal(t, protocol.PacketTypeHandshake, p.longHdrPackets[1].header.Type)
|
|
require.Equal(t, protocol.PacketNumber(2), p.longHdrPackets[1].header.PacketNumber)
|
|
require.Len(t, p.longHdrPackets[1].frames, 1)
|
|
require.IsType(t, &wire.ConnectionCloseFrame{}, p.longHdrPackets[1].frames[0].Frame)
|
|
ccf = p.longHdrPackets[1].frames[0].Frame.(*wire.ConnectionCloseFrame)
|
|
require.False(t, ccf.IsApplicationError)
|
|
require.Equal(t, uint64(qerr.ApplicationErrorErrorCode), ccf.ErrorCode)
|
|
require.Empty(t, ccf.ReasonPhrase)
|
|
|
|
// for application-data packet number space (1-RTT for the server, 0-RTT for the client),
|
|
// the application-level error code is sent
|
|
|
|
switch pers {
|
|
case protocol.PerspectiveClient:
|
|
require.Equal(t, protocol.PacketNumber(3), p.longHdrPackets[2].header.PacketNumber)
|
|
require.Len(t, p.longHdrPackets[2].frames, 1)
|
|
require.IsType(t, &wire.ConnectionCloseFrame{}, p.longHdrPackets[2].frames[0].Frame)
|
|
ccf = p.longHdrPackets[2].frames[0].Frame.(*wire.ConnectionCloseFrame)
|
|
case protocol.PerspectiveServer:
|
|
require.Equal(t, protocol.PacketNumber(3), p.shortHdrPacket.PacketNumber)
|
|
require.Len(t, p.shortHdrPacket.Frames, 1)
|
|
require.IsType(t, &wire.ConnectionCloseFrame{}, p.shortHdrPacket.Frames[0].Frame)
|
|
ccf = p.shortHdrPacket.Frames[0].Frame.(*wire.ConnectionCloseFrame)
|
|
}
|
|
require.True(t, ccf.IsApplicationError)
|
|
require.Equal(t, uint64(0x1337), ccf.ErrorCode)
|
|
require.Equal(t, "test error", ccf.ReasonPhrase)
|
|
|
|
// the client needs to pad this packet to the max packet size
|
|
switch pers {
|
|
case protocol.PerspectiveClient:
|
|
require.Equal(t, maxPacketSize, p.buffer.Len())
|
|
case protocol.PerspectiveServer:
|
|
require.Less(t, p.buffer.Len(), protocol.ByteCount(100))
|
|
}
|
|
}
|
|
|
|
func TestPackConnectionCloseCryptoError(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42))
|
|
tp.sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped)
|
|
tp.sealingManager.EXPECT().GetHandshakeSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable)
|
|
quicErr := qerr.NewLocalCryptoError(0x42, errors.New("crypto error"))
|
|
quicErr.FrameType = 0x1234
|
|
p, err := tp.packer.PackConnectionClose(quicErr, protocol.MaxByteCount, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Len(t, p.longHdrPackets, 1)
|
|
require.Equal(t, protocol.PacketTypeHandshake, p.longHdrPackets[0].header.Type)
|
|
require.Len(t, p.longHdrPackets[0].frames, 1)
|
|
require.IsType(t, &wire.ConnectionCloseFrame{}, p.longHdrPackets[0].frames[0].Frame)
|
|
ccf := p.longHdrPackets[0].frames[0].Frame.(*wire.ConnectionCloseFrame)
|
|
require.False(t, ccf.IsApplicationError)
|
|
require.Equal(t, uint64(0x100+0x42), ccf.ErrorCode)
|
|
require.Equal(t, uint64(0x1234), ccf.FrameType)
|
|
// for crypto errors, the reason phrase is cleared
|
|
require.Empty(t, ccf.ReasonPhrase)
|
|
}
|
|
|
|
func TestPackConnectionClose1RTT(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42))
|
|
tp.sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped)
|
|
tp.sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysDropped)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
// expect no framer.PopStreamFrames
|
|
p, err := tp.packer.PackConnectionClose(&qerr.TransportError{
|
|
ErrorCode: qerr.CryptoBufferExceeded,
|
|
ErrorMessage: "test error",
|
|
}, protocol.MaxByteCount, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Empty(t, p.longHdrPackets)
|
|
require.Len(t, p.shortHdrPacket.Frames, 1)
|
|
require.IsType(t, &wire.ConnectionCloseFrame{}, p.shortHdrPacket.Frames[0].Frame)
|
|
ccf := p.shortHdrPacket.Frames[0].Frame.(*wire.ConnectionCloseFrame)
|
|
require.False(t, ccf.IsApplicationError)
|
|
require.Equal(t, uint64(qerr.CryptoBufferExceeded), ccf.ErrorCode)
|
|
require.Equal(t, "test error", ccf.ReasonPhrase)
|
|
}
|
|
|
|
func TestPack1RTTPacketNothingToSend(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
// don't expect any calls to PopPacketNumber
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), true)
|
|
tp.framer.EXPECT().HasData()
|
|
_, err := tp.packer.AppendPacket(getPacketBuffer(), protocol.MaxByteCount, time.Now(), protocol.Version1)
|
|
require.ErrorIs(t, err, errNothingToPack)
|
|
}
|
|
|
|
func TestPack1RTTPacketWithData(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42))
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.framer.EXPECT().HasData().Return(true)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), false)
|
|
f := &wire.StreamFrame{
|
|
StreamID: 5,
|
|
Data: []byte{0xde, 0xca, 0xfb, 0xad},
|
|
}
|
|
expectAppendFrames(
|
|
tp.framer,
|
|
[]ackhandler.Frame{
|
|
{Frame: &wire.ResetStreamFrame{}, Handler: &mtuFinderAckHandler{}}, // set any non-nil ackhandler.FrameHandler
|
|
{Frame: &wire.MaxDataFrame{}},
|
|
},
|
|
[]ackhandler.StreamFrame{{Frame: f}},
|
|
)
|
|
buffer := getPacketBuffer()
|
|
buffer.Data = append(buffer.Data, []byte("foobar")...)
|
|
p, err := tp.packer.AppendPacket(buffer, protocol.MaxByteCount, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
b, err := f.Append(nil, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Len(t, p.StreamFrames, 1)
|
|
var sawResetStream, sawMaxData bool
|
|
for _, frame := range p.Frames {
|
|
switch frame.Frame.(type) {
|
|
case *wire.ResetStreamFrame:
|
|
sawResetStream = true
|
|
require.Equal(t, frame.Handler, &mtuFinderAckHandler{})
|
|
case *wire.MaxDataFrame:
|
|
sawMaxData = true
|
|
require.NotNil(t, frame.Handler)
|
|
require.NotEqual(t, frame.Handler, &mtuFinderAckHandler{})
|
|
}
|
|
}
|
|
require.True(t, sawResetStream)
|
|
require.True(t, sawMaxData)
|
|
require.Equal(t, f.StreamID, p.StreamFrames[0].Frame.StreamID)
|
|
require.Equal(t, buffer.Data[:6], []byte("foobar")) // make sure the packet was actually appended
|
|
require.Contains(t, string(buffer.Data), string(b))
|
|
}
|
|
|
|
func TestPack1RTTPacketWithACK(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42))
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Largest: 42, Smallest: 1}}}
|
|
tp.framer.EXPECT().HasData()
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), true).Return(ack)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
p, err := tp.packer.AppendPacket(getPacketBuffer(), protocol.MaxByteCount, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, ack, p.Ack)
|
|
}
|
|
|
|
func TestPackPathChallengeAndPathResponse(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42))
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.framer.EXPECT().HasData().Return(true)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), false)
|
|
frames := []ackhandler.Frame{
|
|
{Frame: &wire.PathChallengeFrame{}},
|
|
{Frame: &wire.PathResponseFrame{}},
|
|
{Frame: &wire.DataBlockedFrame{}},
|
|
}
|
|
expectAppendFrames(tp.framer, frames, nil)
|
|
buffer := getPacketBuffer()
|
|
p, err := tp.packer.AppendPacket(buffer, protocol.MaxByteCount, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Len(t, p.Frames, 3)
|
|
var sawPathChallenge, sawPathResponse bool
|
|
for _, f := range p.Frames {
|
|
switch f.Frame.(type) {
|
|
case *wire.PathChallengeFrame:
|
|
sawPathChallenge = true
|
|
// this means that the frame won't be retransmitted.
|
|
require.Nil(t, f.Handler)
|
|
case *wire.PathResponseFrame:
|
|
sawPathResponse = true
|
|
// this means that the frame won't be retransmitted.
|
|
require.Nil(t, f.Handler)
|
|
default:
|
|
require.NotNil(t, f.Handler)
|
|
}
|
|
}
|
|
require.True(t, sawPathChallenge)
|
|
require.True(t, sawPathResponse)
|
|
require.NotZero(t, buffer.Len())
|
|
}
|
|
|
|
func TestPackDatagramFrames(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), true)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42))
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.datagramQueue.Add(&wire.DatagramFrame{
|
|
DataLenPresent: true,
|
|
Data: []byte("foobar"),
|
|
})
|
|
tp.framer.EXPECT().HasData()
|
|
buffer := getPacketBuffer()
|
|
p, err := tp.packer.AppendPacket(buffer, protocol.MaxByteCount, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Len(t, p.Frames, 1)
|
|
require.IsType(t, &wire.DatagramFrame{}, p.Frames[0].Frame)
|
|
require.Equal(t, []byte("foobar"), p.Frames[0].Frame.(*wire.DatagramFrame).Data)
|
|
require.NotEmpty(t, buffer.Data)
|
|
}
|
|
|
|
func TestPackLargeDatagramFrame(t *testing.T) {
|
|
// If a packet contains an ACK, and doesn't have enough space for the DATAGRAM frame,
|
|
// it should be skipped. It will be packed in the next packet.
|
|
const maxPacketSize = 1000
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), true).Return(&wire.AckFrame{AckRanges: []wire.AckRange{{Largest: 100}}})
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42))
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
f := &wire.DatagramFrame{DataLenPresent: true, Data: make([]byte, maxPacketSize-10)}
|
|
tp.datagramQueue.Add(f)
|
|
tp.framer.EXPECT().HasData()
|
|
buffer := getPacketBuffer()
|
|
p, err := tp.packer.AppendPacket(buffer, maxPacketSize, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, p.Ack)
|
|
require.Empty(t, p.Frames)
|
|
require.NotEmpty(t, buffer.Data)
|
|
require.Equal(t, f, tp.datagramQueue.Peek()) // make sure the frame is still there
|
|
|
|
// Now try packing again, but with a smaller packet size.
|
|
// The DATAGRAM frame should now be dropped, as we can't expect to ever be able tosend it out.
|
|
const newMaxPacketSize = maxPacketSize - 10
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), true)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x43), protocol.PacketNumberLen2)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.framer.EXPECT().HasData()
|
|
buffer = getPacketBuffer()
|
|
p, err = tp.packer.AppendPacket(buffer, newMaxPacketSize, time.Now(), protocol.Version1)
|
|
require.ErrorIs(t, err, errNothingToPack)
|
|
require.Nil(t, tp.datagramQueue.Peek()) // make sure the frame is gone
|
|
}
|
|
|
|
func TestPackRetransmissions(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
f := &wire.CryptoFrame{Data: []byte("Initial")}
|
|
tp.retransmissionQueue.addInitial(f)
|
|
tp.retransmissionQueue.addHandshake(&wire.CryptoFrame{Data: []byte("Handshake")})
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42))
|
|
tp.sealingManager.EXPECT().GetInitialSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysNotYetAvailable)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, gomock.Any(), false)
|
|
p, err := tp.packer.PackCoalescedPacket(false, 1000, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Len(t, p.longHdrPackets, 1)
|
|
require.Equal(t, protocol.EncryptionInitial, p.longHdrPackets[0].EncryptionLevel())
|
|
require.Len(t, p.longHdrPackets[0].frames, 1)
|
|
require.Equal(t, f, p.longHdrPackets[0].frames[0].Frame)
|
|
require.NotNil(t, p.longHdrPackets[0].frames[0].Handler)
|
|
}
|
|
|
|
func packMaxNumNonAckElicitingAcks(t *testing.T, tp *testPacketPacker, mockCtrl *gomock.Controller, maxPacketSize protocol.ByteCount) {
|
|
t.Helper()
|
|
for i := 0; i < protocol.MaxNonAckElicitingAcks; i++ {
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42))
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.framer.EXPECT().HasData().Return(true)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), false).Return(
|
|
&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}},
|
|
)
|
|
expectAppendFrames(tp.framer, nil, nil)
|
|
p, err := tp.packer.AppendPacket(getPacketBuffer(), maxPacketSize, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, p.Ack)
|
|
require.Empty(t, p.Frames)
|
|
}
|
|
}
|
|
|
|
func TestPackEvery20thPacketAckEliciting(t *testing.T) {
|
|
const maxPacketSize = 1000
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
|
|
// send the maximum number of non-ACK-eliciting packets
|
|
packMaxNumNonAckElicitingAcks(t, tp, mockCtrl, maxPacketSize)
|
|
|
|
// Now there's nothing to send, so we shouldn't generate a packet just to send a PING
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), false)
|
|
tp.framer.EXPECT().HasData().Return(true)
|
|
expectAppendFrames(tp.framer, nil, nil)
|
|
_, err := tp.packer.AppendPacket(getPacketBuffer(), maxPacketSize, time.Now(), protocol.Version1)
|
|
require.ErrorIs(t, err, errNothingToPack)
|
|
|
|
// Now we have an ACK to send. We should bundle a PING to make the packet ack-eliciting.
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42))
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.framer.EXPECT().HasData().Return(true)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), false).Return(
|
|
&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}},
|
|
)
|
|
expectAppendFrames(tp.framer, nil, nil)
|
|
p, err := tp.packer.AppendPacket(getPacketBuffer(), maxPacketSize, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Len(t, p.Frames, 1)
|
|
require.Equal(t, &wire.PingFrame{}, p.Frames[0].Frame)
|
|
require.Nil(t, p.Frames[0].Handler) // make sure the PING is not retransmitted if lost
|
|
|
|
// make sure the next packet doesn't contain another PING
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42))
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.framer.EXPECT().HasData().Return(true)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), false).Return(
|
|
&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}},
|
|
)
|
|
expectAppendFrames(tp.framer, nil, nil)
|
|
p, err = tp.packer.AppendPacket(getPacketBuffer(), maxPacketSize, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, p.Ack)
|
|
require.Empty(t, p.Frames)
|
|
}
|
|
|
|
func TestPackLongHeaderPadToAtLeast4Bytes(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen1)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42))
|
|
|
|
sealer := newMockShortHeaderSealer(mockCtrl)
|
|
tp.sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped)
|
|
tp.sealingManager.EXPECT().GetHandshakeSealer().Return(sealer, nil)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable)
|
|
tp.retransmissionQueue.addHandshake(&wire.PingFrame{})
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, gomock.Any(), false)
|
|
|
|
packet, err := tp.packer.PackCoalescedPacket(false, protocol.MaxByteCount, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, packet)
|
|
require.Len(t, packet.longHdrPackets, 1)
|
|
require.Nil(t, packet.shortHdrPacket)
|
|
|
|
hdr, _, _, err := wire.ParsePacket(packet.buffer.Data)
|
|
require.NoError(t, err)
|
|
data := packet.buffer.Data
|
|
extHdr, err := hdr.ParseExtended(data)
|
|
require.NoError(t, err)
|
|
require.Equal(t, protocol.PacketNumberLen1, extHdr.PacketNumberLen)
|
|
|
|
data = data[extHdr.ParsedLen():]
|
|
require.Len(t, data, 4-1 /* packet number length */ +sealer.Overhead())
|
|
// first bytes should be 2 PADDING frames...
|
|
require.Equal(t, []byte{0, 0}, data[:2])
|
|
// ...followed by the PING frame
|
|
frameParser := wire.NewFrameParser(false)
|
|
l, frame, err := frameParser.ParseNext(data[2:], protocol.EncryptionHandshake, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.IsType(t, &wire.PingFrame{}, frame)
|
|
require.Equal(t, sealer.Overhead(), len(data)-2-l)
|
|
}
|
|
|
|
func TestPackShortHeaderPadToAtLeast4Bytes(t *testing.T) {
|
|
// small stream ID, such that only a single byte is consumed
|
|
f := &wire.StreamFrame{StreamID: 0x10, Fin: true}
|
|
require.Equal(t, protocol.ByteCount(2), f.Length(protocol.Version1))
|
|
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen1)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42))
|
|
sealer := newMockShortHeaderSealer(mockCtrl)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(sealer, nil)
|
|
tp.framer.EXPECT().HasData().Return(true)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), false)
|
|
expectAppendFrames(tp.framer, nil, []ackhandler.StreamFrame{{Frame: f}})
|
|
|
|
buffer := getPacketBuffer()
|
|
_, err := tp.packer.AppendPacket(buffer, protocol.MaxByteCount, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
// cut off the tag that the mock sealer added
|
|
buffer.Data = buffer.Data[:buffer.Len()-protocol.ByteCount(sealer.Overhead())]
|
|
data := buffer.Data
|
|
|
|
l, _, pnLen, _, err := wire.ParseShortHeader(data, testPackerConnIDLen)
|
|
require.NoError(t, err)
|
|
payload := data[l:]
|
|
require.Equal(t, protocol.PacketNumberLen1, pnLen)
|
|
require.Equal(t, 4-1 /* packet number length */, len(payload))
|
|
// the first byte of the payload should be a PADDING frame...
|
|
require.Equal(t, byte(0), payload[0])
|
|
|
|
// ... followed by the STREAM frame
|
|
frameParser := wire.NewFrameParser(true)
|
|
frameLen, frame, err := frameParser.ParseNext(payload[1:], protocol.Encryption1RTT, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, f, frame)
|
|
require.Equal(t, len(payload)-1, frameLen)
|
|
}
|
|
|
|
func TestPackInitialProbePacket(t *testing.T) {
|
|
t.Run("client", func(t *testing.T) {
|
|
testPackProbePacket(t, protocol.EncryptionInitial, protocol.PerspectiveClient)
|
|
})
|
|
t.Run("server", func(t *testing.T) {
|
|
testPackProbePacket(t, protocol.EncryptionInitial, protocol.PerspectiveServer)
|
|
})
|
|
}
|
|
|
|
func TestPackHandshakeProbePacket(t *testing.T) {
|
|
t.Run("client", func(t *testing.T) {
|
|
testPackProbePacket(t, protocol.EncryptionHandshake, protocol.PerspectiveClient)
|
|
})
|
|
t.Run("server", func(t *testing.T) {
|
|
testPackProbePacket(t, protocol.EncryptionHandshake, protocol.PerspectiveServer)
|
|
})
|
|
}
|
|
|
|
func testPackProbePacket(t *testing.T, encLevel protocol.EncryptionLevel, perspective protocol.Perspective) {
|
|
const maxPacketSize protocol.ByteCount = 1234
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, perspective)
|
|
|
|
switch encLevel {
|
|
case protocol.EncryptionInitial:
|
|
tp.sealingManager.EXPECT().GetInitialSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.packer.initialStream.Write([]byte("foobar"))
|
|
case protocol.EncryptionHandshake:
|
|
tp.sealingManager.EXPECT().GetHandshakeSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.packer.handshakeStream.Write([]byte("foobar"))
|
|
}
|
|
tp.ackFramer.EXPECT().GetAckFrame(encLevel, gomock.Any(), false)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(encLevel).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(encLevel).Return(protocol.PacketNumber(0x42))
|
|
|
|
p, err := tp.packer.MaybePackPTOProbePacket(encLevel, maxPacketSize, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, p)
|
|
require.Len(t, p.longHdrPackets, 1)
|
|
packet := p.longHdrPackets[0]
|
|
require.Equal(t, encLevel, packet.EncryptionLevel())
|
|
if encLevel == protocol.EncryptionInitial {
|
|
require.GreaterOrEqual(t, p.buffer.Len(), protocol.ByteCount(protocol.MinInitialPacketSize))
|
|
require.Equal(t, maxPacketSize, p.buffer.Len())
|
|
}
|
|
require.Len(t, packet.frames, 1)
|
|
require.Equal(t, &wire.CryptoFrame{Data: []byte("foobar")}, packet.frames[0].Frame)
|
|
hdrs, more := parsePacket(t, p.buffer.Data)
|
|
require.Len(t, hdrs, 1)
|
|
switch encLevel {
|
|
case protocol.EncryptionInitial:
|
|
require.Equal(t, protocol.PacketTypeInitial, hdrs[0].Type)
|
|
case protocol.EncryptionHandshake:
|
|
require.Equal(t, protocol.PacketTypeHandshake, hdrs[0].Type)
|
|
}
|
|
require.Empty(t, more)
|
|
}
|
|
|
|
func TestPackProbePacketNothingToSend(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveClient)
|
|
tp.sealingManager.EXPECT().GetInitialSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, gomock.Any(), true)
|
|
|
|
p, err := tp.packer.MaybePackPTOProbePacket(protocol.EncryptionInitial, protocol.MaxByteCount, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Nil(t, p)
|
|
}
|
|
|
|
func TestPack1RTTProbePacket(t *testing.T) {
|
|
const maxPacketSize protocol.ByteCount = 999
|
|
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), false)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42))
|
|
tp.framer.EXPECT().HasData().Return(true)
|
|
tp.framer.EXPECT().Append(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), protocol.Version1).DoAndReturn(
|
|
func(cf []ackhandler.Frame, sf []ackhandler.StreamFrame, size protocol.ByteCount, _ time.Time, v protocol.Version) ([]ackhandler.Frame, []ackhandler.StreamFrame, protocol.ByteCount) {
|
|
f, split := (&wire.StreamFrame{Data: make([]byte, 2*maxPacketSize)}).MaybeSplitOffFrame(size, v)
|
|
require.True(t, split)
|
|
return cf, append(sf, ackhandler.StreamFrame{Frame: f}), f.Length(v)
|
|
},
|
|
)
|
|
|
|
p, err := tp.packer.MaybePackPTOProbePacket(protocol.Encryption1RTT, maxPacketSize, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, p)
|
|
require.True(t, p.IsOnlyShortHeaderPacket())
|
|
require.Empty(t, p.longHdrPackets)
|
|
require.NotNil(t, p.shortHdrPacket)
|
|
packet := p.shortHdrPacket
|
|
require.Empty(t, packet.Frames)
|
|
require.Len(t, packet.StreamFrames, 1)
|
|
require.Equal(t, maxPacketSize, packet.Length)
|
|
}
|
|
|
|
func TestPackProbePacketNothingToPack(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2)
|
|
tp.ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, gomock.Any(), true)
|
|
tp.framer.EXPECT().HasData()
|
|
|
|
packet, err := tp.packer.MaybePackPTOProbePacket(protocol.Encryption1RTT, protocol.MaxByteCount, time.Now(), protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Nil(t, packet)
|
|
}
|
|
|
|
func TestPackMTUProbePacket(t *testing.T) {
|
|
const (
|
|
maxPacketSize protocol.ByteCount = 1000
|
|
probePacketSize = maxPacketSize + 42
|
|
)
|
|
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveClient)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x43), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x43))
|
|
ping := ackhandler.Frame{Frame: &wire.PingFrame{}}
|
|
p, buffer, err := tp.packer.PackMTUProbePacket(ping, probePacketSize, protocol.Version1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, probePacketSize, p.Length)
|
|
require.Equal(t, protocol.PacketNumber(0x43), p.PacketNumber)
|
|
require.Len(t, buffer.Data, int(probePacketSize))
|
|
require.True(t, p.IsPathMTUProbePacket)
|
|
require.False(t, p.IsPathProbePacket)
|
|
}
|
|
|
|
func TestPackPathProbePacket(t *testing.T) {
|
|
mockCtrl := gomock.NewController(t)
|
|
tp := newTestPacketPacker(t, mockCtrl, protocol.PerspectiveServer)
|
|
tp.sealingManager.EXPECT().Get1RTTSealer().Return(newMockShortHeaderSealer(mockCtrl), nil)
|
|
tp.pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x43), protocol.PacketNumberLen2)
|
|
tp.pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x43))
|
|
|
|
p, buf, err := tp.packer.PackPathProbePacket(
|
|
protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
|
|
ackhandler.Frame{Frame: &wire.PathChallengeFrame{Data: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}}},
|
|
protocol.Version1,
|
|
)
|
|
require.NoError(t, err)
|
|
require.Equal(t, protocol.PacketNumber(0x43), p.PacketNumber)
|
|
require.Nil(t, p.Ack)
|
|
require.Empty(t, p.StreamFrames)
|
|
require.Equal(t, &wire.PathChallengeFrame{Data: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}}, p.Frames[0].Frame)
|
|
require.Len(t, buf.Data, protocol.MinInitialPacketSize)
|
|
require.True(t, p.IsPathProbePacket)
|
|
require.False(t, p.IsPathMTUProbePacket)
|
|
}
|