mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-03 20:27:35 +03:00
uTLS is not yet bumped to the new version, so this commit breaks the dependencies relationship by getting rid of the local replace.
1429 lines
60 KiB
Go
1429 lines
60 KiB
Go
package ackhandler
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/golang/mock/gomock"
|
|
|
|
"github.com/refraction-networking/uquic/internal/mocks"
|
|
"github.com/refraction-networking/uquic/internal/protocol"
|
|
"github.com/refraction-networking/uquic/internal/qerr"
|
|
"github.com/refraction-networking/uquic/internal/utils"
|
|
"github.com/refraction-networking/uquic/internal/wire"
|
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
type customFrameHandler struct {
|
|
onLost, onAcked func(wire.Frame)
|
|
}
|
|
|
|
func (h *customFrameHandler) OnLost(f wire.Frame) {
|
|
if h.onLost != nil {
|
|
h.onLost(f)
|
|
}
|
|
}
|
|
|
|
func (h *customFrameHandler) OnAcked(f wire.Frame) {
|
|
if h.onAcked != nil {
|
|
h.onAcked(f)
|
|
}
|
|
}
|
|
|
|
var _ = Describe("SentPacketHandler", func() {
|
|
var (
|
|
handler *sentPacketHandler
|
|
streamFrame wire.StreamFrame
|
|
lostPackets []protocol.PacketNumber
|
|
perspective protocol.Perspective
|
|
)
|
|
|
|
BeforeEach(func() { perspective = protocol.PerspectiveServer })
|
|
|
|
JustBeforeEach(func() {
|
|
lostPackets = nil
|
|
rttStats := utils.NewRTTStats()
|
|
handler = newSentPacketHandler(42, protocol.InitialPacketSizeIPv4, rttStats, false, perspective, nil, utils.DefaultLogger)
|
|
streamFrame = wire.StreamFrame{
|
|
StreamID: 5,
|
|
Data: []byte{0x13, 0x37},
|
|
}
|
|
})
|
|
|
|
getPacket := func(pn protocol.PacketNumber, encLevel protocol.EncryptionLevel) *packet {
|
|
for _, p := range handler.getPacketNumberSpace(encLevel).history.packets {
|
|
if p != nil && p.PacketNumber == pn {
|
|
return p
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
ackElicitingPacket := func(p *packet) *packet {
|
|
if p.EncryptionLevel == 0 {
|
|
p.EncryptionLevel = protocol.Encryption1RTT
|
|
}
|
|
if p.Length == 0 {
|
|
p.Length = 1
|
|
}
|
|
if p.SendTime.IsZero() {
|
|
p.SendTime = time.Now()
|
|
}
|
|
if len(p.Frames) == 0 {
|
|
p.Frames = []Frame{
|
|
{Frame: &wire.PingFrame{}, Handler: &customFrameHandler{
|
|
onLost: func(wire.Frame) { lostPackets = append(lostPackets, p.PacketNumber) },
|
|
}},
|
|
}
|
|
}
|
|
return p
|
|
}
|
|
|
|
nonAckElicitingPacket := func(p *packet) *packet {
|
|
p = ackElicitingPacket(p)
|
|
p.Frames = nil
|
|
p.LargestAcked = 1
|
|
return p
|
|
}
|
|
|
|
initialPacket := func(p *packet) *packet {
|
|
p = ackElicitingPacket(p)
|
|
p.EncryptionLevel = protocol.EncryptionInitial
|
|
return p
|
|
}
|
|
|
|
handshakePacket := func(p *packet) *packet {
|
|
p = ackElicitingPacket(p)
|
|
p.EncryptionLevel = protocol.EncryptionHandshake
|
|
return p
|
|
}
|
|
|
|
handshakePacketNonAckEliciting := func(p *packet) *packet {
|
|
p = nonAckElicitingPacket(p)
|
|
p.EncryptionLevel = protocol.EncryptionHandshake
|
|
return p
|
|
}
|
|
|
|
sentPacket := func(p *packet) {
|
|
handler.SentPacket(p.SendTime, p.PacketNumber, p.LargestAcked, p.StreamFrames, p.Frames, p.EncryptionLevel, p.Length, p.IsPathMTUProbePacket)
|
|
}
|
|
|
|
expectInPacketHistory := func(expected []protocol.PacketNumber, encLevel protocol.EncryptionLevel) {
|
|
pnSpace := handler.getPacketNumberSpace(encLevel)
|
|
var length int
|
|
pnSpace.history.Iterate(func(p *packet) (bool, error) {
|
|
if !p.declaredLost && !p.skippedPacket {
|
|
length++
|
|
}
|
|
return true, nil
|
|
})
|
|
ExpectWithOffset(1, length).To(Equal(len(expected)))
|
|
for _, p := range expected {
|
|
ExpectWithOffset(2, getPacket(p, encLevel)).ToNot(BeNil())
|
|
}
|
|
}
|
|
|
|
updateRTT := func(rtt time.Duration) {
|
|
handler.rttStats.UpdateRTT(rtt, 0, time.Now())
|
|
ExpectWithOffset(1, handler.rttStats.SmoothedRTT()).To(Equal(rtt))
|
|
}
|
|
|
|
// setHandshakeConfirmed drops both Initial and Handshake packets and then confirms the handshake
|
|
setHandshakeConfirmed := func() {
|
|
handler.DropPackets(protocol.EncryptionInitial)
|
|
handler.DropPackets(protocol.EncryptionHandshake)
|
|
handler.SetHandshakeConfirmed()
|
|
}
|
|
|
|
Context("registering sent packets", func() {
|
|
It("accepts two consecutive packets", func() {
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, EncryptionLevel: protocol.EncryptionHandshake}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, EncryptionLevel: protocol.EncryptionHandshake}))
|
|
Expect(handler.handshakePackets.largestSent).To(Equal(protocol.PacketNumber(2)))
|
|
expectInPacketHistory([]protocol.PacketNumber{1, 2}, protocol.EncryptionHandshake)
|
|
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(2)))
|
|
})
|
|
|
|
It("uses the same packet number space for 0-RTT and 1-RTT packets", func() {
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, EncryptionLevel: protocol.Encryption0RTT}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, EncryptionLevel: protocol.Encryption1RTT}))
|
|
Expect(handler.appDataPackets.largestSent).To(Equal(protocol.PacketNumber(2)))
|
|
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(2)))
|
|
})
|
|
|
|
It("accepts packet number 0", func() {
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 0, EncryptionLevel: protocol.Encryption1RTT}))
|
|
Expect(handler.appDataPackets.largestSent).To(BeZero())
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, EncryptionLevel: protocol.Encryption1RTT}))
|
|
Expect(handler.appDataPackets.largestSent).To(Equal(protocol.PacketNumber(1)))
|
|
expectInPacketHistory([]protocol.PacketNumber{0, 1}, protocol.Encryption1RTT)
|
|
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(2)))
|
|
})
|
|
|
|
It("stores the sent time", func() {
|
|
sendTime := time.Now().Add(-time.Minute)
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: sendTime}))
|
|
Expect(handler.appDataPackets.lastAckElicitingPacketTime).To(Equal(sendTime))
|
|
})
|
|
|
|
It("stores the sent time of Initial packets", func() {
|
|
sendTime := time.Now().Add(-time.Minute)
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: sendTime, EncryptionLevel: protocol.EncryptionInitial}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, SendTime: sendTime.Add(time.Hour), EncryptionLevel: protocol.Encryption1RTT}))
|
|
Expect(handler.initialPackets.lastAckElicitingPacketTime).To(Equal(sendTime))
|
|
})
|
|
})
|
|
|
|
Context("ACK processing", func() {
|
|
JustBeforeEach(func() {
|
|
for i := protocol.PacketNumber(0); i < 10; i++ {
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: i}))
|
|
}
|
|
// Increase RTT, because the tests would be flaky otherwise
|
|
updateRTT(time.Hour)
|
|
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(10)))
|
|
})
|
|
|
|
Context("ACK processing", func() {
|
|
It("accepts ACKs sent in packet 0", func() {
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 0, Largest: 5}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.appDataPackets.largestAcked).To(Equal(protocol.PacketNumber(5)))
|
|
})
|
|
|
|
It("says if a 1-RTT packet was acknowledged", func() {
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 10, EncryptionLevel: protocol.Encryption0RTT}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 11, EncryptionLevel: protocol.Encryption0RTT}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 12, EncryptionLevel: protocol.Encryption1RTT}))
|
|
acked1RTT, err := handler.ReceivedAck(
|
|
&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 11}}},
|
|
protocol.Encryption1RTT,
|
|
time.Now(),
|
|
)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(acked1RTT).To(BeFalse())
|
|
acked1RTT, err = handler.ReceivedAck(
|
|
&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 11, Largest: 12}}},
|
|
protocol.Encryption1RTT,
|
|
time.Now(),
|
|
)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(acked1RTT).To(BeTrue())
|
|
})
|
|
|
|
It("accepts multiple ACKs sent in the same packet", func() {
|
|
ack1 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 0, Largest: 3}}}
|
|
ack2 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 0, Largest: 4}}}
|
|
_, err := handler.ReceivedAck(ack1, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.appDataPackets.largestAcked).To(Equal(protocol.PacketNumber(3)))
|
|
// this wouldn't happen in practice
|
|
// for testing purposes, we pretend to send a different ACK frame in a duplicated packet, to be able to verify that it actually doesn't get processed
|
|
_, err = handler.ReceivedAck(ack2, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.appDataPackets.largestAcked).To(Equal(protocol.PacketNumber(4)))
|
|
})
|
|
|
|
It("rejects ACKs that acknowledge a skipped packet number", func() {
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 10}))
|
|
handler.appDataPackets.history.SkippedPacket(11)
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 12}))
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 12}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).To(MatchError(&qerr.TransportError{
|
|
ErrorCode: qerr.ProtocolViolation,
|
|
ErrorMessage: "received an ACK for skipped packet number: 11 (1-RTT)",
|
|
}))
|
|
})
|
|
|
|
It("rejects ACKs with a too high LargestAcked packet number", func() {
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 0, Largest: 9999}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).To(MatchError(&qerr.TransportError{
|
|
ErrorCode: qerr.ProtocolViolation,
|
|
ErrorMessage: "received ACK for an unsent packet",
|
|
}))
|
|
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(10)))
|
|
})
|
|
|
|
It("ignores repeated ACKs", func() {
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 0, Largest: 3}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(6)))
|
|
_, err = handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.appDataPackets.largestAcked).To(Equal(protocol.PacketNumber(3)))
|
|
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(6)))
|
|
})
|
|
})
|
|
|
|
Context("acks the right packets", func() {
|
|
expectInPacketHistoryOrLost := func(expected []protocol.PacketNumber, encLevel protocol.EncryptionLevel) {
|
|
pnSpace := handler.getPacketNumberSpace(encLevel)
|
|
var length int
|
|
pnSpace.history.Iterate(func(p *packet) (bool, error) {
|
|
if !p.declaredLost {
|
|
length++
|
|
}
|
|
return true, nil
|
|
})
|
|
ExpectWithOffset(1, length+len(lostPackets)).To(Equal(len(expected)))
|
|
expectedLoop:
|
|
for _, p := range expected {
|
|
if getPacket(p, encLevel) != nil {
|
|
continue
|
|
}
|
|
for _, lostP := range lostPackets {
|
|
if lostP == p {
|
|
continue expectedLoop
|
|
}
|
|
}
|
|
Fail(fmt.Sprintf("Packet %d not in packet history.", p))
|
|
}
|
|
}
|
|
|
|
It("adjusts the LargestAcked, and adjusts the bytes in flight", func() {
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 0, Largest: 5}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.appDataPackets.largestAcked).To(Equal(protocol.PacketNumber(5)))
|
|
expectInPacketHistoryOrLost([]protocol.PacketNumber{6, 7, 8, 9}, protocol.Encryption1RTT)
|
|
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(4)))
|
|
})
|
|
|
|
It("acks packet 0", func() {
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 0, Largest: 0}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(getPacket(0, protocol.Encryption1RTT)).To(BeNil())
|
|
expectInPacketHistoryOrLost([]protocol.PacketNumber{1, 2, 3, 4, 5, 6, 7, 8, 9}, protocol.Encryption1RTT)
|
|
})
|
|
|
|
It("calls the OnAcked callback", func() {
|
|
var acked bool
|
|
ping := &wire.PingFrame{}
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: 10,
|
|
Frames: []Frame{{
|
|
Frame: ping,
|
|
Handler: &customFrameHandler{
|
|
onAcked: func(f wire.Frame) {
|
|
Expect(f).To(Equal(ping))
|
|
acked = true
|
|
},
|
|
},
|
|
}},
|
|
}))
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 10}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(acked).To(BeTrue())
|
|
})
|
|
|
|
It("handles an ACK frame with one missing packet range", func() {
|
|
ack := &wire.AckFrame{ // lose 4 and 5
|
|
AckRanges: []wire.AckRange{
|
|
{Smallest: 6, Largest: 9},
|
|
{Smallest: 1, Largest: 3},
|
|
},
|
|
}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
expectInPacketHistoryOrLost([]protocol.PacketNumber{0, 4, 5}, protocol.Encryption1RTT)
|
|
})
|
|
|
|
It("does not ack packets below the LowestAcked", func() {
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 3, Largest: 8}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
expectInPacketHistoryOrLost([]protocol.PacketNumber{0, 1, 2, 9}, protocol.Encryption1RTT)
|
|
})
|
|
|
|
It("handles an ACK with multiple missing packet ranges", func() {
|
|
ack := &wire.AckFrame{ // packets 2, 4 and 5, and 8 were lost
|
|
AckRanges: []wire.AckRange{
|
|
{Smallest: 9, Largest: 9},
|
|
{Smallest: 6, Largest: 7},
|
|
{Smallest: 3, Largest: 3},
|
|
{Smallest: 1, Largest: 1},
|
|
},
|
|
}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
expectInPacketHistoryOrLost([]protocol.PacketNumber{0, 2, 4, 5, 8}, protocol.Encryption1RTT)
|
|
})
|
|
|
|
It("processes an ACK frame that would be sent after a late arrival of a packet", func() {
|
|
ack1 := &wire.AckFrame{ // 5 lost
|
|
AckRanges: []wire.AckRange{
|
|
{Smallest: 6, Largest: 6},
|
|
{Smallest: 1, Largest: 4},
|
|
},
|
|
}
|
|
_, err := handler.ReceivedAck(ack1, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
expectInPacketHistoryOrLost([]protocol.PacketNumber{0, 5, 7, 8, 9}, protocol.Encryption1RTT)
|
|
ack2 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 6}}} // now ack 5
|
|
_, err = handler.ReceivedAck(ack2, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
expectInPacketHistoryOrLost([]protocol.PacketNumber{0, 7, 8, 9}, protocol.Encryption1RTT)
|
|
})
|
|
|
|
It("processes an ACK that contains old ACK ranges", func() {
|
|
ack1 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 6}}}
|
|
_, err := handler.ReceivedAck(ack1, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
expectInPacketHistoryOrLost([]protocol.PacketNumber{0, 7, 8, 9}, protocol.Encryption1RTT)
|
|
ack2 := &wire.AckFrame{
|
|
AckRanges: []wire.AckRange{
|
|
{Smallest: 8, Largest: 8},
|
|
{Smallest: 3, Largest: 3},
|
|
{Smallest: 1, Largest: 1},
|
|
},
|
|
}
|
|
_, err = handler.ReceivedAck(ack2, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
expectInPacketHistoryOrLost([]protocol.PacketNumber{0, 7, 9}, protocol.Encryption1RTT)
|
|
})
|
|
})
|
|
|
|
Context("calculating RTT", func() {
|
|
It("computes the RTT", func() {
|
|
now := time.Now()
|
|
// First, fake the sent times of the first, second and last packet
|
|
getPacket(1, protocol.Encryption1RTT).SendTime = now.Add(-10 * time.Minute)
|
|
getPacket(2, protocol.Encryption1RTT).SendTime = now.Add(-5 * time.Minute)
|
|
getPacket(6, protocol.Encryption1RTT).SendTime = now.Add(-1 * time.Minute)
|
|
// Now, check that the proper times are used when calculating the deltas
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 10*time.Minute, 1*time.Second))
|
|
ack = &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 2}}}
|
|
_, err = handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 5*time.Minute, 1*time.Second))
|
|
ack = &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 6}}}
|
|
_, err = handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 1*time.Minute, 1*time.Second))
|
|
})
|
|
|
|
It("ignores the DelayTime for Initial and Handshake packets", func() {
|
|
sentPacket(initialPacket(&packet{PacketNumber: 1}))
|
|
handler.rttStats.SetMaxAckDelay(time.Hour)
|
|
// make sure the rttStats have a min RTT, so that the delay is used
|
|
handler.rttStats.UpdateRTT(5*time.Minute, 0, time.Now())
|
|
getPacket(1, protocol.EncryptionInitial).SendTime = time.Now().Add(-10 * time.Minute)
|
|
ack := &wire.AckFrame{
|
|
AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}},
|
|
DelayTime: 5 * time.Minute,
|
|
}
|
|
_, err := handler.ReceivedAck(ack, protocol.EncryptionInitial, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 10*time.Minute, 1*time.Second))
|
|
})
|
|
|
|
It("uses the DelayTime in the ACK frame", func() {
|
|
handler.rttStats.SetMaxAckDelay(time.Hour)
|
|
// make sure the rttStats have a min RTT, so that the delay is used
|
|
handler.rttStats.UpdateRTT(5*time.Minute, 0, time.Now())
|
|
getPacket(1, protocol.Encryption1RTT).SendTime = time.Now().Add(-10 * time.Minute)
|
|
ack := &wire.AckFrame{
|
|
AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}},
|
|
DelayTime: 5 * time.Minute,
|
|
}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 5*time.Minute, 1*time.Second))
|
|
})
|
|
|
|
It("limits the DelayTime in the ACK frame to max_ack_delay", func() {
|
|
handler.rttStats.SetMaxAckDelay(time.Minute)
|
|
// make sure the rttStats have a min RTT, so that the delay is used
|
|
handler.rttStats.UpdateRTT(5*time.Minute, 0, time.Now())
|
|
getPacket(1, protocol.Encryption1RTT).SendTime = time.Now().Add(-10 * time.Minute)
|
|
ack := &wire.AckFrame{
|
|
AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}},
|
|
DelayTime: 5 * time.Minute,
|
|
}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 9*time.Minute, 1*time.Second))
|
|
})
|
|
})
|
|
|
|
Context("determining which ACKs we have received an ACK for", func() {
|
|
JustBeforeEach(func() {
|
|
morePackets := []*packet{
|
|
{
|
|
PacketNumber: 10,
|
|
LargestAcked: 100,
|
|
Frames: []Frame{{Frame: &streamFrame}},
|
|
Length: 1,
|
|
EncryptionLevel: protocol.Encryption1RTT,
|
|
},
|
|
{
|
|
PacketNumber: 11,
|
|
LargestAcked: 200,
|
|
Frames: []Frame{{Frame: &streamFrame}},
|
|
Length: 1,
|
|
EncryptionLevel: protocol.Encryption1RTT,
|
|
},
|
|
{
|
|
PacketNumber: 12,
|
|
Frames: []Frame{{Frame: &streamFrame}},
|
|
Length: 1,
|
|
EncryptionLevel: protocol.Encryption1RTT,
|
|
},
|
|
}
|
|
for _, packet := range morePackets {
|
|
sentPacket(packet)
|
|
}
|
|
})
|
|
|
|
It("determines which ACK we have received an ACK for", func() {
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 12}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(201)))
|
|
})
|
|
|
|
It("doesn't do anything when the acked packet didn't contain an ACK", func() {
|
|
ack1 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 10}}}
|
|
ack2 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 12, Largest: 12}}}
|
|
_, err := handler.ReceivedAck(ack1, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(101)))
|
|
_, err = handler.ReceivedAck(ack2, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(101)))
|
|
})
|
|
|
|
It("doesn't decrease the value", func() {
|
|
ack1 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 11, Largest: 11}}}
|
|
ack2 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 10}}}
|
|
_, err := handler.ReceivedAck(ack1, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(201)))
|
|
_, err = handler.ReceivedAck(ack2, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(201)))
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("congestion", func() {
|
|
var cong *mocks.MockSendAlgorithmWithDebugInfos
|
|
|
|
JustBeforeEach(func() {
|
|
cong = mocks.NewMockSendAlgorithmWithDebugInfos(mockCtrl)
|
|
handler.congestion = cong
|
|
})
|
|
|
|
It("should call OnSent", func() {
|
|
cong.EXPECT().OnPacketSent(
|
|
gomock.Any(),
|
|
protocol.ByteCount(42),
|
|
protocol.PacketNumber(1),
|
|
protocol.ByteCount(42),
|
|
true,
|
|
)
|
|
sentPacket(&packet{
|
|
PacketNumber: 1,
|
|
Length: 42,
|
|
Frames: []Frame{{Frame: &wire.PingFrame{}}},
|
|
EncryptionLevel: protocol.Encryption1RTT,
|
|
})
|
|
})
|
|
|
|
It("should call MaybeExitSlowStart and OnPacketAcked", func() {
|
|
rcvTime := time.Now().Add(-5 * time.Second)
|
|
cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3)
|
|
gomock.InOrder(
|
|
cong.EXPECT().MaybeExitSlowStart(), // must be called before packets are acked
|
|
cong.EXPECT().OnPacketAcked(protocol.PacketNumber(1), protocol.ByteCount(1), protocol.ByteCount(3), rcvTime),
|
|
cong.EXPECT().OnPacketAcked(protocol.PacketNumber(2), protocol.ByteCount(1), protocol.ByteCount(3), rcvTime),
|
|
)
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 1}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 2}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 3}))
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 2}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, rcvTime)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
It("doesn't call OnPacketAcked when a retransmitted packet is acked", func() {
|
|
cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(2)
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: time.Now().Add(-time.Hour)}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 2}))
|
|
// lose packet 1
|
|
gomock.InOrder(
|
|
cong.EXPECT().MaybeExitSlowStart(),
|
|
cong.EXPECT().OnPacketLost(protocol.PacketNumber(1), protocol.ByteCount(1), protocol.ByteCount(2)),
|
|
cong.EXPECT().OnPacketAcked(protocol.PacketNumber(2), protocol.ByteCount(1), protocol.ByteCount(2), gomock.Any()),
|
|
)
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
// don't EXPECT any further calls to the congestion controller
|
|
ack = &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 2}}}
|
|
_, err = handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
It("doesn't call OnPacketLost when a Path MTU probe packet is lost", func() {
|
|
cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(2)
|
|
var mtuPacketDeclaredLost bool
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: 1,
|
|
SendTime: time.Now().Add(-time.Hour),
|
|
IsPathMTUProbePacket: true,
|
|
Frames: []Frame{
|
|
{
|
|
Frame: &wire.PingFrame{},
|
|
Handler: &customFrameHandler{onLost: func(wire.Frame) { mtuPacketDeclaredLost = true }},
|
|
},
|
|
},
|
|
}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 2}))
|
|
// lose packet 1, but don't EXPECT any calls to OnPacketLost()
|
|
gomock.InOrder(
|
|
cong.EXPECT().MaybeExitSlowStart(),
|
|
cong.EXPECT().OnPacketAcked(protocol.PacketNumber(2), protocol.ByteCount(1), protocol.ByteCount(2), gomock.Any()),
|
|
)
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(mtuPacketDeclaredLost).To(BeTrue())
|
|
Expect(handler.bytesInFlight).To(BeZero())
|
|
})
|
|
|
|
It("calls OnPacketAcked and OnPacketLost with the right bytes_in_flight value", func() {
|
|
cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(4)
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: time.Now().Add(-time.Hour)}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, SendTime: time.Now().Add(-30 * time.Minute)}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 3, SendTime: time.Now().Add(-30 * time.Minute)}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 4, SendTime: time.Now()}))
|
|
// receive the first ACK
|
|
gomock.InOrder(
|
|
cong.EXPECT().MaybeExitSlowStart(),
|
|
cong.EXPECT().OnPacketLost(protocol.PacketNumber(1), protocol.ByteCount(1), protocol.ByteCount(4)),
|
|
cong.EXPECT().OnPacketAcked(protocol.PacketNumber(2), protocol.ByteCount(1), protocol.ByteCount(4), gomock.Any()),
|
|
)
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now().Add(-30*time.Minute))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
// receive the second ACK
|
|
gomock.InOrder(
|
|
cong.EXPECT().MaybeExitSlowStart(),
|
|
cong.EXPECT().OnPacketLost(protocol.PacketNumber(3), protocol.ByteCount(1), protocol.ByteCount(2)),
|
|
cong.EXPECT().OnPacketAcked(protocol.PacketNumber(4), protocol.ByteCount(1), protocol.ByteCount(2), gomock.Any()),
|
|
)
|
|
ack = &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 4, Largest: 4}}}
|
|
_, err = handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
It("passes the bytes in flight to the congestion controller", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
cong.EXPECT().OnPacketSent(gomock.Any(), protocol.ByteCount(42), gomock.Any(), protocol.ByteCount(42), true)
|
|
sentPacket(&packet{
|
|
Length: 42,
|
|
EncryptionLevel: protocol.EncryptionInitial,
|
|
Frames: []Frame{{Frame: &wire.PingFrame{}}},
|
|
SendTime: time.Now(),
|
|
})
|
|
cong.EXPECT().CanSend(protocol.ByteCount(42)).Return(true)
|
|
cong.EXPECT().HasPacingBudget(gomock.Any()).Return(true)
|
|
handler.SendMode(time.Now())
|
|
})
|
|
|
|
It("allows sending of ACKs when congestion limited", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
cong.EXPECT().CanSend(gomock.Any()).Return(true)
|
|
cong.EXPECT().HasPacingBudget(gomock.Any()).Return(true)
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
cong.EXPECT().CanSend(gomock.Any()).Return(false)
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAck))
|
|
})
|
|
|
|
It("allows sending of ACKs when we're keeping track of MaxOutstandingSentPackets packets", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
cong.EXPECT().CanSend(gomock.Any()).Return(true).AnyTimes()
|
|
cong.EXPECT().HasPacingBudget(gomock.Any()).Return(true).AnyTimes()
|
|
cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
|
for i := protocol.PacketNumber(0); i < protocol.MaxOutstandingSentPackets; i++ {
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: i}))
|
|
}
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAck))
|
|
})
|
|
|
|
It("allows PTOs, even when congestion limited", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
// note that we don't EXPECT a call to GetCongestionWindow
|
|
// that means retransmissions are sent without considering the congestion window
|
|
handler.numProbesToSend = 1
|
|
handler.ptoMode = SendPTOHandshake
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOHandshake))
|
|
})
|
|
|
|
It("returns the pacing delay", func() {
|
|
t := time.Now()
|
|
cong.EXPECT().TimeUntilSend(gomock.Any()).Return(t)
|
|
Expect(handler.TimeUntilSend()).To(Equal(t))
|
|
})
|
|
})
|
|
|
|
It("doesn't set an alarm if there are no outstanding packets", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 10}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 11}))
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 11}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeZero())
|
|
})
|
|
|
|
It("does nothing on OnAlarm if there are no outstanding packets", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
})
|
|
|
|
Context("probe packets", func() {
|
|
It("queues a probe packet", func() {
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 10}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 11}))
|
|
queued := handler.QueueProbePacket(protocol.Encryption1RTT)
|
|
Expect(queued).To(BeTrue())
|
|
Expect(lostPackets).To(Equal([]protocol.PacketNumber{10}))
|
|
})
|
|
|
|
It("says when it can't queue a probe packet", func() {
|
|
queued := handler.QueueProbePacket(protocol.Encryption1RTT)
|
|
Expect(queued).To(BeFalse())
|
|
})
|
|
|
|
It("implements exponential backoff", func() {
|
|
handler.peerAddressValidated = true
|
|
setHandshakeConfirmed()
|
|
sendTime := time.Now().Add(-time.Hour)
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: sendTime}))
|
|
timeout := handler.GetLossDetectionTimeout().Sub(sendTime)
|
|
Expect(handler.GetLossDetectionTimeout().Sub(sendTime)).To(Equal(timeout))
|
|
handler.ptoCount = 1
|
|
handler.setLossDetectionTimer()
|
|
Expect(handler.GetLossDetectionTimeout().Sub(sendTime)).To(Equal(2 * timeout))
|
|
handler.ptoCount = 2
|
|
handler.setLossDetectionTimer()
|
|
Expect(handler.GetLossDetectionTimeout().Sub(sendTime)).To(Equal(4 * timeout))
|
|
// truncated when the exponential gets too large
|
|
handler.ptoCount = 20
|
|
handler.setLossDetectionTimer()
|
|
Expect(handler.GetLossDetectionTimeout().Sub(sendTime)).To(Equal(maxPTODuration))
|
|
// protected from rollover
|
|
handler.ptoCount = 100
|
|
handler.setLossDetectionTimer()
|
|
Expect(handler.GetLossDetectionTimeout().Sub(sendTime)).To(Equal(maxPTODuration))
|
|
})
|
|
|
|
It("reset the PTO count when receiving an ACK", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
now := time.Now()
|
|
setHandshakeConfirmed()
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: now.Add(-time.Minute)}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, SendTime: now.Add(-time.Minute)}))
|
|
handler.appDataPackets.pns.(*skippingPacketNumberGenerator).next = 3
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeTemporally("~", now.Add(-time.Minute), time.Second))
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData))
|
|
Expect(handler.ptoCount).To(BeEquivalentTo(1))
|
|
_, err := handler.ReceivedAck(&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}}, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.ptoCount).To(BeZero())
|
|
})
|
|
|
|
It("resets the PTO mode and PTO count when a packet number space is dropped", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
|
|
now := time.Now()
|
|
handler.rttStats.UpdateRTT(time.Second/2, 0, now)
|
|
Expect(handler.rttStats.SmoothedRTT()).To(Equal(time.Second / 2))
|
|
Expect(handler.rttStats.PTO(true)).To(And(
|
|
BeNumerically(">", time.Second),
|
|
BeNumerically("<", 2*time.Second),
|
|
))
|
|
sendTimeHandshake := now.Add(-2 * time.Minute)
|
|
sendTimeAppData := now.Add(-time.Minute)
|
|
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: 1,
|
|
EncryptionLevel: protocol.EncryptionHandshake,
|
|
SendTime: sendTimeHandshake,
|
|
}))
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: 2,
|
|
SendTime: sendTimeAppData,
|
|
}))
|
|
|
|
// PTO timer based on the Handshake packet
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.ptoCount).To(BeEquivalentTo(1))
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOHandshake))
|
|
Expect(handler.GetLossDetectionTimeout()).To(Equal(sendTimeHandshake.Add(handler.rttStats.PTO(false) << 1)))
|
|
setHandshakeConfirmed()
|
|
handler.DropPackets(protocol.EncryptionHandshake)
|
|
// PTO timer based on the 1-RTT packet
|
|
Expect(handler.GetLossDetectionTimeout()).To(Equal(sendTimeAppData.Add(handler.rttStats.PTO(true)))) // no backoff. PTO count = 0
|
|
Expect(handler.SendMode(time.Now())).ToNot(Equal(SendPTOHandshake))
|
|
Expect(handler.ptoCount).To(BeZero())
|
|
})
|
|
|
|
It("allows two 1-RTT PTOs", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
setHandshakeConfirmed()
|
|
var lostPackets []protocol.PacketNumber
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT),
|
|
SendTime: time.Now().Add(-time.Hour),
|
|
Frames: []Frame{
|
|
{
|
|
Frame: &wire.PingFrame{},
|
|
Handler: &customFrameHandler{onLost: func(wire.Frame) { lostPackets = append(lostPackets, 1) }},
|
|
},
|
|
},
|
|
}))
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)}))
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)}))
|
|
Expect(handler.SendMode(time.Now())).ToNot(Equal(SendPTOAppData))
|
|
})
|
|
|
|
It("only counts ack-eliciting packets as probe packets", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
setHandshakeConfirmed()
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT),
|
|
SendTime: time.Now().Add(-time.Hour),
|
|
}))
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)}))
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData))
|
|
for i := 0; i < 30; i++ {
|
|
sentPacket(nonAckElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)}))
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData))
|
|
}
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)}))
|
|
Expect(handler.SendMode(time.Now())).ToNot(Equal(SendPTOAppData))
|
|
})
|
|
|
|
It("gets two probe packets if PTO expires", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
setHandshakeConfirmed()
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)}))
|
|
|
|
updateRTT(time.Hour)
|
|
Expect(handler.appDataPackets.lossTime.IsZero()).To(BeTrue())
|
|
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed()) // TLP
|
|
Expect(handler.ptoCount).To(BeEquivalentTo(1))
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)}))
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)}))
|
|
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed()) // PTO
|
|
Expect(handler.ptoCount).To(BeEquivalentTo(2))
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)}))
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)}))
|
|
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
})
|
|
|
|
It("gets two probe packets if PTO expires, for Handshake packets", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
sentPacket(initialPacket(&packet{PacketNumber: 1}))
|
|
sentPacket(initialPacket(&packet{PacketNumber: 2}))
|
|
|
|
updateRTT(time.Hour)
|
|
Expect(handler.initialPackets.lossTime.IsZero()).To(BeTrue())
|
|
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOInitial))
|
|
sentPacket(initialPacket(&packet{PacketNumber: 3}))
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOInitial))
|
|
sentPacket(initialPacket(&packet{PacketNumber: 4}))
|
|
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
})
|
|
|
|
It("doesn't send 1-RTT probe packets before the handshake completes", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)}))
|
|
updateRTT(time.Hour)
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeZero())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
setHandshakeConfirmed()
|
|
Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero())
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData))
|
|
})
|
|
|
|
It("resets the send mode when it receives an acknowledgement after queueing probe packets", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
setHandshakeConfirmed()
|
|
pn := handler.PopPacketNumber(protocol.Encryption1RTT)
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: pn, SendTime: time.Now().Add(-time.Hour)}))
|
|
updateRTT(time.Second)
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData))
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: pn, Largest: pn}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
})
|
|
|
|
It("handles ACKs for the original packet", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT),
|
|
SendTime: time.Now().Add(-time.Hour),
|
|
}))
|
|
updateRTT(time.Second)
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
})
|
|
|
|
It("doesn't set the PTO timer for Path MTU probe packets", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
setHandshakeConfirmed()
|
|
updateRTT(time.Second)
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 5, SendTime: time.Now(), IsPathMTUProbePacket: true}))
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeZero())
|
|
})
|
|
})
|
|
|
|
Context("amplification limit, for the server", func() {
|
|
It("limits the window to 3x the bytes received, to avoid amplification attacks", func() {
|
|
now := time.Now()
|
|
handler.ReceivedPacket(protocol.EncryptionInitial) // receiving an Initial packet doesn't validate the client's address
|
|
handler.ReceivedBytes(200)
|
|
sentPacket(&packet{
|
|
PacketNumber: 1,
|
|
Length: 599,
|
|
EncryptionLevel: protocol.EncryptionInitial,
|
|
Frames: []Frame{{Frame: &wire.PingFrame{}}},
|
|
SendTime: now,
|
|
})
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
sentPacket(&packet{
|
|
PacketNumber: 2,
|
|
Length: 1,
|
|
EncryptionLevel: protocol.EncryptionInitial,
|
|
Frames: []Frame{{Frame: &wire.PingFrame{}}},
|
|
SendTime: now,
|
|
})
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendNone))
|
|
})
|
|
|
|
It("cancels the loss detection timer when it is amplification limited, and resets it when becoming unblocked", func() {
|
|
handler.ReceivedBytes(300)
|
|
sentPacket(&packet{
|
|
PacketNumber: 1,
|
|
Length: 900,
|
|
EncryptionLevel: protocol.EncryptionInitial,
|
|
Frames: []Frame{{Frame: &wire.PingFrame{}}},
|
|
SendTime: time.Now(),
|
|
})
|
|
// Amplification limited. We don't need to set a timer now.
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeZero())
|
|
// Unblock the server. Now we should fire up the timer.
|
|
handler.ReceivedBytes(1)
|
|
Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero())
|
|
})
|
|
|
|
It("resets the loss detection timer when the client's address is validated", func() {
|
|
handler.ReceivedBytes(300)
|
|
sentPacket(&packet{
|
|
PacketNumber: 1,
|
|
Length: 900,
|
|
EncryptionLevel: protocol.EncryptionHandshake,
|
|
Frames: []Frame{{Frame: &wire.PingFrame{}}},
|
|
SendTime: time.Now(),
|
|
})
|
|
// Amplification limited. We don't need to set a timer now.
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeZero())
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero())
|
|
})
|
|
|
|
It("cancels the loss detection alarm when all Handshake packets are acknowledged", func() {
|
|
t := time.Now().Add(-time.Second)
|
|
handler.ReceivedBytes(99999)
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, SendTime: t}))
|
|
sentPacket(handshakePacket(&packet{PacketNumber: 3, SendTime: t}))
|
|
sentPacket(handshakePacket(&packet{PacketNumber: 4, SendTime: t}))
|
|
Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero())
|
|
handler.ReceivedAck(&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 3, Largest: 4}}}, protocol.EncryptionHandshake, time.Now())
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeZero())
|
|
})
|
|
})
|
|
|
|
Context("amplification limit, for the server, with validated address", func() {
|
|
JustBeforeEach(func() {
|
|
rttStats := utils.NewRTTStats()
|
|
handler = newSentPacketHandler(42, protocol.InitialPacketSizeIPv4, rttStats, true, perspective, nil, utils.DefaultLogger)
|
|
})
|
|
|
|
It("do not limits the window", func() {
|
|
handler.ReceivedBytes(0)
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
sentPacket(&packet{
|
|
PacketNumber: 1,
|
|
Length: 900,
|
|
EncryptionLevel: protocol.EncryptionInitial,
|
|
Frames: []Frame{{Frame: &wire.PingFrame{}}},
|
|
SendTime: time.Now(),
|
|
})
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
})
|
|
})
|
|
|
|
Context("amplification limit, for the client", func() {
|
|
BeforeEach(func() {
|
|
perspective = protocol.PerspectiveClient
|
|
})
|
|
|
|
It("sends an Initial packet to unblock the server", func() {
|
|
sentPacket(initialPacket(&packet{PacketNumber: 1}))
|
|
_, err := handler.ReceivedAck(
|
|
&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}},
|
|
protocol.EncryptionInitial,
|
|
time.Now(),
|
|
)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
// No packets are outstanding at this point.
|
|
// Make sure that a probe packet is sent.
|
|
Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero())
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOInitial))
|
|
|
|
// send a single packet to unblock the server
|
|
sentPacket(initialPacket(&packet{PacketNumber: 2}))
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
|
|
// Now receive an ACK for a Handshake packet.
|
|
// This tells the client that the server completed address validation.
|
|
sentPacket(handshakePacket(&packet{PacketNumber: 1}))
|
|
handler.DropPackets(protocol.EncryptionInitial) // sending a Handshake packet drops the Initial packet number space
|
|
_, err = handler.ReceivedAck(
|
|
&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}},
|
|
protocol.EncryptionHandshake,
|
|
time.Now(),
|
|
)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
// Make sure that no timer is set at this point.
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeZero())
|
|
})
|
|
|
|
It("sends a Handshake packet to unblock the server, if Initial keys were already dropped", func() {
|
|
sentPacket(initialPacket(&packet{PacketNumber: 1}))
|
|
_, err := handler.ReceivedAck(
|
|
&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}},
|
|
protocol.EncryptionInitial,
|
|
time.Now(),
|
|
)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
sentPacket(handshakePacketNonAckEliciting(&packet{PacketNumber: 1}))
|
|
handler.DropPackets(protocol.EncryptionInitial) // sending a Handshake packet drops the Initial packet number space
|
|
Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero())
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOHandshake))
|
|
|
|
// Now receive an ACK for this packet, and send another one.
|
|
_, err = handler.ReceivedAck(
|
|
&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}},
|
|
protocol.EncryptionHandshake,
|
|
time.Now(),
|
|
)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
sentPacket(handshakePacketNonAckEliciting(&packet{PacketNumber: 2}))
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeZero())
|
|
})
|
|
|
|
It("doesn't send a packet to unblock the server after handshake confirmation, even if no Handshake ACK was received", func() {
|
|
sentPacket(handshakePacket(&packet{PacketNumber: 1}))
|
|
Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero())
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOHandshake))
|
|
// confirm the handshake
|
|
handler.DropPackets(protocol.EncryptionHandshake)
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeZero())
|
|
})
|
|
|
|
It("correctly sets the timer after the Initial packet number space has been dropped", func() {
|
|
sentPacket(initialPacket(&packet{PacketNumber: 1, SendTime: time.Now().Add(-19 * time.Second)}))
|
|
_, err := handler.ReceivedAck(
|
|
&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}},
|
|
protocol.EncryptionInitial,
|
|
time.Now(),
|
|
)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
sentPacket(handshakePacketNonAckEliciting(&packet{PacketNumber: 1, SendTime: time.Now()}))
|
|
handler.DropPackets(protocol.EncryptionInitial) // sending a Handshake packet drops the Initial packet number space
|
|
|
|
pto := handler.rttStats.PTO(false)
|
|
Expect(pto).ToNot(BeZero())
|
|
// pto is approximately 19 * 3. Using a number > 19 above will
|
|
// run into the maxPTODuration limit
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeTemporally("~", time.Now().Add(pto), 10*time.Millisecond))
|
|
})
|
|
|
|
It("doesn't reset the PTO count when receiving an ACK", func() {
|
|
now := time.Now()
|
|
sentPacket(initialPacket(&packet{PacketNumber: 1, SendTime: now.Add(-time.Minute)}))
|
|
sentPacket(initialPacket(&packet{PacketNumber: 2, SendTime: now.Add(-time.Minute)}))
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeTemporally("~", now.Add(-time.Minute), time.Second))
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOInitial))
|
|
Expect(handler.ptoCount).To(BeEquivalentTo(1))
|
|
_, err := handler.ReceivedAck(&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}}, protocol.EncryptionInitial, time.Now())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.ptoCount).To(BeEquivalentTo(1))
|
|
})
|
|
})
|
|
|
|
Context("Packet-based loss detection", func() {
|
|
It("declares packet below the packet loss threshold as lost", func() {
|
|
now := time.Now()
|
|
for i := protocol.PacketNumber(1); i <= 6; i++ {
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: i}))
|
|
}
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 6, Largest: 6}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, now)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
expectInPacketHistory([]protocol.PacketNumber{4, 5}, protocol.Encryption1RTT)
|
|
Expect(lostPackets).To(Equal([]protocol.PacketNumber{1, 2, 3}))
|
|
})
|
|
})
|
|
|
|
Context("Delay-based loss detection", func() {
|
|
It("immediately detects old packets as lost when receiving an ACK", func() {
|
|
now := time.Now()
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: now.Add(-time.Hour)}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, SendTime: now.Add(-time.Second)}))
|
|
Expect(handler.appDataPackets.lossTime.IsZero()).To(BeTrue())
|
|
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, now)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
// no need to set an alarm, since packet 1 was already declared lost
|
|
Expect(handler.appDataPackets.lossTime.IsZero()).To(BeTrue())
|
|
Expect(handler.bytesInFlight).To(BeZero())
|
|
})
|
|
|
|
It("sets the early retransmit alarm", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
handler.handshakeConfirmed = true
|
|
now := time.Now()
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: now.Add(-2 * time.Second)}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, SendTime: now.Add(-2 * time.Second)}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 3, SendTime: now}))
|
|
Expect(handler.appDataPackets.lossTime.IsZero()).To(BeTrue())
|
|
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, now.Add(-time.Second))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.rttStats.SmoothedRTT()).To(Equal(time.Second))
|
|
|
|
// Packet 1 should be considered lost (1+1/8) RTTs after it was sent.
|
|
Expect(handler.GetLossDetectionTimeout().Sub(getPacket(1, protocol.Encryption1RTT).SendTime)).To(Equal(time.Second * 9 / 8))
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
|
|
expectInPacketHistory([]protocol.PacketNumber{1, 3}, protocol.Encryption1RTT)
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
expectInPacketHistory([]protocol.PacketNumber{3}, protocol.Encryption1RTT)
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
})
|
|
|
|
It("sets the early retransmit alarm for crypto packets", func() {
|
|
handler.ReceivedBytes(1000)
|
|
now := time.Now()
|
|
sentPacket(initialPacket(&packet{PacketNumber: 1, SendTime: now.Add(-2 * time.Second)}))
|
|
sentPacket(initialPacket(&packet{PacketNumber: 2, SendTime: now.Add(-2 * time.Second)}))
|
|
sentPacket(initialPacket(&packet{PacketNumber: 3, SendTime: now}))
|
|
Expect(handler.initialPackets.lossTime.IsZero()).To(BeTrue())
|
|
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.EncryptionInitial, now.Add(-time.Second))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(handler.rttStats.SmoothedRTT()).To(Equal(time.Second))
|
|
|
|
// Packet 1 should be considered lost (1+1/8) RTTs after it was sent.
|
|
Expect(handler.GetLossDetectionTimeout().Sub(getPacket(1, protocol.EncryptionInitial).SendTime)).To(Equal(time.Second * 9 / 8))
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
|
|
expectInPacketHistory([]protocol.PacketNumber{1, 3}, protocol.EncryptionInitial)
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
expectInPacketHistory([]protocol.PacketNumber{3}, protocol.EncryptionInitial)
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
})
|
|
|
|
It("sets the early retransmit alarm for Path MTU probe packets", func() {
|
|
var mtuPacketDeclaredLost bool
|
|
now := time.Now()
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: 1,
|
|
SendTime: now.Add(-3 * time.Second),
|
|
IsPathMTUProbePacket: true,
|
|
Frames: []Frame{
|
|
{
|
|
Frame: &wire.PingFrame{},
|
|
Handler: &customFrameHandler{onLost: func(wire.Frame) { mtuPacketDeclaredLost = true }},
|
|
},
|
|
},
|
|
}))
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, SendTime: now.Add(-3 * time.Second)}))
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, now.Add(-time.Second))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(mtuPacketDeclaredLost).To(BeFalse())
|
|
Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero())
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(mtuPacketDeclaredLost).To(BeTrue())
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeZero())
|
|
})
|
|
})
|
|
|
|
Context("crypto packets", func() {
|
|
It("rejects an ACK that acks packets with a higher encryption level", func() {
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: 13,
|
|
EncryptionLevel: protocol.Encryption1RTT,
|
|
}))
|
|
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 13, Largest: 13}}}
|
|
_, err := handler.ReceivedAck(ack, protocol.EncryptionHandshake, time.Now())
|
|
Expect(err).To(MatchError(&qerr.TransportError{
|
|
ErrorCode: qerr.ProtocolViolation,
|
|
ErrorMessage: "received ACK for an unsent packet",
|
|
}))
|
|
})
|
|
|
|
It("deletes Initial packets, as a server", func() {
|
|
for i := protocol.PacketNumber(0); i < 6; i++ {
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: i,
|
|
EncryptionLevel: protocol.EncryptionInitial,
|
|
}))
|
|
}
|
|
for i := protocol.PacketNumber(0); i < 10; i++ {
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: i,
|
|
EncryptionLevel: protocol.EncryptionHandshake,
|
|
}))
|
|
}
|
|
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(16)))
|
|
handler.DropPackets(protocol.EncryptionInitial)
|
|
Expect(lostPackets).To(BeEmpty()) // frames must not be queued for retransmission
|
|
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(10)))
|
|
Expect(handler.initialPackets).To(BeNil())
|
|
Expect(handler.handshakePackets.history.Len()).ToNot(BeZero())
|
|
})
|
|
|
|
It("deletes Handshake packets", func() {
|
|
for i := protocol.PacketNumber(0); i < 6; i++ {
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: i,
|
|
EncryptionLevel: protocol.EncryptionHandshake,
|
|
}))
|
|
}
|
|
for i := protocol.PacketNumber(0); i < 10; i++ {
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: i,
|
|
EncryptionLevel: protocol.Encryption1RTT,
|
|
}))
|
|
}
|
|
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(16)))
|
|
handler.DropPackets(protocol.EncryptionHandshake)
|
|
Expect(lostPackets).To(BeEmpty()) // frames must not be queued for retransmission
|
|
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(10)))
|
|
Expect(handler.handshakePackets).To(BeNil())
|
|
})
|
|
|
|
It("doesn't retransmit 0-RTT packets when 0-RTT keys are dropped", func() {
|
|
for i := protocol.PacketNumber(0); i < 6; i++ {
|
|
if i == 3 {
|
|
handler.appDataPackets.history.SkippedPacket(3)
|
|
continue
|
|
}
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: i,
|
|
EncryptionLevel: protocol.Encryption0RTT,
|
|
}))
|
|
}
|
|
for i := protocol.PacketNumber(6); i < 12; i++ {
|
|
sentPacket(ackElicitingPacket(&packet{PacketNumber: i}))
|
|
}
|
|
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(11)))
|
|
handler.DropPackets(protocol.Encryption0RTT)
|
|
Expect(lostPackets).To(BeEmpty())
|
|
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(6)))
|
|
})
|
|
|
|
It("cancels the PTO when dropping a packet number space", func() {
|
|
handler.ReceivedPacket(protocol.EncryptionHandshake)
|
|
now := time.Now()
|
|
sentPacket(handshakePacket(&packet{PacketNumber: 1, SendTime: now.Add(-time.Minute)}))
|
|
sentPacket(handshakePacket(&packet{PacketNumber: 2, SendTime: now.Add(-time.Minute)}))
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeTemporally("~", now.Add(-time.Minute), time.Second))
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOHandshake))
|
|
Expect(handler.ptoCount).To(BeEquivalentTo(1))
|
|
handler.DropPackets(protocol.EncryptionHandshake)
|
|
Expect(handler.ptoCount).To(BeZero())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
})
|
|
})
|
|
|
|
Context("peeking and popping packet number", func() {
|
|
It("peeks and pops the initial packet number", func() {
|
|
pn, _ := handler.PeekPacketNumber(protocol.EncryptionInitial)
|
|
Expect(pn).To(Equal(protocol.PacketNumber(42)))
|
|
Expect(handler.PopPacketNumber(protocol.EncryptionInitial)).To(Equal(protocol.PacketNumber(42)))
|
|
})
|
|
|
|
It("peeks and pops beyond the initial packet number", func() {
|
|
Expect(handler.PopPacketNumber(protocol.EncryptionInitial)).To(Equal(protocol.PacketNumber(42)))
|
|
Expect(handler.PopPacketNumber(protocol.EncryptionInitial)).To(BeNumerically(">", 42))
|
|
})
|
|
|
|
It("starts at 0 for handshake and application-data packet number space", func() {
|
|
pn, _ := handler.PeekPacketNumber(protocol.EncryptionHandshake)
|
|
Expect(pn).To(BeZero())
|
|
Expect(handler.PopPacketNumber(protocol.EncryptionHandshake)).To(BeZero())
|
|
pn, _ = handler.PeekPacketNumber(protocol.Encryption1RTT)
|
|
Expect(pn).To(BeZero())
|
|
Expect(handler.PopPacketNumber(protocol.Encryption1RTT)).To(BeZero())
|
|
})
|
|
})
|
|
|
|
Context("for the client", func() {
|
|
BeforeEach(func() {
|
|
perspective = protocol.PerspectiveClient
|
|
})
|
|
|
|
It("considers the server's address validated right away", func() {
|
|
})
|
|
|
|
It("queues outstanding packets for retransmission, cancels alarms and resets PTO count when receiving a Retry", func() {
|
|
sentPacket(initialPacket(&packet{PacketNumber: 42}))
|
|
Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero())
|
|
Expect(handler.bytesInFlight).ToNot(BeZero())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
// now receive a Retry
|
|
Expect(handler.ResetForRetry()).To(Succeed())
|
|
Expect(lostPackets).To(Equal([]protocol.PacketNumber{42}))
|
|
Expect(handler.bytesInFlight).To(BeZero())
|
|
Expect(handler.GetLossDetectionTimeout()).To(BeZero())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendAny))
|
|
Expect(handler.ptoCount).To(BeZero())
|
|
})
|
|
|
|
It("queues outstanding frames for retransmission and cancels alarms when receiving a Retry", func() {
|
|
var lostInitial, lost0RTT bool
|
|
sentPacket(&packet{
|
|
PacketNumber: 13,
|
|
EncryptionLevel: protocol.EncryptionInitial,
|
|
Frames: []Frame{
|
|
{
|
|
Frame: &wire.CryptoFrame{Data: []byte("foobar")},
|
|
Handler: &customFrameHandler{onLost: func(wire.Frame) { lostInitial = true }},
|
|
},
|
|
},
|
|
Length: 100,
|
|
})
|
|
pn := handler.PopPacketNumber(protocol.Encryption0RTT)
|
|
sentPacket(&packet{
|
|
PacketNumber: pn,
|
|
EncryptionLevel: protocol.Encryption0RTT,
|
|
Frames: []Frame{
|
|
{
|
|
Frame: &wire.StreamFrame{Data: []byte("foobar")},
|
|
Handler: &customFrameHandler{onLost: func(wire.Frame) { lost0RTT = true }},
|
|
},
|
|
},
|
|
Length: 999,
|
|
})
|
|
Expect(handler.bytesInFlight).ToNot(BeZero())
|
|
// now receive a Retry
|
|
Expect(handler.ResetForRetry()).To(Succeed())
|
|
Expect(handler.bytesInFlight).To(BeZero())
|
|
Expect(lostInitial).To(BeTrue())
|
|
Expect(lost0RTT).To(BeTrue())
|
|
|
|
// make sure we keep increasing the packet number for 0-RTT packets
|
|
Expect(handler.PopPacketNumber(protocol.Encryption0RTT)).To(BeNumerically(">", pn))
|
|
})
|
|
|
|
It("uses a Retry for an RTT estimate, if it was not retransmitted", func() {
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: 42,
|
|
EncryptionLevel: protocol.EncryptionInitial,
|
|
SendTime: time.Now().Add(-500 * time.Millisecond),
|
|
}))
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: 43,
|
|
EncryptionLevel: protocol.EncryptionInitial,
|
|
SendTime: time.Now().Add(-10 * time.Millisecond),
|
|
}))
|
|
Expect(handler.ResetForRetry()).To(Succeed())
|
|
Expect(handler.rttStats.SmoothedRTT()).To(BeNumerically("~", 500*time.Millisecond, 100*time.Millisecond))
|
|
})
|
|
|
|
It("uses a Retry for an RTT estimate, but doesn't set the RTT to a value lower than 5ms", func() {
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: 42,
|
|
EncryptionLevel: protocol.EncryptionInitial,
|
|
SendTime: time.Now().Add(-500 * time.Microsecond),
|
|
}))
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: 43,
|
|
EncryptionLevel: protocol.EncryptionInitial,
|
|
SendTime: time.Now().Add(-10 * time.Microsecond),
|
|
}))
|
|
Expect(handler.ResetForRetry()).To(Succeed())
|
|
Expect(handler.rttStats.SmoothedRTT()).To(Equal(minRTTAfterRetry))
|
|
})
|
|
|
|
It("doesn't use a Retry for an RTT estimate, if it was not retransmitted", func() {
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: 42,
|
|
EncryptionLevel: protocol.EncryptionInitial,
|
|
SendTime: time.Now().Add(-800 * time.Millisecond),
|
|
}))
|
|
Expect(handler.OnLossDetectionTimeout()).To(Succeed())
|
|
Expect(handler.SendMode(time.Now())).To(Equal(SendPTOInitial))
|
|
sentPacket(ackElicitingPacket(&packet{
|
|
PacketNumber: 43,
|
|
EncryptionLevel: protocol.EncryptionInitial,
|
|
SendTime: time.Now().Add(-100 * time.Millisecond),
|
|
}))
|
|
Expect(handler.ResetForRetry()).To(Succeed())
|
|
Expect(handler.rttStats.SmoothedRTT()).To(BeZero())
|
|
})
|
|
})
|
|
})
|