diff --git a/connection.go b/connection.go index 2d67c151..df0ca314 100644 --- a/connection.go +++ b/connection.go @@ -2194,7 +2194,7 @@ func (s *connection) registerPackedShortHeaderPacket(p shortHeaderPacket, ecn pr if p.Ack != nil { largestAcked = p.Ack.LargestAcked() } - s.sentPacketHandler.SentPacket(now, p.PacketNumber, largestAcked, p.StreamFrames, p.Frames, protocol.Encryption1RTT, ecn, p.Length, p.IsPathMTUProbePacket) + s.sentPacketHandler.SentPacket(now, p.PacketNumber, largestAcked, p.StreamFrames, p.Frames, protocol.Encryption1RTT, ecn, p.Length, p.IsPathMTUProbePacket, false) s.connIDManager.SentPacket() } @@ -2208,7 +2208,7 @@ func (s *connection) sendPackedCoalescedPacket(packet *coalescedPacket, ecn prot if p.ack != nil { largestAcked = p.ack.LargestAcked() } - s.sentPacketHandler.SentPacket(now, p.header.PacketNumber, largestAcked, p.streamFrames, p.frames, p.EncryptionLevel(), ecn, p.length, false) + s.sentPacketHandler.SentPacket(now, p.header.PacketNumber, largestAcked, p.streamFrames, p.frames, p.EncryptionLevel(), ecn, p.length, false, false) if s.perspective == protocol.PerspectiveClient && p.EncryptionLevel() == protocol.EncryptionHandshake && !s.droppedInitialKeys { // On the client side, Initial keys are dropped as soon as the first Handshake packet is sent. @@ -2226,7 +2226,7 @@ func (s *connection) sendPackedCoalescedPacket(packet *coalescedPacket, ecn prot if p.Ack != nil { largestAcked = p.Ack.LargestAcked() } - s.sentPacketHandler.SentPacket(now, p.PacketNumber, largestAcked, p.StreamFrames, p.Frames, protocol.Encryption1RTT, ecn, p.Length, p.IsPathMTUProbePacket) + s.sentPacketHandler.SentPacket(now, p.PacketNumber, largestAcked, p.StreamFrames, p.Frames, protocol.Encryption1RTT, ecn, p.Length, p.IsPathMTUProbePacket, false) } s.connIDManager.SentPacket() s.sendQueue.Send(packet.buffer, 0, ecn) diff --git a/connection_test.go b/connection_test.go index 18541fc9..57f49a03 100644 --- a/connection_test.go +++ b/connection_test.go @@ -1635,15 +1635,15 @@ func TestConnectionPacketPacing(t *testing.T) { gomock.InOrder( // 1. allow 2 packets to be sent sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny), - sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()), + sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()), sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny), - sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()), + sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()), sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendPacingLimited), // 2. become pacing limited for 25ms sph.EXPECT().TimeUntilSend().DoAndReturn(func() time.Time { return time.Now().Add(step) }), // 3. send another packet sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny), - sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()), + sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()), sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendPacingLimited), // 4. become pacing limited for 25ms... sph.EXPECT().TimeUntilSend().DoAndReturn(func() time.Time { return time.Now().Add(step) }), @@ -1652,7 +1652,7 @@ func TestConnectionPacketPacing(t *testing.T) { sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendPacingLimited), // 5. stop the test by becoming pacing limited forever sph.EXPECT().TimeUntilSend().Return(time.Now().Add(time.Hour)), - sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()), + sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()), ) sph.EXPECT().ECNMode(gomock.Any()).AnyTimes() for i := 0; i < 3; i++ { @@ -1743,7 +1743,7 @@ func TestConnectionIdleTimeout(t *testing.T) { sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() - sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) + sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) sph.EXPECT().ECNMode(gomock.Any()).AnyTimes() var lastSendTime time.Time tc.packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( @@ -1890,7 +1890,7 @@ func TestConnectionACKTimer(t *testing.T) { sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() - sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() sph.EXPECT().ECNMode(gomock.Any()).AnyTimes() rph.EXPECT().GetAlarmTimeout().Return(time.Now().Add(time.Hour)) tc.sendConn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() @@ -1960,7 +1960,7 @@ func TestConnectionGSOBatch(t *testing.T) { // allow packets to be sent sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().TimeUntilSend().Return(time.Time{}).AnyTimes() - sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() sph.EXPECT().GetLossDetectionTimeout().Return(time.Time{}).AnyTimes() sph.EXPECT().ECNMode(gomock.Any()).Return(protocol.ECT1).AnyTimes() @@ -2019,7 +2019,7 @@ func TestConnectionGSOBatchPacketSize(t *testing.T) { // allow packets to be sent sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().TimeUntilSend().Return(time.Time{}).AnyTimes() - sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() sph.EXPECT().GetLossDetectionTimeout().Return(time.Time{}).AnyTimes() sph.EXPECT().ECNMode(gomock.Any()).Return(protocol.ECT1).AnyTimes() @@ -2099,7 +2099,7 @@ func TestConnectionGSOBatchECN(t *testing.T) { ecnMode := protocol.ECT1 sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().TimeUntilSend().Return(time.Time{}).AnyTimes() - sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() sph.EXPECT().GetLossDetectionTimeout().Return(time.Time{}).AnyTimes() sph.EXPECT().ECNMode(gomock.Any()).DoAndReturn(func(bool) protocol.ECN { return ecnMode }).AnyTimes() @@ -2201,7 +2201,7 @@ func testConnectionPTOProbePackets(t *testing.T, encLevel protocol.EncryptionLev sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendNone) sph.EXPECT().ECNMode(gomock.Any()) sph.EXPECT().QueueProbePacket(encLevel).Return(false) - sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) + sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) tc.packer.EXPECT().MaybePackPTOProbePacket(encLevel, gomock.Any(), gomock.Any(), protocol.Version1).DoAndReturn( func(encLevel protocol.EncryptionLevel, maxSize protocol.ByteCount, t time.Time, version protocol.Version) (*coalescedPacket, error) { @@ -2254,7 +2254,7 @@ func TestConnectionCongestionControl(t *testing.T) { sph.EXPECT().ECNMode(true).AnyTimes() sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).Times(2) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAck).MaxTimes(1) - sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(2) + sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(2) // Since we're already sending out packets, we don't expect any calls to PackAckOnlyPacket for i := 0; i < 2; i++ { tc.packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( @@ -2347,7 +2347,7 @@ func testConnectionSendQueue(t *testing.T, enableGSO bool) { }, ) sph.EXPECT().GetLossDetectionTimeout().AnyTimes() - sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) + sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().ECNMode(gomock.Any()).AnyTimes() tc.packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return( diff --git a/internal/ackhandler/interfaces.go b/internal/ackhandler/interfaces.go index acf95426..5fcce44d 100644 --- a/internal/ackhandler/interfaces.go +++ b/internal/ackhandler/interfaces.go @@ -10,7 +10,7 @@ import ( // SentPacketHandler handles ACKs received for outgoing packets type SentPacketHandler interface { // SentPacket may modify the packet - SentPacket(t time.Time, pn, largestAcked protocol.PacketNumber, streamFrames []StreamFrame, frames []Frame, encLevel protocol.EncryptionLevel, ecn protocol.ECN, size protocol.ByteCount, isPathMTUProbePacket bool) + SentPacket(t time.Time, pn, largestAcked protocol.PacketNumber, streamFrames []StreamFrame, frames []Frame, encLevel protocol.EncryptionLevel, ecn protocol.ECN, size protocol.ByteCount, isPathMTUProbePacket, isPathProbePacket bool) // ReceivedAck processes an ACK frame. // It does not store a copy of the frame. ReceivedAck(f *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) (bool /* 1-RTT packet acked */, error) @@ -34,6 +34,8 @@ type SentPacketHandler interface { GetLossDetectionTimeout() time.Time OnLossDetectionTimeout(now time.Time) error + + MigratedPath(now time.Time, initialMaxPacketSize protocol.ByteCount) } type sentPacketTracker interface { diff --git a/internal/ackhandler/sent_packet_handler.go b/internal/ackhandler/sent_packet_handler.go index 6730de0e..f8b8cd92 100644 --- a/internal/ackhandler/sent_packet_handler.go +++ b/internal/ackhandler/sent_packet_handler.go @@ -27,6 +27,9 @@ const ( maxPTODuration = 60 * time.Second ) +// Path probe packets are declared lost after this time. +const pathProbePacketLossTimeout = time.Second + type packetNumberSpace struct { history sentPacketHistory pns packetNumberGenerator @@ -249,11 +252,12 @@ func (h *sentPacketHandler) SentPacket( ecn protocol.ECN, size protocol.ByteCount, isPathMTUProbePacket bool, + isPathProbePacket bool, ) { h.bytesSent += size pnSpace := h.getPacketNumberSpace(encLevel) - if h.logger.Debug() && pnSpace.history.HasOutstandingPackets() { + if h.logger.Debug() && (pnSpace.history.HasOutstandingPackets() || pnSpace.history.HasOutstandingPathProbes()) { for p := max(0, pnSpace.largestSent+1); p < pn; p++ { h.logger.Debugf("Skipping packet number %d", p) } @@ -262,6 +266,18 @@ func (h *sentPacketHandler) SentPacket( pnSpace.largestSent = pn isAckEliciting := len(streamFrames) > 0 || len(frames) > 0 + if isPathProbePacket { + p := getPacket() + p.SendTime = t + p.PacketNumber = pn + p.EncryptionLevel = encLevel + p.Length = size + p.Frames = frames + p.isPathProbePacket = true + pnSpace.history.SentPathProbePacket(p) + h.setLossDetectionTimer(t) + return + } if isAckEliciting { pnSpace.lastAckElicitingPacketTime = t h.bytesInFlight += size @@ -341,7 +357,7 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En } // update the RTT, if the largest acked is newly acknowledged if len(ackedPackets) > 0 { - if p := ackedPackets[len(ackedPackets)-1]; p.PacketNumber == ack.LargestAcked() { + if p := ackedPackets[len(ackedPackets)-1]; p.PacketNumber == ack.LargestAcked() && !p.isPathProbePacket { // don't use the ack delay for Initial and Handshake packets var ackDelay time.Duration if encLevel == protocol.Encryption1RTT { @@ -366,6 +382,9 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En pnSpace.largestAcked = max(pnSpace.largestAcked, largestAcked) h.detectLostPackets(rcvTime, encLevel) + if encLevel == protocol.Encryption1RTT { + h.detectLostPathProbes(rcvTime) + } var acked1RTTPacket bool for _, p := range ackedPackets { if p.includedInBytesInFlight && !p.declaredLost { @@ -375,7 +394,9 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En acked1RTTPacket = true } h.removeFromBytesInFlight(p) - putPacket(p) + if !p.isPathProbePacket { + putPacket(p) + } } // After this point, we must not use ackedPackets any longer! // We've already returned the buffers. @@ -411,11 +432,11 @@ func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encL largestAcked := ack.LargestAcked() var processErr error pnSpace.history.Iterate(func(p *packet) bool { - // Ignore packets below the lowest acked + // ignore packets below the lowest acked if p.PacketNumber < lowestAcked { return true } - // Break after largest acked is reached + // break after largest acked is reached if p.PacketNumber > largestAcked { return false } @@ -443,7 +464,15 @@ func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encL } return false } - h.ackedPackets = append(h.ackedPackets, p) + if p.isPathProbePacket { + probePacket := pnSpace.history.RemovePathProbe(p.PacketNumber) + if probePacket == nil { + panic(fmt.Sprintf("path probe doesn't exist: %d", p.PacketNumber)) + } + h.ackedPackets = append(h.ackedPackets, probePacket) + } else { + h.ackedPackets = append(h.ackedPackets, p) + } return true }) if h.logger.Debug() && len(h.ackedPackets) > 0 { @@ -510,7 +539,7 @@ func (h *sentPacketHandler) getScaledPTO(includeMaxAckDelay bool) time.Duration } // same logic as getLossTimeAndSpace, but for lastAckElicitingPacketTime instead of lossTime -func (h *sentPacketHandler) getPTOTimeAndSpace(now time.Time) (pto time.Time, encLevel protocol.EncryptionLevel, ok bool) { +func (h *sentPacketHandler) getPTOTimeAndSpace(now time.Time) (pto time.Time, encLevel protocol.EncryptionLevel) { // We only send application data probe packets once the handshake is confirmed, // because before that, we don't have the keys to decrypt ACKs sent in 1-RTT packets. if !h.handshakeConfirmed && !h.hasOutstandingCryptoPackets() { @@ -519,32 +548,35 @@ func (h *sentPacketHandler) getPTOTimeAndSpace(now time.Time) (pto time.Time, en } t := now.Add(h.getScaledPTO(false)) if h.initialPackets != nil { - return t, protocol.EncryptionInitial, true + return t, protocol.EncryptionInitial } - return t, protocol.EncryptionHandshake, true + return t, protocol.EncryptionHandshake } - if h.initialPackets != nil { + if h.initialPackets != nil && h.initialPackets.history.HasOutstandingPackets() && + !h.initialPackets.lastAckElicitingPacketTime.IsZero() { encLevel = protocol.EncryptionInitial if t := h.initialPackets.lastAckElicitingPacketTime; !t.IsZero() { pto = t.Add(h.getScaledPTO(false)) } } - if h.handshakePackets != nil && !h.handshakePackets.lastAckElicitingPacketTime.IsZero() { + if h.handshakePackets != nil && h.handshakePackets.history.HasOutstandingPackets() && + !h.handshakePackets.lastAckElicitingPacketTime.IsZero() { t := h.handshakePackets.lastAckElicitingPacketTime.Add(h.getScaledPTO(false)) if pto.IsZero() || (!t.IsZero() && t.Before(pto)) { pto = t encLevel = protocol.EncryptionHandshake } } - if h.handshakeConfirmed && !h.appDataPackets.lastAckElicitingPacketTime.IsZero() { + if h.handshakeConfirmed && h.appDataPackets.history.HasOutstandingPackets() && + !h.appDataPackets.lastAckElicitingPacketTime.IsZero() { t := h.appDataPackets.lastAckElicitingPacketTime.Add(h.getScaledPTO(true)) if pto.IsZero() || (!t.IsZero() && t.Before(pto)) { pto = t encLevel = protocol.Encryption1RTT } } - return pto, encLevel, true + return pto, encLevel } func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool { @@ -576,8 +608,8 @@ func (h *sentPacketHandler) setLossDetectionTimer(now time.Time) { func (h *sentPacketHandler) lossDetectionTime(now time.Time) alarmTimer { // cancel the alarm if no packets are outstanding - if h.peerCompletedAddressValidation && - !h.hasOutstandingCryptoPackets() && !h.appDataPackets.history.HasOutstandingPackets() { + if h.peerCompletedAddressValidation && !h.hasOutstandingCryptoPackets() && + !h.appDataPackets.history.HasOutstandingPackets() && !h.appDataPackets.history.HasOutstandingPathProbes() { return alarmTimer{} } @@ -586,24 +618,58 @@ func (h *sentPacketHandler) lossDetectionTime(now time.Time) alarmTimer { return alarmTimer{} } + var pathProbeLossTime time.Time + if h.appDataPackets.history.HasOutstandingPathProbes() { + if p := h.appDataPackets.history.FirstOutstandingPathProbe(); p != nil { + pathProbeLossTime = p.SendTime.Add(pathProbePacketLossTimeout) + } + } + // early retransmit timer or time loss detection lossTime, encLevel := h.getLossTimeAndSpace() - if !lossTime.IsZero() { + if !lossTime.IsZero() && (pathProbeLossTime.IsZero() || lossTime.Before(pathProbeLossTime)) { return alarmTimer{ Time: lossTime, TimerType: logging.TimerTypeACK, EncryptionLevel: encLevel, } } - - ptoTime, encLevel, ok := h.getPTOTimeAndSpace(now) - if !ok { - return alarmTimer{} + ptoTime, encLevel := h.getPTOTimeAndSpace(now) + if !ptoTime.IsZero() && (pathProbeLossTime.IsZero() || ptoTime.Before(pathProbeLossTime)) { + return alarmTimer{ + Time: ptoTime, + TimerType: logging.TimerTypePTO, + EncryptionLevel: encLevel, + } } - return alarmTimer{ - Time: ptoTime, - TimerType: logging.TimerTypePTO, - EncryptionLevel: encLevel, + if !pathProbeLossTime.IsZero() { + return alarmTimer{ + Time: pathProbeLossTime, + TimerType: logging.TimerTypePathProbe, + EncryptionLevel: encLevel, + } + } + return alarmTimer{} +} + +func (h *sentPacketHandler) detectLostPathProbes(now time.Time) { + if !h.appDataPackets.history.HasOutstandingPathProbes() { + return + } + lossTime := now.Add(-pathProbePacketLossTimeout) + // RemovePathProbe cannot be called while iterating. + var lostPathProbes []*packet + h.appDataPackets.history.IteratePathProbes(func(p *packet) bool { + if !p.SendTime.After(lossTime) { + lostPathProbes = append(lostPathProbes, p) + } + return true + }) + for _, p := range lostPathProbes { + for _, f := range p.Frames { + f.Handler.OnLost(f.Frame) + } + h.appDataPackets.history.RemovePathProbe(p.PacketNumber) } } @@ -626,10 +692,11 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E return false } + isRegularPacket := !p.skippedPacket && !p.isPathProbePacket var packetLost bool if !p.SendTime.After(lostSendTime) { packetLost = true - if !p.skippedPacket { + if isRegularPacket { if h.logger.Debug() { h.logger.Debugf("\tlost packet %d (time threshold)", p.PacketNumber) } @@ -639,7 +706,7 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E } } else if pnSpace.largestAcked >= p.PacketNumber+packetThreshold { packetLost = true - if !p.skippedPacket { + if isRegularPacket { if h.logger.Debug() { h.logger.Debugf("\tlost packet %d (reordering threshold)", p.PacketNumber) } @@ -657,7 +724,7 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E } if packetLost { pnSpace.history.DeclareLost(p.PacketNumber) - if !p.skippedPacket { + if isRegularPacket { // the bytes in flight need to be reduced no matter if the frames in this packet will be retransmitted h.removeFromBytesInFlight(p) h.queueFramesForRetransmission(p) @@ -675,6 +742,11 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E func (h *sentPacketHandler) OnLossDetectionTimeout(now time.Time) error { defer h.setLossDetectionTimer(now) + + if h.handshakeConfirmed { + h.detectLostPathProbes(now) + } + earliestLossTime, encLevel := h.getLossTimeAndSpace() if !earliestLossTime.IsZero() { if h.logger.Debug() { @@ -706,11 +778,12 @@ func (h *sentPacketHandler) OnLossDetectionTimeout(now time.Time) error { return nil } - _, encLevel, ok := h.getPTOTimeAndSpace(now) - if !ok { + ptoTime, encLevel := h.getPTOTimeAndSpace(now) + if ptoTime.IsZero() { return nil } - if ps := h.getPacketNumberSpace(encLevel); !ps.history.HasOutstandingPackets() && !h.peerCompletedAddressValidation { + ps := h.getPacketNumberSpace(encLevel) + if !ps.history.HasOutstandingPackets() && !ps.history.HasOutstandingPathProbes() && !h.peerCompletedAddressValidation { return nil } h.ptoCount++ @@ -917,3 +990,27 @@ func (h *sentPacketHandler) ResetForRetry(now time.Time) { } h.ptoCount = 0 } + +func (h *sentPacketHandler) MigratedPath(now time.Time, initialMaxDatagramSize protocol.ByteCount) { + h.rttStats.ResetForPathMigration() + h.appDataPackets.history.Iterate(func(p *packet) bool { + h.appDataPackets.history.DeclareLost(p.PacketNumber) + if !p.skippedPacket && !p.isPathProbePacket { + h.removeFromBytesInFlight(p) + h.queueFramesForRetransmission(p) + } + return true + }) + h.appDataPackets.history.IteratePathProbes(func(p *packet) bool { + h.appDataPackets.history.RemovePathProbe(p.PacketNumber) + return true + }) + h.congestion = congestion.NewCubicSender( + congestion.DefaultClock{}, + h.rttStats, + initialMaxDatagramSize, + true, // use Reno + h.tracer, + ) + h.setLossDetectionTimer(now) +} diff --git a/internal/ackhandler/sent_packet_handler_test.go b/internal/ackhandler/sent_packet_handler_test.go index a51b76cf..15a4eabd 100644 --- a/internal/ackhandler/sent_packet_handler_test.go +++ b/internal/ackhandler/sent_packet_handler_test.go @@ -117,7 +117,7 @@ func testSentPacketHandlerSendAndAcknowledge(t *testing.T, encLevel protocol.Enc e = protocol.Encryption0RTT } pn := sph.PopPacketNumber(e) - sph.SentPacket(now, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, e, protocol.ECNNon, 1200, false) + sph.SentPacket(now, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, e, protocol.ECNNon, 1200, false, false) pns = append(pns, pn) } @@ -172,7 +172,7 @@ func TestSentPacketHandlerAcknowledgeSkippedPacket(t *testing.T) { if pn >= 1e6 { t.Fatal("expected a skipped packet number") } - sph.SentPacket(now, pn, protocol.InvalidPacketNumber, nil, []Frame{{Frame: &wire.PingFrame{}}}, protocol.Encryption1RTT, protocol.ECNNon, 1200, false) + sph.SentPacket(now, pn, protocol.InvalidPacketNumber, nil, []Frame{{Frame: &wire.PingFrame{}}}, protocol.Encryption1RTT, protocol.ECNNon, 1200, false, false) lastPN = pn if skippedPN != protocol.InvalidPacketNumber { break @@ -216,7 +216,7 @@ func testSentPacketHandlerRTTs(t *testing.T, encLevel protocol.EncryptionLevel, sendPacket := func(ti time.Time) protocol.PacketNumber { pn := sph.PopPacketNumber(encLevel) - sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, []Frame{{Frame: &wire.PingFrame{}}}, encLevel, protocol.ECNNon, 1200, false) + sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, []Frame{{Frame: &wire.PingFrame{}}}, encLevel, protocol.ECNNon, 1200, false, false) return pn } @@ -318,7 +318,7 @@ func testSentPacketHandlerAmplificationLimitServer(t *testing.T, addressValidate for i := 0; i < 4; i++ { require.Equal(t, SendAny, sph.SendMode(time.Now())) pn := sph.PopPacketNumber(protocol.EncryptionInitial) - sph.SentPacket(time.Now(), pn, protocol.InvalidPacketNumber, nil, []Frame{{Frame: &wire.PingFrame{}}}, protocol.EncryptionInitial, protocol.ECNNon, 999, false) + sph.SentPacket(time.Now(), pn, protocol.InvalidPacketNumber, nil, []Frame{{Frame: &wire.PingFrame{}}}, protocol.EncryptionInitial, protocol.ECNNon, 999, false, false) if i != 3 { require.NotZero(t, sph.GetLossDetectionTimeout()) } @@ -333,7 +333,7 @@ func testSentPacketHandlerAmplificationLimitServer(t *testing.T, addressValidate for i := 0; i < 3; i++ { require.Equal(t, SendAny, sph.SendMode(time.Now())) pn := sph.PopPacketNumber(protocol.EncryptionInitial) - sph.SentPacket(time.Now(), pn, protocol.InvalidPacketNumber, nil, []Frame{{Frame: &wire.PingFrame{}}}, protocol.EncryptionInitial, protocol.ECNNon, 1000, false) + sph.SentPacket(time.Now(), pn, protocol.InvalidPacketNumber, nil, []Frame{{Frame: &wire.PingFrame{}}}, protocol.EncryptionInitial, protocol.ECNNon, 1000, false, false) } require.Equal(t, SendNone, sph.SendMode(time.Now())) require.Zero(t, sph.GetLossDetectionTimeout()) @@ -373,7 +373,7 @@ func testSentPacketHandlerAmplificationLimitClient(t *testing.T, dropHandshake b require.Equal(t, SendAny, sph.SendMode(time.Now())) pn := sph.PopPacketNumber(protocol.EncryptionInitial) - sph.SentPacket(time.Now(), pn, protocol.InvalidPacketNumber, nil, []Frame{{Frame: &wire.PingFrame{}}}, protocol.EncryptionInitial, protocol.ECNNon, 999, false) + sph.SentPacket(time.Now(), pn, protocol.InvalidPacketNumber, nil, []Frame{{Frame: &wire.PingFrame{}}}, protocol.EncryptionInitial, protocol.ECNNon, 999, false, false) // it's not surprising that the loss detection timer is set, as this packet might be lost... require.NotZero(t, sph.GetLossDetectionTimeout()) // ... but it's still set after receiving an ACK for this packet, @@ -405,7 +405,7 @@ func testSentPacketHandlerAmplificationLimitClient(t *testing.T, dropHandshake b // receiving an ACK for a handshake packet shows that the server completed address validation pn = sph.PopPacketNumber(protocol.EncryptionHandshake) - sph.SentPacket(time.Now(), pn, protocol.InvalidPacketNumber, nil, []Frame{{Frame: &wire.PingFrame{}}}, protocol.EncryptionHandshake, protocol.ECNNon, 999, false) + sph.SentPacket(time.Now(), pn, protocol.InvalidPacketNumber, nil, []Frame{{Frame: &wire.PingFrame{}}}, protocol.EncryptionHandshake, protocol.ECNNon, 999, false, false) require.NotZero(t, sph.GetLossDetectionTimeout()) _, err = sph.ReceivedAck(&wire.AckFrame{AckRanges: ackRanges(pn)}, protocol.EncryptionHandshake, time.Now()) require.NoError(t, err) @@ -428,7 +428,7 @@ func TestSentPacketHandlerDelayBasedLossDetection(t *testing.T) { var packets packetTracker sendPacket := func(ti time.Time, isPathMTUProbePacket bool) protocol.PacketNumber { pn := sph.PopPacketNumber(protocol.EncryptionInitial) - sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, protocol.EncryptionInitial, protocol.ECNNon, 1000, isPathMTUProbePacket) + sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, protocol.EncryptionInitial, protocol.ECNNon, 1000, isPathMTUProbePacket, false) return pn } @@ -482,7 +482,7 @@ func TestSentPacketHandlerPacketBasedLossDetection(t *testing.T) { var pns []protocol.PacketNumber for range 5 { pn := sph.PopPacketNumber(protocol.EncryptionInitial) - sph.SentPacket(now, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, protocol.EncryptionInitial, protocol.ECNNon, 1000, false) + sph.SentPacket(now, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, protocol.EncryptionInitial, protocol.ECNNon, 1000, false, false) pns = append(pns, pn) } @@ -551,9 +551,9 @@ func testSentPacketHandlerPTO(t *testing.T, encLevel protocol.EncryptionLevel, p pn := sph.PopPacketNumber(encLevel) if ackEliciting { tr.EXPECT().SetLossTimer(logging.TimerTypePTO, encLevel, gomock.Any()) - sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, encLevel, protocol.ECNNon, 1000, false) + sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, encLevel, protocol.ECNNon, 1000, false, false) } else { - sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, nil, encLevel, protocol.ECNNon, 1000, true) + sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, nil, encLevel, protocol.ECNNon, 1000, true, false) } return pn } @@ -682,7 +682,7 @@ func TestSentPacketHandlerPacketNumberSpacesPTO(t *testing.T) { sendPacket := func(ti time.Time, encLevel protocol.EncryptionLevel) protocol.PacketNumber { pn := sph.PopPacketNumber(encLevel) - sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, []Frame{{Frame: &wire.PingFrame{}}}, encLevel, protocol.ECNNon, 1000, false) + sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, []Frame{{Frame: &wire.PingFrame{}}}, encLevel, protocol.ECNNon, 1000, false, false) return pn } @@ -779,7 +779,7 @@ func TestSentPacketHandler0RTT(t *testing.T) { } else { frames = []Frame{{Frame: &wire.PingFrame{}}} } - sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, frames, encLevel, protocol.ECNNon, 1000, false) + sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, frames, encLevel, protocol.ECNNon, 1000, false, false) return pn } @@ -836,7 +836,7 @@ func TestSentPacketHandlerCongestion(t *testing.T) { pn := sph.PopPacketNumber(protocol.EncryptionInitial) bytesInFlight += 1000 cong.EXPECT().OnPacketSent(now, bytesInFlight, pn, protocol.ByteCount(1000), true) - sph.SentPacket(now, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, protocol.EncryptionInitial, protocol.ECNNon, 1000, i == 1) + sph.SentPacket(now, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, protocol.EncryptionInitial, protocol.ECNNon, 1000, i == 1, false) pns = append(pns, pn) sendTimes = append(sendTimes, now) now = now.Add(100 * time.Millisecond) @@ -889,7 +889,7 @@ func TestSentPacketHandlerCongestion(t *testing.T) { now = timeout.Add(100 * time.Millisecond) pn := sph.PopPacketNumber(protocol.EncryptionInitial) cong.EXPECT().OnPacketSent(now, protocol.ByteCount(2000), pn, protocol.ByteCount(1000), true) - sph.SentPacket(now, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, protocol.EncryptionInitial, protocol.ECNNon, 1000, false) + sph.SentPacket(now, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, protocol.EncryptionInitial, protocol.ECNNon, 1000, false, false) } func TestSentPacketHandlerRetry(t *testing.T) { @@ -925,12 +925,12 @@ func testSentPacketHandlerRetry(t *testing.T, rtt, expectedRTT time.Duration) { for range 2 { pn := sph.PopPacketNumber(protocol.EncryptionInitial) initialPNs = append(initialPNs, pn) - sph.SentPacket(now, pn, protocol.InvalidPacketNumber, nil, []Frame{initialPackets.NewPingFrame(pn)}, protocol.EncryptionInitial, protocol.ECNNon, 1000, false) + sph.SentPacket(now, pn, protocol.InvalidPacketNumber, nil, []Frame{initialPackets.NewPingFrame(pn)}, protocol.EncryptionInitial, protocol.ECNNon, 1000, false, false) now = now.Add(100 * time.Millisecond) pn = sph.PopPacketNumber(protocol.Encryption0RTT) appDataPNs = append(appDataPNs, pn) - sph.SentPacket(now, pn, protocol.InvalidPacketNumber, nil, []Frame{appDataPackets.NewPingFrame(pn)}, protocol.Encryption0RTT, protocol.ECNNon, 1000, false) + sph.SentPacket(now, pn, protocol.InvalidPacketNumber, nil, []Frame{appDataPackets.NewPingFrame(pn)}, protocol.Encryption0RTT, protocol.ECNNon, 1000, false, false) now = now.Add(100 * time.Millisecond) } require.Equal(t, protocol.ByteCount(4000), sph.getBytesInFlight()) @@ -972,7 +972,7 @@ func TestSentPacketHandlerRetryAfterPTO(t *testing.T) { start := time.Now() now := start pn1 := sph.PopPacketNumber(protocol.EncryptionInitial) - sph.SentPacket(now, pn1, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn1)}, protocol.EncryptionInitial, protocol.ECNNon, 1000, false) + sph.SentPacket(now, pn1, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn1)}, protocol.EncryptionInitial, protocol.ECNNon, 1000, false, false) timeout := sph.GetLossDetectionTimeout() require.NotZero(t, timeout) @@ -983,7 +983,7 @@ func TestSentPacketHandlerRetryAfterPTO(t *testing.T) { // send a retransmission for the first packet now = timeout.Add(100 * time.Millisecond) pn2 := sph.PopPacketNumber(protocol.EncryptionInitial) - sph.SentPacket(now, pn2, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn2)}, protocol.EncryptionInitial, protocol.ECNNon, 900, false) + sph.SentPacket(now, pn2, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn2)}, protocol.EncryptionInitial, protocol.ECNNon, 900, false, false) const rtt = time.Second sph.ResetForRetry(now.Add(rtt)) @@ -1014,15 +1014,15 @@ func TestSentPacketHandlerECN(t *testing.T) { sph.congestion = cong // ECN marks on non-1-RTT packets are ignored - sph.SentPacket(time.Now(), sph.PopPacketNumber(protocol.EncryptionInitial), protocol.InvalidPacketNumber, nil, nil, protocol.EncryptionInitial, protocol.ECT1, 1200, false) - sph.SentPacket(time.Now(), sph.PopPacketNumber(protocol.EncryptionHandshake), protocol.InvalidPacketNumber, nil, nil, protocol.EncryptionHandshake, protocol.ECT0, 1200, false) - sph.SentPacket(time.Now(), sph.PopPacketNumber(protocol.Encryption0RTT), protocol.InvalidPacketNumber, nil, nil, protocol.Encryption0RTT, protocol.ECNCE, 1200, false) + sph.SentPacket(time.Now(), sph.PopPacketNumber(protocol.EncryptionInitial), protocol.InvalidPacketNumber, nil, nil, protocol.EncryptionInitial, protocol.ECT1, 1200, false, false) + sph.SentPacket(time.Now(), sph.PopPacketNumber(protocol.EncryptionHandshake), protocol.InvalidPacketNumber, nil, nil, protocol.EncryptionHandshake, protocol.ECT0, 1200, false, false) + sph.SentPacket(time.Now(), sph.PopPacketNumber(protocol.Encryption0RTT), protocol.InvalidPacketNumber, nil, nil, protocol.Encryption0RTT, protocol.ECNCE, 1200, false, false) var packets packetTracker sendPacket := func(ti time.Time, ecn protocol.ECN) protocol.PacketNumber { pn := sph.PopPacketNumber(protocol.Encryption1RTT) ecnHandler.EXPECT().SentPacket(pn, ecn) - sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, protocol.Encryption1RTT, ecn, 1200, false) + sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, protocol.Encryption1RTT, ecn, 1200, false, false) return pn } @@ -1096,3 +1096,131 @@ func TestSentPacketHandlerECN(t *testing.T) { _, err = sph.ReceivedAck(&wire.AckFrame{AckRanges: ackRanges(pns[0])}, protocol.Encryption1RTT, now.Add(100*time.Millisecond)) require.NoError(t, err) } + +func TestSentPacketHandlerPathProbe(t *testing.T) { + const rtt = 10 * time.Millisecond // RTT of the original path + var rttStats utils.RTTStats + rttStats.UpdateRTT(rtt, 0) + + sph := newSentPacketHandler( + 0, + 1200, + &rttStats, + true, + false, + protocol.PerspectiveClient, + nil, + utils.DefaultLogger, + ) + sph.DropPackets(protocol.EncryptionInitial, time.Now()) + sph.DropPackets(protocol.EncryptionHandshake, time.Now()) + + var packets packetTracker + sendPacket := func(ti time.Time, isPathProbe bool) protocol.PacketNumber { + pn := sph.PopPacketNumber(protocol.Encryption1RTT) + sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, protocol.Encryption1RTT, protocol.ECNNon, 1200, false, isPathProbe) + return pn + } + + // send 5 packets: 2 non-probe packets, 1 probe packet, 2 non-probe packets + now := time.Now() + var pns [5]protocol.PacketNumber + pns[0] = sendPacket(now, false) + now = now.Add(rtt) + pns[1] = sendPacket(now, false) + pns[2] = sendPacket(now, true) + pathProbeTimeout := now.Add(pathProbePacketLossTimeout) + now = now.Add(rtt) + pns[3] = sendPacket(now, false) + now = now.Add(rtt) + pns[4] = sendPacket(now, false) + require.Less(t, sph.GetLossDetectionTimeout(), pathProbeTimeout) + + now = now.Add(100 * time.Millisecond) + // make sure that this ACK doesn't declare the path probe packet lost + require.Greater(t, pathProbeTimeout, now) + _, err := sph.ReceivedAck( + &wire.AckFrame{AckRanges: ackRanges(pns[0], pns[3], pns[4])}, + protocol.Encryption1RTT, + now, + ) + require.NoError(t, err) + require.Equal(t, []protocol.PacketNumber{pns[0], pns[3], pns[4]}, packets.Acked) + // despite having been sent at the same time, the probe packet was not lost + require.Equal(t, []protocol.PacketNumber{pns[1]}, packets.Lost) + + // the timeout is now based on the probe packet + timeout := sph.GetLossDetectionTimeout() + require.Equal(t, pathProbeTimeout, timeout) + require.Zero(t, sph.getBytesInFlight()) + pn1 := sendPacket(now, false) + pn2 := sendPacket(now, false) + require.Equal(t, protocol.ByteCount(2400), sph.getBytesInFlight()) + + // send one more non-probe packet + pn := sendPacket(now, false) + // the timeout is now based on this packet + require.Less(t, sph.GetLossDetectionTimeout(), pathProbeTimeout) + _, err = sph.ReceivedAck( + &wire.AckFrame{AckRanges: ackRanges(pns[2], pn)}, + protocol.Encryption1RTT, + now, + ) + require.NoError(t, err) + + packets.Lost = packets.Lost[:0] + sph.MigratedPath(now, 1200) + require.Zero(t, sph.getBytesInFlight()) + require.Zero(t, rttStats.SmoothedRTT()) + require.Equal(t, []protocol.PacketNumber{pn1, pn2}, packets.Lost) +} + +func TestSentPacketHandlerPathProbeAckAndLoss(t *testing.T) { + const rtt = 10 * time.Millisecond // RTT of the original path + var rttStats utils.RTTStats + rttStats.UpdateRTT(rtt, 0) + + sph := newSentPacketHandler( + 0, + 1200, + &rttStats, + true, + false, + protocol.PerspectiveClient, + nil, + utils.DefaultLogger, + ) + sph.DropPackets(protocol.EncryptionInitial, time.Now()) + sph.DropPackets(protocol.EncryptionHandshake, time.Now()) + + var packets packetTracker + sendPacket := func(ti time.Time, isPathProbe bool) protocol.PacketNumber { + pn := sph.PopPacketNumber(protocol.Encryption1RTT) + sph.SentPacket(ti, pn, protocol.InvalidPacketNumber, nil, []Frame{packets.NewPingFrame(pn)}, protocol.Encryption1RTT, protocol.ECNNon, 1200, false, isPathProbe) + return pn + } + + now := time.Now() + pn1 := sendPacket(now, true) + t1 := now + now = now.Add(100 * time.Millisecond) + _ = sendPacket(now, true) + now = now.Add(100 * time.Millisecond) + pn3 := sendPacket(now, true) + + now = now.Add(100 * time.Millisecond) + require.Equal(t, t1.Add(pathProbePacketLossTimeout), sph.GetLossDetectionTimeout()) + _, err := sph.ReceivedAck( + &wire.AckFrame{AckRanges: ackRanges(pn3)}, + protocol.Encryption1RTT, + now, + ) + require.NoError(t, err) + require.Equal(t, []protocol.PacketNumber{pn3}, packets.Acked) + require.Empty(t, packets.Lost) + + require.Equal(t, t1.Add(pathProbePacketLossTimeout), sph.GetLossDetectionTimeout()) + + require.NoError(t, sph.OnLossDetectionTimeout(sph.GetLossDetectionTimeout())) + require.Equal(t, []protocol.PacketNumber{pn1}, packets.Lost) +} diff --git a/internal/mocks/ackhandler/sent_packet_handler.go b/internal/mocks/ackhandler/sent_packet_handler.go index bac7ec78..61939c9d 100644 --- a/internal/mocks/ackhandler/sent_packet_handler.go +++ b/internal/mocks/ackhandler/sent_packet_handler.go @@ -155,6 +155,42 @@ func (c *MockSentPacketHandlerGetLossDetectionTimeoutCall) DoAndReturn(f func() return c } +// MigratedPath mocks base method. +func (m *MockSentPacketHandler) MigratedPath(now time.Time, initialMaxPacketSize protocol.ByteCount) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "MigratedPath", now, initialMaxPacketSize) +} + +// MigratedPath indicates an expected call of MigratedPath. +func (mr *MockSentPacketHandlerMockRecorder) MigratedPath(now, initialMaxPacketSize any) *MockSentPacketHandlerMigratedPathCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MigratedPath", reflect.TypeOf((*MockSentPacketHandler)(nil).MigratedPath), now, initialMaxPacketSize) + return &MockSentPacketHandlerMigratedPathCall{Call: call} +} + +// MockSentPacketHandlerMigratedPathCall wrap *gomock.Call +type MockSentPacketHandlerMigratedPathCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockSentPacketHandlerMigratedPathCall) Return() *MockSentPacketHandlerMigratedPathCall { + c.Call = c.Call.Return() + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockSentPacketHandlerMigratedPathCall) Do(f func(time.Time, protocol.ByteCount)) *MockSentPacketHandlerMigratedPathCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockSentPacketHandlerMigratedPathCall) DoAndReturn(f func(time.Time, protocol.ByteCount)) *MockSentPacketHandlerMigratedPathCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // OnLossDetectionTimeout mocks base method. func (m *MockSentPacketHandler) OnLossDetectionTimeout(now time.Time) error { m.ctrl.T.Helper() @@ -458,15 +494,15 @@ func (c *MockSentPacketHandlerSendModeCall) DoAndReturn(f func(time.Time) ackhan } // SentPacket mocks base method. -func (m *MockSentPacketHandler) SentPacket(t time.Time, pn, largestAcked protocol.PacketNumber, streamFrames []ackhandler.StreamFrame, frames []ackhandler.Frame, encLevel protocol.EncryptionLevel, ecn protocol.ECN, size protocol.ByteCount, isPathMTUProbePacket bool) { +func (m *MockSentPacketHandler) SentPacket(t time.Time, pn, largestAcked protocol.PacketNumber, streamFrames []ackhandler.StreamFrame, frames []ackhandler.Frame, encLevel protocol.EncryptionLevel, ecn protocol.ECN, size protocol.ByteCount, isPathMTUProbePacket, isPathProbePacket bool) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SentPacket", t, pn, largestAcked, streamFrames, frames, encLevel, ecn, size, isPathMTUProbePacket) + m.ctrl.Call(m, "SentPacket", t, pn, largestAcked, streamFrames, frames, encLevel, ecn, size, isPathMTUProbePacket, isPathProbePacket) } // SentPacket indicates an expected call of SentPacket. -func (mr *MockSentPacketHandlerMockRecorder) SentPacket(t, pn, largestAcked, streamFrames, frames, encLevel, ecn, size, isPathMTUProbePacket any) *MockSentPacketHandlerSentPacketCall { +func (mr *MockSentPacketHandlerMockRecorder) SentPacket(t, pn, largestAcked, streamFrames, frames, encLevel, ecn, size, isPathMTUProbePacket, isPathProbePacket any) *MockSentPacketHandlerSentPacketCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentPacket", reflect.TypeOf((*MockSentPacketHandler)(nil).SentPacket), t, pn, largestAcked, streamFrames, frames, encLevel, ecn, size, isPathMTUProbePacket) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentPacket", reflect.TypeOf((*MockSentPacketHandler)(nil).SentPacket), t, pn, largestAcked, streamFrames, frames, encLevel, ecn, size, isPathMTUProbePacket, isPathProbePacket) return &MockSentPacketHandlerSentPacketCall{Call: call} } @@ -482,13 +518,13 @@ func (c *MockSentPacketHandlerSentPacketCall) Return() *MockSentPacketHandlerSen } // Do rewrite *gomock.Call.Do -func (c *MockSentPacketHandlerSentPacketCall) Do(f func(time.Time, protocol.PacketNumber, protocol.PacketNumber, []ackhandler.StreamFrame, []ackhandler.Frame, protocol.EncryptionLevel, protocol.ECN, protocol.ByteCount, bool)) *MockSentPacketHandlerSentPacketCall { +func (c *MockSentPacketHandlerSentPacketCall) Do(f func(time.Time, protocol.PacketNumber, protocol.PacketNumber, []ackhandler.StreamFrame, []ackhandler.Frame, protocol.EncryptionLevel, protocol.ECN, protocol.ByteCount, bool, bool)) *MockSentPacketHandlerSentPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockSentPacketHandlerSentPacketCall) DoAndReturn(f func(time.Time, protocol.PacketNumber, protocol.PacketNumber, []ackhandler.StreamFrame, []ackhandler.Frame, protocol.EncryptionLevel, protocol.ECN, protocol.ByteCount, bool)) *MockSentPacketHandlerSentPacketCall { +func (c *MockSentPacketHandlerSentPacketCall) DoAndReturn(f func(time.Time, protocol.PacketNumber, protocol.PacketNumber, []ackhandler.StreamFrame, []ackhandler.Frame, protocol.EncryptionLevel, protocol.ECN, protocol.ByteCount, bool, bool)) *MockSentPacketHandlerSentPacketCall { c.Call = c.Call.DoAndReturn(f) return c } diff --git a/logging/types.go b/logging/types.go index 0d79b0a9..65da3559 100644 --- a/logging/types.go +++ b/logging/types.go @@ -63,9 +63,11 @@ type TimerType uint8 const ( // TimerTypeACK is the timer type for the early retransmit timer - TimerTypeACK TimerType = iota + TimerTypeACK TimerType = iota + 1 // TimerTypePTO is the timer type for the PTO retransmit timer TimerTypePTO + // TimerTypePathProbe is the timer type for the path probe retransmit timer + TimerTypePathProbe ) // TimeoutReason is the reason why a connection is closed diff --git a/qlog/types.go b/qlog/types.go index 25dfc710..d0ff7ae6 100644 --- a/qlog/types.go +++ b/qlog/types.go @@ -291,6 +291,8 @@ func (t timerType) String() string { return "ack" case logging.TimerTypePTO: return "pto" + case logging.TimerTypePathProbe: + return "path_probe" default: return "unknown timer type" } diff --git a/qlog/types_test.go b/qlog/types_test.go index b31c3e1a..71c595e5 100644 --- a/qlog/types_test.go +++ b/qlog/types_test.go @@ -94,6 +94,7 @@ func TestTimerTypeStringRepresentation(t *testing.T) { }{ {logging.TimerTypeACK, "ack"}, {logging.TimerTypePTO, "pto"}, + {logging.TimerTypePathProbe, "path_probe"}, } for _, tc := range testCases {