diff --git a/internal/ackhandler/sent_packet_handler.go b/internal/ackhandler/sent_packet_handler.go index a30d94531..78b13488c 100644 --- a/internal/ackhandler/sent_packet_handler.go +++ b/internal/ackhandler/sent_packet_handler.go @@ -24,6 +24,7 @@ type packetNumberSpace struct { history *sentPacketHistory pns *packetNumberGenerator + lossTime time.Time largestAcked protocol.PacketNumber largestSent protocol.PacketNumber } @@ -68,9 +69,6 @@ type sentPacketHandler struct { // Only applies to the application-data packet number space. numProbesToSend int - // The time at which the next packet will be considered lost based on early transmit or exceeding the reordering window in time. - lossTime time.Time - // The alarm timeout alarm time.Time @@ -314,6 +312,25 @@ func (h *sentPacketHandler) determineNewlyAckedPackets( return ackedPackets, err } +func (h *sentPacketHandler) getEarliestLossTime() (time.Time, protocol.EncryptionLevel) { + var encLevel protocol.EncryptionLevel + var lossTime time.Time + + if h.initialPackets != nil { + lossTime = h.initialPackets.lossTime + encLevel = protocol.EncryptionInitial + } + if h.handshakePackets != nil && (lossTime.IsZero() || h.handshakePackets.lossTime.Before(lossTime)) { + lossTime = h.handshakePackets.lossTime + encLevel = protocol.EncryptionHandshake + } + if lossTime.IsZero() || h.oneRTTPackets.lossTime.Before(lossTime) { + lossTime = h.oneRTTPackets.lossTime + encLevel = protocol.Encryption1RTT + } + return lossTime, encLevel +} + func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool { var hasInitial, hasHandshake bool if h.initialPackets != nil { @@ -336,11 +353,13 @@ func (h *sentPacketHandler) setLossDetectionTimer() { return } + lossTime, _ := h.getEarliestLossTime() + if h.hasOutstandingCryptoPackets() { h.alarm = h.lastSentCryptoPacketTime.Add(h.computeCryptoTimeout()) - } else if !h.lossTime.IsZero() { + } else if !lossTime.IsZero() { // Early retransmit timer or time loss detection. - h.alarm = h.lossTime + h.alarm = lossTime } else { // PTO alarm h.alarm = h.lastSentAckElicitingPacketTime.Add(h.rttStats.PTO() << h.ptoCount) } @@ -351,10 +370,8 @@ func (h *sentPacketHandler) detectLostPackets( encLevel protocol.EncryptionLevel, priorInFlight protocol.ByteCount, ) error { - if encLevel == protocol.Encryption1RTT { - h.lossTime = time.Time{} - } pnSpace := h.getPacketNumberSpace(encLevel) + pnSpace.lossTime = time.Time{} maxRTT := float64(utils.MaxDuration(h.rttStats.LatestRTT(), h.rttStats.SmoothedRTT())) lossDelay := time.Duration(timeThreshold * maxRTT) @@ -371,12 +388,12 @@ func (h *sentPacketHandler) detectLostPackets( timeSinceSent := now.Sub(packet.SendTime) if timeSinceSent > lossDelay { lostPackets = append(lostPackets, packet) - } else if h.lossTime.IsZero() && encLevel == protocol.Encryption1RTT { + } else if pnSpace.lossTime.IsZero() && encLevel == protocol.Encryption1RTT { if h.logger.Debug() { h.logger.Debugf("\tsetting loss timer for packet %#x to %s (in %s)", packet.PacketNumber, lossDelay, lossDelay-timeSinceSent) } // Note: This conditional is only entered once per call - h.lossTime = now.Add(lossDelay - timeSinceSent) + pnSpace.lossTime = now.Add(lossDelay - timeSinceSent) } return true, nil }) @@ -433,18 +450,19 @@ func (h *sentPacketHandler) OnLossDetectionTimeout() error { func (h *sentPacketHandler) onVerifiedLossDetectionTimeout() error { var err error - if h.hasOutstandingCryptoPackets() { + lossTime, encLevel := h.getEarliestLossTime() + if !lossTime.IsZero() { + if h.logger.Debug() { + h.logger.Debugf("Loss detection alarm fired in loss timer mode. Loss time: %s", lossTime) + } + // Early retransmit or time loss detection + err = h.detectLostPackets(time.Now(), encLevel, h.bytesInFlight) + } else if h.hasOutstandingCryptoPackets() { if h.logger.Debug() { h.logger.Debugf("Loss detection alarm fired in crypto mode. Crypto count: %d", h.cryptoCount) } h.cryptoCount++ err = h.queueCryptoPacketsForRetransmission() - } else if !h.lossTime.IsZero() { - if h.logger.Debug() { - h.logger.Debugf("Loss detection alarm fired in loss timer mode. Loss time: %s", h.lossTime) - } - // Early retransmit or time loss detection - err = h.detectLostPackets(time.Now(), protocol.Encryption1RTT, h.bytesInFlight) } else { // PTO if h.logger.Debug() { h.logger.Debugf("Loss detection alarm fired in PTO mode. PTO count: %d", h.ptoCount) diff --git a/internal/ackhandler/sent_packet_handler_test.go b/internal/ackhandler/sent_packet_handler_test.go index 88a4294c6..1b2b50603 100644 --- a/internal/ackhandler/sent_packet_handler_test.go +++ b/internal/ackhandler/sent_packet_handler_test.go @@ -655,7 +655,7 @@ var _ = Describe("SentPacketHandler", func() { handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 2})) updateRTT(time.Hour) - Expect(handler.lossTime.IsZero()).To(BeTrue()) + Expect(handler.oneRTTPackets.lossTime.IsZero()).To(BeTrue()) handler.OnLossDetectionTimeout() // TLP handler.OnLossDetectionTimeout() // TLP @@ -761,15 +761,29 @@ var _ = Describe("SentPacketHandler", func() { now := time.Now() handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1, SendTime: now.Add(-time.Hour)})) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 2, SendTime: now.Add(-time.Second)})) - Expect(handler.lossTime.IsZero()).To(BeTrue()) + Expect(handler.oneRTTPackets.lossTime.IsZero()).To(BeTrue()) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}} - err := handler.ReceivedAck(ack, 1, protocol.Encryption1RTT, now) - Expect(err).NotTo(HaveOccurred()) + Expect(handler.ReceivedAck(ack, 1, protocol.Encryption1RTT, now)).To(Succeed()) Expect(handler.DequeuePacketForRetransmission()).ToNot(BeNil()) Expect(handler.DequeuePacketForRetransmission()).To(BeNil()) // no need to set an alarm, since packet 1 was already declared lost - Expect(handler.lossTime.IsZero()).To(BeTrue()) + Expect(handler.oneRTTPackets.lossTime.IsZero()).To(BeTrue()) + Expect(handler.bytesInFlight).To(BeZero()) + }) + + It("uses early retransmit for crypto packets", func() { + now := time.Now() + handler.SentPacket(cryptoPacket(&Packet{PacketNumber: 1, SendTime: now.Add(-time.Hour)})) + handler.SentPacket(cryptoPacket(&Packet{PacketNumber: 2, SendTime: now.Add(-time.Second)})) + Expect(handler.oneRTTPackets.lossTime.IsZero()).To(BeTrue()) + + ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}} + Expect(handler.ReceivedAck(ack, 1, protocol.EncryptionInitial, now)).To(Succeed()) + Expect(handler.DequeuePacketForRetransmission()).ToNot(BeNil()) + Expect(handler.DequeuePacketForRetransmission()).To(BeNil()) + // no need to set an alarm, since packet 1 was already declared lost + Expect(handler.initialPackets.lossTime.IsZero()).To(BeTrue()) Expect(handler.bytesInFlight).To(BeZero()) }) @@ -778,15 +792,15 @@ var _ = Describe("SentPacketHandler", func() { handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1, SendTime: now.Add(-2 * time.Second), EncryptionLevel: protocol.Encryption1RTT})) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 2, SendTime: now.Add(-2 * time.Second), EncryptionLevel: protocol.Encryption1RTT})) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 3, SendTime: now.Add(-time.Second), EncryptionLevel: protocol.Encryption1RTT})) - Expect(handler.lossTime.IsZero()).To(BeTrue()) + Expect(handler.oneRTTPackets.lossTime.IsZero()).To(BeTrue()) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}} Expect(handler.ReceivedAck(ack, 1, protocol.Encryption1RTT, now.Add(-time.Second))).To(Succeed()) Expect(handler.rttStats.SmoothedRTT()).To(Equal(time.Second)) // Packet 1 should be considered lost (1+1/8) RTTs after it was sent. - Expect(handler.lossTime.IsZero()).To(BeFalse()) - Expect(handler.lossTime.Sub(getPacket(1, protocol.Encryption1RTT).SendTime)).To(Equal(time.Second * 9 / 8)) + Expect(handler.oneRTTPackets.lossTime.IsZero()).To(BeFalse()) + Expect(handler.oneRTTPackets.lossTime.Sub(getPacket(1, protocol.Encryption1RTT).SendTime)).To(Equal(time.Second * 9 / 8)) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.DequeuePacketForRetransmission()).NotTo(BeNil()) @@ -810,7 +824,7 @@ var _ = Describe("SentPacketHandler", func() { Expect(handler.ReceivedAck(ack, 1, protocol.EncryptionInitial, now)).To(Succeed()) // RTT is now 1 minute Expect(handler.rttStats.SmoothedRTT()).To(Equal(time.Minute)) - Expect(handler.lossTime.IsZero()).To(BeTrue()) + Expect(handler.oneRTTPackets.lossTime.IsZero()).To(BeTrue()) Expect(handler.GetLossDetectionTimeout().Sub(sendTime)).To(Equal(2 * time.Minute)) Expect(handler.OnLossDetectionTimeout()).To(Succeed())