package ackhandler import ( "fmt" "time" "github.com/quic-go/quic-go/internal/mocks" "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/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) 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()) }) }) })