Skip to main content

quiche/recovery/
mod.rs

1// Copyright (C) 2018-2019, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use std::str::FromStr;
28use std::time::Duration;
29use std::time::Instant;
30
31use crate::frame;
32use crate::packet;
33use crate::ranges::RangeSet;
34pub(crate) use crate::recovery::bandwidth::Bandwidth;
35use crate::Config;
36use crate::Result;
37
38#[cfg(feature = "qlog")]
39use qlog::events::EventData;
40#[cfg(feature = "qlog")]
41use serde::Serialize;
42
43use smallvec::SmallVec;
44
45use self::congestion::recovery::LegacyRecovery;
46use self::gcongestion::GRecovery;
47pub use gcongestion::BbrBwLoReductionStrategy;
48pub use gcongestion::BbrParams;
49
50// Loss Recovery
51const INITIAL_PACKET_THRESHOLD: u64 = 3;
52
53const MAX_PACKET_THRESHOLD: u64 = 20;
54
55// Time threshold used to calculate the loss time.
56//
57// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.1.2
58const INITIAL_TIME_THRESHOLD: f64 = 9.0 / 8.0;
59
60// Reduce the sensitivity to packet reordering after the first reordering event.
61//
62// Packet reorder is not a real loss event so quickly reduce the sensitivity to
63// avoid penializing subsequent packet reordering.
64//
65// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.1.2
66//
67// Implementations MAY experiment with absolute thresholds, thresholds from
68// previous connections, adaptive thresholds, or the including of RTT variation.
69// Smaller thresholds reduce reordering resilience and increase spurious
70// retransmissions, and larger thresholds increase loss detection delay.
71const PACKET_REORDER_TIME_THRESHOLD: f64 = 5.0 / 4.0;
72
73// # Experiment: enable_relaxed_loss_threshold
74//
75// Time threshold overhead used to calculate the loss time.
76//
77// The actual threshold is calcualted as 1 + INITIAL_TIME_THRESHOLD_OVERHEAD and
78// equivalent to INITIAL_TIME_THRESHOLD.
79const INITIAL_TIME_THRESHOLD_OVERHEAD: f64 = 1.0 / 8.0;
80// # Experiment: enable_relaxed_loss_threshold
81//
82// The factor by which to increase the time threshold on spurious loss.
83const TIME_THRESHOLD_OVERHEAD_MULTIPLIER: f64 = 2.0;
84
85const GRANULARITY: Duration = Duration::from_millis(1);
86
87const MAX_PTO_PROBES_COUNT: usize = 2;
88
89const MINIMUM_WINDOW_PACKETS: usize = 2;
90
91const LOSS_REDUCTION_FACTOR: f64 = 0.5;
92
93// How many non ACK eliciting packets we send before including a PING to solicit
94// an ACK.
95pub(super) const MAX_OUTSTANDING_NON_ACK_ELICITING: usize = 24;
96
97#[derive(Default)]
98struct LossDetectionTimer {
99    time: Option<Instant>,
100}
101
102impl LossDetectionTimer {
103    fn update(&mut self, timeout: Instant) {
104        self.time = Some(timeout);
105    }
106
107    fn clear(&mut self) {
108        self.time = None;
109    }
110}
111
112impl std::fmt::Debug for LossDetectionTimer {
113    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
114        match self.time {
115            Some(v) => {
116                let now = Instant::now();
117                if v > now {
118                    let d = v.duration_since(now);
119                    write!(f, "{d:?}")
120                } else {
121                    write!(f, "exp")
122                }
123            },
124            None => write!(f, "none"),
125        }
126    }
127}
128
129#[derive(Clone, Copy, PartialEq)]
130pub struct RecoveryConfig {
131    pub initial_rtt: Duration,
132    pub max_send_udp_payload_size: usize,
133    pub max_ack_delay: Duration,
134    pub cc_algorithm: CongestionControlAlgorithm,
135    pub custom_bbr_params: Option<BbrParams>,
136    pub hystart: bool,
137    pub pacing: bool,
138    pub max_pacing_rate: Option<u64>,
139    pub initial_congestion_window_packets: usize,
140    pub enable_relaxed_loss_threshold: bool,
141    pub enable_cubic_idle_restart_fix: bool,
142}
143
144impl RecoveryConfig {
145    pub fn from_config(config: &Config) -> Self {
146        Self {
147            initial_rtt: config.initial_rtt,
148            max_send_udp_payload_size: config.max_send_udp_payload_size,
149            max_ack_delay: Duration::ZERO,
150            cc_algorithm: config.cc_algorithm,
151            custom_bbr_params: config.custom_bbr_params,
152            hystart: config.hystart,
153            pacing: config.pacing,
154            max_pacing_rate: config.max_pacing_rate,
155            initial_congestion_window_packets: config
156                .initial_congestion_window_packets,
157            enable_relaxed_loss_threshold: config.enable_relaxed_loss_threshold,
158            enable_cubic_idle_restart_fix: config.enable_cubic_idle_restart_fix,
159        }
160    }
161}
162
163#[enum_dispatch::enum_dispatch(RecoveryOps)]
164#[allow(clippy::large_enum_variant)]
165#[derive(Debug)]
166pub(crate) enum Recovery {
167    Legacy(LegacyRecovery),
168    GCongestion(GRecovery),
169}
170
171#[derive(Debug, Default, PartialEq)]
172pub struct OnAckReceivedOutcome {
173    pub lost_packets: usize,
174    pub lost_bytes: usize,
175    pub acked_bytes: usize,
176    pub spurious_losses: usize,
177}
178
179#[derive(Debug, Default)]
180pub struct OnLossDetectionTimeoutOutcome {
181    pub lost_packets: usize,
182    pub lost_bytes: usize,
183}
184
185#[enum_dispatch::enum_dispatch]
186/// Api for the Recovery implementation
187pub trait RecoveryOps {
188    fn lost_count(&self) -> usize;
189    fn bytes_lost(&self) -> u64;
190
191    /// Returns whether or not we should elicit an ACK even if we wouldn't
192    /// otherwise have constructed an ACK eliciting packet.
193    fn should_elicit_ack(&self, epoch: packet::Epoch) -> bool;
194
195    fn next_acked_frame(&mut self, epoch: packet::Epoch) -> Option<frame::Frame>;
196
197    fn next_lost_frame(&mut self, epoch: packet::Epoch) -> Option<frame::Frame>;
198
199    fn get_largest_acked_on_epoch(&self, epoch: packet::Epoch) -> Option<u64>;
200    fn has_lost_frames(&self, epoch: packet::Epoch) -> bool;
201    fn loss_probes(&self, epoch: packet::Epoch) -> usize;
202    #[cfg(test)]
203    fn inc_loss_probes(&mut self, epoch: packet::Epoch);
204    #[cfg(test)]
205    fn lost_frames_count(&self, epoch: packet::Epoch) -> usize;
206
207    fn ping_sent(&mut self, epoch: packet::Epoch);
208
209    fn on_packet_sent(
210        &mut self, pkt: Sent, epoch: packet::Epoch,
211        handshake_status: HandshakeStatus, now: Instant, trace_id: &str,
212    );
213    fn get_packet_send_time(&self, now: Instant) -> Instant;
214
215    #[allow(clippy::too_many_arguments)]
216    fn on_ack_received(
217        &mut self, ranges: &RangeSet, ack_delay: u64, epoch: packet::Epoch,
218        handshake_status: HandshakeStatus, now: Instant, skip_pn: Option<u64>,
219        trace_id: &str,
220    ) -> Result<OnAckReceivedOutcome>;
221
222    fn on_loss_detection_timeout(
223        &mut self, handshake_status: HandshakeStatus, now: Instant,
224        trace_id: &str,
225    ) -> OnLossDetectionTimeoutOutcome;
226    fn on_pkt_num_space_discarded(
227        &mut self, epoch: packet::Epoch, handshake_status: HandshakeStatus,
228        now: Instant,
229    );
230    fn on_path_change(
231        &mut self, epoch: packet::Epoch, now: Instant, _trace_id: &str,
232    ) -> (usize, usize);
233    fn loss_detection_timer(&self) -> Option<Instant>;
234    fn cwnd(&self) -> usize;
235    fn cwnd_available(&self) -> usize;
236    fn rtt(&self) -> Duration;
237
238    fn min_rtt(&self) -> Option<Duration>;
239
240    fn max_rtt(&self) -> Option<Duration>;
241
242    fn rttvar(&self) -> Duration;
243
244    fn pto(&self) -> Duration;
245
246    /// The most recent data delivery rate estimate.
247    fn delivery_rate(&self) -> Bandwidth;
248
249    /// Maximum bandwidth estimate, if one is available.
250    fn max_bandwidth(&self) -> Option<Bandwidth>;
251
252    /// Statistics from when a CCA first exited the startup phase.
253    fn startup_exit(&self) -> Option<StartupExit>;
254
255    fn max_datagram_size(&self) -> usize;
256
257    fn pmtud_update_max_datagram_size(&mut self, new_max_datagram_size: usize);
258
259    fn update_max_datagram_size(&mut self, new_max_datagram_size: usize);
260
261    fn on_app_limited(&mut self);
262
263    // Since a recovery module is path specific, this tracks the largest packet
264    // sent per path.
265    #[cfg(test)]
266    fn largest_sent_pkt_num_on_path(&self, epoch: packet::Epoch) -> Option<u64>;
267
268    #[cfg(test)]
269    fn app_limited(&self) -> bool;
270
271    #[cfg(test)]
272    fn sent_packets_len(&self, epoch: packet::Epoch) -> usize;
273
274    fn bytes_in_flight(&self) -> usize;
275
276    fn bytes_in_flight_duration(&self) -> Duration;
277
278    #[cfg(test)]
279    fn in_flight_count(&self, epoch: packet::Epoch) -> usize;
280
281    #[cfg(test)]
282    fn pacing_rate(&self) -> u64;
283
284    #[cfg(test)]
285    fn pto_count(&self) -> u32;
286
287    // This value might be `None` when experiment `enable_relaxed_loss_threshold`
288    // is enabled for gcongestion
289    #[cfg(test)]
290    fn pkt_thresh(&self) -> Option<u64>;
291
292    #[cfg(test)]
293    fn time_thresh(&self) -> f64;
294
295    #[cfg(test)]
296    fn lost_spurious_count(&self) -> usize;
297
298    #[cfg(test)]
299    fn detect_lost_packets_for_test(
300        &mut self, epoch: packet::Epoch, now: Instant,
301    ) -> (usize, usize);
302
303    fn update_app_limited(&mut self, v: bool);
304
305    fn delivery_rate_update_app_limited(&mut self, v: bool);
306
307    fn update_max_ack_delay(&mut self, max_ack_delay: Duration);
308
309    #[cfg(feature = "qlog")]
310    fn state_str(&self, now: Instant) -> &'static str;
311
312    #[cfg(feature = "qlog")]
313    fn get_updated_qlog_event_data(&mut self) -> Option<EventData>;
314
315    #[cfg(feature = "qlog")]
316    fn get_updated_qlog_cc_state(&mut self, now: Instant)
317        -> Option<&'static str>;
318
319    fn send_quantum(&self) -> usize;
320
321    fn get_next_release_time(&self) -> ReleaseDecision;
322
323    fn gcongestion_enabled(&self) -> bool;
324}
325
326impl Recovery {
327    pub fn new_with_config(recovery_config: &RecoveryConfig) -> Self {
328        let grecovery = GRecovery::new(recovery_config);
329        if let Some(grecovery) = grecovery {
330            Recovery::from(grecovery)
331        } else {
332            Recovery::from(LegacyRecovery::new_with_config(recovery_config))
333        }
334    }
335
336    #[cfg(feature = "qlog")]
337    pub fn maybe_qlog(
338        &mut self, qlog: &mut qlog::streamer::QlogStreamer, now: Instant,
339    ) {
340        if let Some(ev_data) = self.get_updated_qlog_event_data() {
341            qlog.add_event_data_with_instant(ev_data, now).ok();
342        }
343
344        if let Some(cc_state) = self.get_updated_qlog_cc_state(now) {
345            let ev_data = EventData::QuicCongestionStateUpdated(
346                qlog::events::quic::CongestionStateUpdated {
347                    old: None,
348                    new: cc_state.to_string(),
349                    trigger: None,
350                },
351            );
352
353            qlog.add_event_data_with_instant(ev_data, now).ok();
354        }
355    }
356
357    #[cfg(test)]
358    pub fn new(config: &Config) -> Self {
359        Self::new_with_config(&RecoveryConfig::from_config(config))
360    }
361}
362
363/// Available congestion control algorithms.
364///
365/// This enum provides currently available list of congestion control
366/// algorithms.
367#[derive(Debug, Copy, Clone, PartialEq, Eq)]
368#[repr(C)]
369pub enum CongestionControlAlgorithm {
370    /// Reno congestion control algorithm. `reno` in a string form.
371    Reno            = 0,
372    /// CUBIC congestion control algorithm (default). `cubic` in a string form.
373    CUBIC           = 1,
374    /// BBRv2 congestion control algorithm implementation from gcongestion
375    /// branch. `bbr2_gcongestion` in a string form.
376    Bbr2Gcongestion = 4,
377}
378
379impl FromStr for CongestionControlAlgorithm {
380    type Err = crate::Error;
381
382    /// Converts a string to `CongestionControlAlgorithm`.
383    ///
384    /// If `name` is not valid, `Error::CongestionControl` is returned.
385    fn from_str(name: &str) -> std::result::Result<Self, Self::Err> {
386        match name {
387            "reno" => Ok(CongestionControlAlgorithm::Reno),
388            "cubic" => Ok(CongestionControlAlgorithm::CUBIC),
389            "bbr" => Ok(CongestionControlAlgorithm::Bbr2Gcongestion),
390            "bbr2" => Ok(CongestionControlAlgorithm::Bbr2Gcongestion),
391            "bbr2_gcongestion" => Ok(CongestionControlAlgorithm::Bbr2Gcongestion),
392            _ => Err(crate::Error::CongestionControl),
393        }
394    }
395}
396
397#[derive(Clone)]
398pub struct Sent {
399    pub pkt_num: u64,
400
401    pub frames: SmallVec<[frame::Frame; 1]>,
402
403    pub time_sent: Instant,
404
405    pub time_acked: Option<Instant>,
406
407    pub time_lost: Option<Instant>,
408
409    pub size: usize,
410
411    pub ack_eliciting: bool,
412
413    pub in_flight: bool,
414
415    pub delivered: usize,
416
417    pub delivered_time: Instant,
418
419    pub first_sent_time: Instant,
420
421    pub is_app_limited: bool,
422
423    pub tx_in_flight: usize,
424
425    pub lost: u64,
426
427    pub has_data: bool,
428
429    pub is_pmtud_probe: bool,
430}
431
432impl std::fmt::Debug for Sent {
433    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
434        write!(f, "pkt_num={:?} ", self.pkt_num)?;
435        write!(f, "pkt_sent_time={:?} ", self.time_sent)?;
436        write!(f, "pkt_size={:?} ", self.size)?;
437        write!(f, "delivered={:?} ", self.delivered)?;
438        write!(f, "delivered_time={:?} ", self.delivered_time)?;
439        write!(f, "first_sent_time={:?} ", self.first_sent_time)?;
440        write!(f, "is_app_limited={} ", self.is_app_limited)?;
441        write!(f, "tx_in_flight={} ", self.tx_in_flight)?;
442        write!(f, "lost={} ", self.lost)?;
443        write!(f, "has_data={} ", self.has_data)?;
444        write!(f, "is_pmtud_probe={}", self.is_pmtud_probe)?;
445
446        Ok(())
447    }
448}
449
450#[derive(Clone, Copy, Debug)]
451pub struct HandshakeStatus {
452    pub has_handshake_keys: bool,
453
454    pub peer_verified_address: bool,
455
456    pub completed: bool,
457}
458
459#[cfg(test)]
460impl Default for HandshakeStatus {
461    fn default() -> HandshakeStatus {
462        HandshakeStatus {
463            has_handshake_keys: true,
464
465            peer_verified_address: true,
466
467            completed: true,
468        }
469    }
470}
471
472// We don't need to log all qlog metrics every time there is a recovery event.
473// Instead, we can log only the MetricsUpdated event data fields that we care
474// about, only when they change. To support this, the QLogMetrics structure
475// keeps a running picture of the fields.
476#[derive(Default)]
477#[cfg(feature = "qlog")]
478struct QlogMetrics {
479    min_rtt: Duration,
480    smoothed_rtt: Duration,
481    latest_rtt: Duration,
482    rttvar: Duration,
483    cwnd: u64,
484    bytes_in_flight: u64,
485    ssthresh: Option<u64>,
486    pacing_rate: Option<u64>,
487    delivery_rate: Option<u64>,
488    send_rate: Option<u64>,
489    ack_rate: Option<u64>,
490    lost_packets: Option<u64>,
491    lost_bytes: Option<u64>,
492    pto_count: Option<u32>,
493}
494
495#[cfg(feature = "qlog")]
496trait CustomCfQlogField {
497    fn name(&self) -> &'static str;
498    fn as_json_value(&self) -> serde_json::Value;
499}
500
501#[cfg(feature = "qlog")]
502#[serde_with::skip_serializing_none]
503#[derive(Serialize)]
504struct TotalAndDelta {
505    total: Option<u64>,
506    delta: Option<u64>,
507}
508
509#[cfg(feature = "qlog")]
510struct CustomQlogField<T> {
511    name: &'static str,
512    value: T,
513}
514
515#[cfg(feature = "qlog")]
516impl<T> CustomQlogField<T> {
517    fn new(name: &'static str, value: T) -> Self {
518        Self { name, value }
519    }
520}
521
522#[cfg(feature = "qlog")]
523impl<T: Serialize> CustomCfQlogField for CustomQlogField<T> {
524    fn name(&self) -> &'static str {
525        self.name
526    }
527
528    fn as_json_value(&self) -> serde_json::Value {
529        serde_json::json!(&self.value)
530    }
531}
532
533#[cfg(feature = "qlog")]
534struct CfExData(qlog::events::ExData);
535
536#[cfg(feature = "qlog")]
537impl CfExData {
538    fn new() -> Self {
539        Self(qlog::events::ExData::new())
540    }
541
542    fn insert<T: Serialize>(&mut self, name: &'static str, value: T) {
543        let field = CustomQlogField::new(name, value);
544        self.0
545            .insert(field.name().to_string(), field.as_json_value());
546    }
547
548    fn into_inner(self) -> qlog::events::ExData {
549        self.0
550    }
551}
552
553#[cfg(feature = "qlog")]
554impl QlogMetrics {
555    // Make a qlog event if the latest instance of QlogMetrics is different.
556    //
557    // This function diffs each of the fields. A qlog MetricsUpdated event is
558    // only generated if at least one field is different. Where fields are
559    // different, the qlog event contains the latest value.
560    fn maybe_update(&mut self, latest: Self) -> Option<EventData> {
561        let mut emit_event = false;
562
563        let new_min_rtt = if self.min_rtt != latest.min_rtt {
564            self.min_rtt = latest.min_rtt;
565            emit_event = true;
566            Some(latest.min_rtt.as_secs_f32() * 1000.0)
567        } else {
568            None
569        };
570
571        let new_smoothed_rtt = if self.smoothed_rtt != latest.smoothed_rtt {
572            self.smoothed_rtt = latest.smoothed_rtt;
573            emit_event = true;
574            Some(latest.smoothed_rtt.as_secs_f32() * 1000.0)
575        } else {
576            None
577        };
578
579        let new_latest_rtt = if self.latest_rtt != latest.latest_rtt {
580            self.latest_rtt = latest.latest_rtt;
581            emit_event = true;
582            Some(latest.latest_rtt.as_secs_f32() * 1000.0)
583        } else {
584            None
585        };
586
587        let new_rttvar = if self.rttvar != latest.rttvar {
588            self.rttvar = latest.rttvar;
589            emit_event = true;
590            Some(latest.rttvar.as_secs_f32() * 1000.0)
591        } else {
592            None
593        };
594
595        let new_cwnd = if self.cwnd != latest.cwnd {
596            self.cwnd = latest.cwnd;
597            emit_event = true;
598            Some(latest.cwnd)
599        } else {
600            None
601        };
602
603        let new_bytes_in_flight =
604            if self.bytes_in_flight != latest.bytes_in_flight {
605                self.bytes_in_flight = latest.bytes_in_flight;
606                emit_event = true;
607                Some(latest.bytes_in_flight)
608            } else {
609                None
610            };
611
612        let new_ssthresh = if self.ssthresh != latest.ssthresh {
613            self.ssthresh = latest.ssthresh;
614            emit_event = true;
615            latest.ssthresh
616        } else {
617            None
618        };
619
620        let new_pacing_rate = if self.pacing_rate != latest.pacing_rate {
621            self.pacing_rate = latest.pacing_rate;
622            emit_event = true;
623            latest.pacing_rate
624        } else {
625            None
626        };
627
628        let new_pto_count =
629            if latest.pto_count.is_some() && self.pto_count != latest.pto_count {
630                self.pto_count = latest.pto_count;
631                emit_event = true;
632                latest.pto_count.map(|v| v as u16)
633            } else {
634                None
635            };
636
637        // Build ex_data for rate metrics
638        let mut ex_data = CfExData::new();
639        if self.delivery_rate != latest.delivery_rate {
640            if let Some(rate) = latest.delivery_rate {
641                self.delivery_rate = latest.delivery_rate;
642                emit_event = true;
643                ex_data.insert("cf_delivery_rate", rate);
644            }
645        }
646        if self.send_rate != latest.send_rate {
647            if let Some(rate) = latest.send_rate {
648                self.send_rate = latest.send_rate;
649                emit_event = true;
650                ex_data.insert("cf_send_rate", rate);
651            }
652        }
653        if self.ack_rate != latest.ack_rate {
654            if let Some(rate) = latest.ack_rate {
655                self.ack_rate = latest.ack_rate;
656                emit_event = true;
657                ex_data.insert("cf_ack_rate", rate);
658            }
659        }
660
661        if self.lost_packets != latest.lost_packets {
662            if let Some(val) = latest.lost_packets {
663                emit_event = true;
664                ex_data.insert("cf_lost_packets", TotalAndDelta {
665                    total: latest.lost_packets,
666                    delta: Some(val - self.lost_packets.unwrap_or(0)),
667                });
668                self.lost_packets = latest.lost_packets;
669            }
670        }
671        if self.lost_bytes != latest.lost_bytes {
672            if let Some(val) = latest.lost_bytes {
673                emit_event = true;
674                ex_data.insert("cf_lost_bytes", TotalAndDelta {
675                    total: latest.lost_bytes,
676                    delta: Some(val - self.lost_bytes.unwrap_or(0)),
677                });
678                self.lost_bytes = latest.lost_bytes;
679            }
680        }
681
682        if emit_event {
683            return Some(EventData::QuicMetricsUpdated(
684                qlog::events::quic::RecoveryMetricsUpdated {
685                    min_rtt: new_min_rtt,
686                    smoothed_rtt: new_smoothed_rtt,
687                    latest_rtt: new_latest_rtt,
688                    rtt_variance: new_rttvar,
689                    congestion_window: new_cwnd,
690                    bytes_in_flight: new_bytes_in_flight,
691                    ssthresh: new_ssthresh,
692                    pacing_rate: new_pacing_rate,
693                    pto_count: new_pto_count,
694                    ex_data: ex_data.into_inner(),
695                    ..Default::default()
696                },
697            ));
698        }
699
700        None
701    }
702}
703
704/// When the pacer thinks is a good time to release the next packet
705#[derive(Debug, Clone, Copy, PartialEq, Eq)]
706pub enum ReleaseTime {
707    Immediate,
708    At(Instant),
709}
710
711/// When the next packet should be release and if it can be part of a burst
712#[derive(Clone, Copy, Debug, PartialEq, Eq)]
713pub struct ReleaseDecision {
714    time: ReleaseTime,
715    allow_burst: bool,
716}
717
718impl ReleaseTime {
719    /// Add the specific delay to the current time
720    fn inc(&mut self, delay: Duration) {
721        match self {
722            ReleaseTime::Immediate => {},
723            ReleaseTime::At(time) => *time += delay,
724        }
725    }
726
727    /// Set the time to the later of two times
728    fn set_max(&mut self, other: Instant) {
729        match self {
730            ReleaseTime::Immediate => *self = ReleaseTime::At(other),
731            ReleaseTime::At(time) => *self = ReleaseTime::At(other.max(*time)),
732        }
733    }
734}
735
736impl ReleaseDecision {
737    pub(crate) const EQUAL_THRESHOLD: Duration = Duration::from_micros(50);
738
739    /// Get the [`Instant`] the next packet should be released. It will never be
740    /// in the past.
741    #[inline]
742    pub fn time(&self, now: Instant) -> Option<Instant> {
743        match self.time {
744            ReleaseTime::Immediate => None,
745            ReleaseTime::At(other) => other.gt(&now).then_some(other),
746        }
747    }
748
749    /// Can this packet be appended to a previous burst
750    #[inline]
751    pub fn can_burst(&self) -> bool {
752        self.allow_burst
753    }
754
755    /// Check if the two packets can be released at the same time
756    #[inline]
757    pub fn time_eq(&self, other: &Self, now: Instant) -> bool {
758        let delta = match (self.time(now), other.time(now)) {
759            (None, None) => Duration::ZERO,
760            (Some(t), None) | (None, Some(t)) => t.duration_since(now),
761            (Some(t1), Some(t2)) if t1 < t2 => t2.duration_since(t1),
762            (Some(t1), Some(t2)) => t1.duration_since(t2),
763        };
764
765        delta <= Self::EQUAL_THRESHOLD
766    }
767}
768
769/// Recovery statistics
770#[derive(Default, Debug)]
771pub struct RecoveryStats {
772    startup_exit: Option<StartupExit>,
773}
774
775impl RecoveryStats {
776    // Record statistics when a CCA first exits startup.
777    pub fn set_startup_exit(&mut self, startup_exit: StartupExit) {
778        if self.startup_exit.is_none() {
779            self.startup_exit = Some(startup_exit);
780        }
781    }
782}
783
784/// Statistics from when a CCA first exited the startup phase.
785#[derive(Debug, Clone, Copy, PartialEq)]
786pub struct StartupExit {
787    /// The congestion_window recorded at Startup exit.
788    pub cwnd: usize,
789
790    /// The bandwidth estimate recorded at Startup exit.
791    pub bandwidth: Option<u64>,
792
793    /// The reason a CCA exited the startup phase.
794    pub reason: StartupExitReason,
795}
796
797impl StartupExit {
798    fn new(
799        cwnd: usize, bandwidth: Option<Bandwidth>, reason: StartupExitReason,
800    ) -> Self {
801        let bandwidth = bandwidth.map(Bandwidth::to_bytes_per_second);
802        Self {
803            cwnd,
804            bandwidth,
805            reason,
806        }
807    }
808}
809
810/// The reason a CCA exited the startup phase.
811#[derive(Debug, Clone, Copy, PartialEq)]
812pub enum StartupExitReason {
813    /// Exit slow start or BBR startup due to excessive loss
814    Loss,
815
816    /// Exit BBR startup due to bandwidth plateau.
817    BandwidthPlateau,
818
819    /// Exit BBR startup due to persistent queue.
820    PersistentQueue,
821
822    /// Exit HyStart++ conservative slow start after the max rounds allowed.
823    ConservativeSlowStartRounds,
824}
825
826#[cfg(test)]
827mod tests {
828    use super::*;
829    use crate::packet;
830    use crate::range_buf::RangeBuf;
831    use crate::test_utils;
832    use crate::CongestionControlAlgorithm;
833    use crate::DEFAULT_INITIAL_RTT;
834    use rstest::rstest;
835    use smallvec::smallvec;
836    use std::str::FromStr;
837
838    fn recovery_for_alg(algo: CongestionControlAlgorithm) -> Recovery {
839        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
840        cfg.set_cc_algorithm(algo);
841        Recovery::new(&cfg)
842    }
843
844    #[test]
845    fn lookup_cc_algo_ok() {
846        let algo = CongestionControlAlgorithm::from_str("reno").unwrap();
847        assert_eq!(algo, CongestionControlAlgorithm::Reno);
848        assert!(!recovery_for_alg(algo).gcongestion_enabled());
849
850        let algo = CongestionControlAlgorithm::from_str("cubic").unwrap();
851        assert_eq!(algo, CongestionControlAlgorithm::CUBIC);
852        assert!(!recovery_for_alg(algo).gcongestion_enabled());
853
854        let algo = CongestionControlAlgorithm::from_str("bbr").unwrap();
855        assert_eq!(algo, CongestionControlAlgorithm::Bbr2Gcongestion);
856        assert!(recovery_for_alg(algo).gcongestion_enabled());
857
858        let algo = CongestionControlAlgorithm::from_str("bbr2").unwrap();
859        assert_eq!(algo, CongestionControlAlgorithm::Bbr2Gcongestion);
860        assert!(recovery_for_alg(algo).gcongestion_enabled());
861
862        let algo =
863            CongestionControlAlgorithm::from_str("bbr2_gcongestion").unwrap();
864        assert_eq!(algo, CongestionControlAlgorithm::Bbr2Gcongestion);
865        assert!(recovery_for_alg(algo).gcongestion_enabled());
866    }
867
868    #[test]
869    fn lookup_cc_algo_bad() {
870        assert_eq!(
871            CongestionControlAlgorithm::from_str("???"),
872            Err(crate::Error::CongestionControl)
873        );
874    }
875
876    #[rstest]
877    fn loss_on_pto(
878        #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
879    ) {
880        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
881        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
882
883        let mut r = Recovery::new(&cfg);
884
885        let mut now = Instant::now();
886
887        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
888
889        // Start by sending a few packets.
890        let p = Sent {
891            pkt_num: 0,
892            frames: smallvec![],
893            time_sent: now,
894            time_acked: None,
895            time_lost: None,
896            size: 1000,
897            ack_eliciting: true,
898            in_flight: true,
899            delivered: 0,
900            delivered_time: now,
901            first_sent_time: now,
902            is_app_limited: false,
903            tx_in_flight: 0,
904            lost: 0,
905            has_data: false,
906            is_pmtud_probe: false,
907        };
908
909        r.on_packet_sent(
910            p,
911            packet::Epoch::Application,
912            HandshakeStatus::default(),
913            now,
914            "",
915        );
916
917        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
918        assert_eq!(r.bytes_in_flight(), 1000);
919        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
920
921        let p = Sent {
922            pkt_num: 1,
923            frames: smallvec![],
924            time_sent: now,
925            time_acked: None,
926            time_lost: None,
927            size: 1000,
928            ack_eliciting: true,
929            in_flight: true,
930            delivered: 0,
931            delivered_time: now,
932            first_sent_time: now,
933            is_app_limited: false,
934            tx_in_flight: 0,
935            lost: 0,
936            has_data: false,
937            is_pmtud_probe: false,
938        };
939
940        r.on_packet_sent(
941            p,
942            packet::Epoch::Application,
943            HandshakeStatus::default(),
944            now,
945            "",
946        );
947
948        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
949        assert_eq!(r.bytes_in_flight(), 2000);
950        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
951
952        let p = Sent {
953            pkt_num: 2,
954            frames: smallvec![],
955            time_sent: now,
956            time_acked: None,
957            time_lost: None,
958            size: 1000,
959            ack_eliciting: true,
960            in_flight: true,
961            delivered: 0,
962            delivered_time: now,
963            first_sent_time: now,
964            is_app_limited: false,
965            tx_in_flight: 0,
966            lost: 0,
967            has_data: false,
968            is_pmtud_probe: false,
969        };
970
971        r.on_packet_sent(
972            p,
973            packet::Epoch::Application,
974            HandshakeStatus::default(),
975            now,
976            "",
977        );
978        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
979        assert_eq!(r.bytes_in_flight(), 3000);
980        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
981
982        let p = Sent {
983            pkt_num: 3,
984            frames: smallvec![],
985            time_sent: now,
986            time_acked: None,
987            time_lost: None,
988            size: 1000,
989            ack_eliciting: true,
990            in_flight: true,
991            delivered: 0,
992            delivered_time: now,
993            first_sent_time: now,
994            is_app_limited: false,
995            tx_in_flight: 0,
996            lost: 0,
997            has_data: false,
998            is_pmtud_probe: false,
999        };
1000
1001        r.on_packet_sent(
1002            p,
1003            packet::Epoch::Application,
1004            HandshakeStatus::default(),
1005            now,
1006            "",
1007        );
1008        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1009        assert_eq!(r.bytes_in_flight(), 4000);
1010        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1011
1012        // Wait for 10ms.
1013        now += Duration::from_millis(10);
1014
1015        // Only the first 2 packets are acked.
1016        let mut acked = RangeSet::default();
1017        acked.insert(0..2);
1018
1019        assert_eq!(
1020            r.on_ack_received(
1021                &acked,
1022                25,
1023                packet::Epoch::Application,
1024                HandshakeStatus::default(),
1025                now,
1026                None,
1027                "",
1028            )
1029            .unwrap(),
1030            OnAckReceivedOutcome {
1031                lost_packets: 0,
1032                lost_bytes: 0,
1033                acked_bytes: 2 * 1000,
1034                spurious_losses: 0,
1035            }
1036        );
1037
1038        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1039        assert_eq!(r.bytes_in_flight(), 2000);
1040        assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(10));
1041        assert_eq!(r.lost_count(), 0);
1042
1043        // Wait until loss detection timer expires.
1044        now = r.loss_detection_timer().unwrap();
1045
1046        // PTO.
1047        r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
1048        assert_eq!(r.loss_probes(packet::Epoch::Application), 1);
1049        assert_eq!(r.lost_count(), 0);
1050        assert_eq!(r.pto_count(), 1);
1051
1052        let p = Sent {
1053            pkt_num: 4,
1054            frames: smallvec![],
1055            time_sent: now,
1056            time_acked: None,
1057            time_lost: None,
1058            size: 1000,
1059            ack_eliciting: true,
1060            in_flight: true,
1061            delivered: 0,
1062            delivered_time: now,
1063            first_sent_time: now,
1064            is_app_limited: false,
1065            tx_in_flight: 0,
1066            lost: 0,
1067            has_data: false,
1068            is_pmtud_probe: false,
1069        };
1070
1071        r.on_packet_sent(
1072            p,
1073            packet::Epoch::Application,
1074            HandshakeStatus::default(),
1075            now,
1076            "",
1077        );
1078        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
1079        assert_eq!(r.bytes_in_flight(), 3000);
1080        assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(30));
1081
1082        let p = Sent {
1083            pkt_num: 5,
1084            frames: smallvec![],
1085            time_sent: now,
1086            time_acked: None,
1087            time_lost: None,
1088            size: 1000,
1089            ack_eliciting: true,
1090            in_flight: true,
1091            delivered: 0,
1092            delivered_time: now,
1093            first_sent_time: now,
1094            is_app_limited: false,
1095            tx_in_flight: 0,
1096            lost: 0,
1097            has_data: false,
1098            is_pmtud_probe: false,
1099        };
1100
1101        r.on_packet_sent(
1102            p,
1103            packet::Epoch::Application,
1104            HandshakeStatus::default(),
1105            now,
1106            "",
1107        );
1108        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1109        assert_eq!(r.bytes_in_flight(), 4000);
1110        assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(30));
1111        assert_eq!(r.lost_count(), 0);
1112
1113        // Wait for 10ms.
1114        now += Duration::from_millis(10);
1115
1116        // PTO packets are acked.
1117        let mut acked = RangeSet::default();
1118        acked.insert(4..6);
1119
1120        assert_eq!(
1121            r.on_ack_received(
1122                &acked,
1123                25,
1124                packet::Epoch::Application,
1125                HandshakeStatus::default(),
1126                now,
1127                None,
1128                "",
1129            )
1130            .unwrap(),
1131            OnAckReceivedOutcome {
1132                lost_packets: 2,
1133                lost_bytes: 2000,
1134                acked_bytes: 2 * 1000,
1135                spurious_losses: 0,
1136            }
1137        );
1138
1139        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1140        assert_eq!(r.bytes_in_flight(), 0);
1141        assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(40));
1142
1143        assert_eq!(r.lost_count(), 2);
1144
1145        // Wait 1 RTT.
1146        now += r.rtt();
1147
1148        assert_eq!(
1149            r.detect_lost_packets_for_test(packet::Epoch::Application, now),
1150            (0, 0)
1151        );
1152
1153        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1154        if cc_algorithm_name == "reno" || cc_algorithm_name == "cubic" {
1155            assert!(r.startup_exit().is_some());
1156            assert_eq!(r.startup_exit().unwrap().reason, StartupExitReason::Loss);
1157        } else {
1158            assert_eq!(r.startup_exit(), None);
1159        }
1160    }
1161
1162    #[rstest]
1163    fn loss_on_timer(
1164        #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
1165    ) {
1166        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1167        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1168
1169        let mut r = Recovery::new(&cfg);
1170
1171        let mut now = Instant::now();
1172
1173        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1174
1175        // Start by sending a few packets.
1176        let p = Sent {
1177            pkt_num: 0,
1178            frames: smallvec![],
1179            time_sent: now,
1180            time_acked: None,
1181            time_lost: None,
1182            size: 1000,
1183            ack_eliciting: true,
1184            in_flight: true,
1185            delivered: 0,
1186            delivered_time: now,
1187            first_sent_time: now,
1188            is_app_limited: false,
1189            tx_in_flight: 0,
1190            lost: 0,
1191            has_data: false,
1192            is_pmtud_probe: false,
1193        };
1194
1195        r.on_packet_sent(
1196            p,
1197            packet::Epoch::Application,
1198            HandshakeStatus::default(),
1199            now,
1200            "",
1201        );
1202        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
1203        assert_eq!(r.bytes_in_flight(), 1000);
1204        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1205
1206        let p = Sent {
1207            pkt_num: 1,
1208            frames: smallvec![],
1209            time_sent: now,
1210            time_acked: None,
1211            time_lost: None,
1212            size: 1000,
1213            ack_eliciting: true,
1214            in_flight: true,
1215            delivered: 0,
1216            delivered_time: now,
1217            first_sent_time: now,
1218            is_app_limited: false,
1219            tx_in_flight: 0,
1220            lost: 0,
1221            has_data: false,
1222            is_pmtud_probe: false,
1223        };
1224
1225        r.on_packet_sent(
1226            p,
1227            packet::Epoch::Application,
1228            HandshakeStatus::default(),
1229            now,
1230            "",
1231        );
1232        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1233        assert_eq!(r.bytes_in_flight(), 2000);
1234        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1235
1236        let p = Sent {
1237            pkt_num: 2,
1238            frames: smallvec![],
1239            time_sent: now,
1240            time_acked: None,
1241            time_lost: None,
1242            size: 1000,
1243            ack_eliciting: true,
1244            in_flight: true,
1245            delivered: 0,
1246            delivered_time: now,
1247            first_sent_time: now,
1248            is_app_limited: false,
1249            tx_in_flight: 0,
1250            lost: 0,
1251            has_data: false,
1252            is_pmtud_probe: false,
1253        };
1254
1255        r.on_packet_sent(
1256            p,
1257            packet::Epoch::Application,
1258            HandshakeStatus::default(),
1259            now,
1260            "",
1261        );
1262        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
1263        assert_eq!(r.bytes_in_flight(), 3000);
1264        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1265
1266        let p = Sent {
1267            pkt_num: 3,
1268            frames: smallvec![],
1269            time_sent: now,
1270            time_acked: None,
1271            time_lost: None,
1272            size: 1000,
1273            ack_eliciting: true,
1274            in_flight: true,
1275            delivered: 0,
1276            delivered_time: now,
1277            first_sent_time: now,
1278            is_app_limited: false,
1279            tx_in_flight: 0,
1280            lost: 0,
1281            has_data: false,
1282            is_pmtud_probe: false,
1283        };
1284
1285        r.on_packet_sent(
1286            p,
1287            packet::Epoch::Application,
1288            HandshakeStatus::default(),
1289            now,
1290            "",
1291        );
1292        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1293        assert_eq!(r.bytes_in_flight(), 4000);
1294        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1295
1296        // Wait for 10ms.
1297        now += Duration::from_millis(10);
1298
1299        // Only the first 2 packets and the last one are acked.
1300        let mut acked = RangeSet::default();
1301        acked.insert(0..2);
1302        acked.insert(3..4);
1303
1304        assert_eq!(
1305            r.on_ack_received(
1306                &acked,
1307                25,
1308                packet::Epoch::Application,
1309                HandshakeStatus::default(),
1310                now,
1311                None,
1312                "",
1313            )
1314            .unwrap(),
1315            OnAckReceivedOutcome {
1316                lost_packets: 0,
1317                lost_bytes: 0,
1318                acked_bytes: 3 * 1000,
1319                spurious_losses: 0,
1320            }
1321        );
1322
1323        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1324        assert_eq!(r.bytes_in_flight(), 1000);
1325        assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(10));
1326        assert_eq!(r.lost_count(), 0);
1327
1328        // Wait until loss detection timer expires.
1329        now = r.loss_detection_timer().unwrap();
1330
1331        // Packet is declared lost.
1332        r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
1333        assert_eq!(r.loss_probes(packet::Epoch::Application), 0);
1334
1335        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1336        assert_eq!(r.bytes_in_flight(), 0);
1337        assert_eq!(r.bytes_in_flight_duration(), Duration::from_micros(11250));
1338
1339        assert_eq!(r.lost_count(), 1);
1340
1341        // Wait 1 RTT.
1342        now += r.rtt();
1343
1344        assert_eq!(
1345            r.detect_lost_packets_for_test(packet::Epoch::Application, now),
1346            (0, 0)
1347        );
1348
1349        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1350        if cc_algorithm_name == "reno" || cc_algorithm_name == "cubic" {
1351            assert!(r.startup_exit().is_some());
1352            assert_eq!(r.startup_exit().unwrap().reason, StartupExitReason::Loss);
1353        } else {
1354            assert_eq!(r.startup_exit(), None);
1355        }
1356    }
1357
1358    #[rstest]
1359    fn loss_on_reordering(
1360        #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
1361    ) {
1362        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1363        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1364
1365        let mut r = Recovery::new(&cfg);
1366
1367        let mut now = Instant::now();
1368
1369        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1370
1371        // Start by sending a few packets.
1372        //
1373        // pkt number: [0, 1, 2, 3]
1374        for i in 0..4 {
1375            let p = test_utils::helper_packet_sent(i, now, 1000);
1376            r.on_packet_sent(
1377                p,
1378                packet::Epoch::Application,
1379                HandshakeStatus::default(),
1380                now,
1381                "",
1382            );
1383
1384            let pkt_count = (i + 1) as usize;
1385            assert_eq!(r.sent_packets_len(packet::Epoch::Application), pkt_count);
1386            assert_eq!(r.bytes_in_flight(), pkt_count * 1000);
1387            assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1388        }
1389
1390        // Wait for 10ms after sending.
1391        now += Duration::from_millis(10);
1392
1393        // Recieve reordered ACKs, i.e. pkt_num [2, 3]
1394        let mut acked = RangeSet::default();
1395        acked.insert(2..4);
1396        assert_eq!(
1397            r.on_ack_received(
1398                &acked,
1399                25,
1400                packet::Epoch::Application,
1401                HandshakeStatus::default(),
1402                now,
1403                None,
1404                "",
1405            )
1406            .unwrap(),
1407            OnAckReceivedOutcome {
1408                lost_packets: 1,
1409                lost_bytes: 1000,
1410                acked_bytes: 1000 * 2,
1411                spurious_losses: 0,
1412            }
1413        );
1414        // Since we only remove packets from the back to avoid compaction, the
1415        // send length remains the same after receiving reordered ACKs
1416        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1417        assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1418        assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1419
1420        // Wait for 10ms after receiving first set of ACKs.
1421        now += Duration::from_millis(10);
1422
1423        // Recieve remaining ACKs, i.e. pkt_num [0, 1]
1424        let mut acked = RangeSet::default();
1425        acked.insert(0..2);
1426        assert_eq!(
1427            r.on_ack_received(
1428                &acked,
1429                25,
1430                packet::Epoch::Application,
1431                HandshakeStatus::default(),
1432                now,
1433                None,
1434                "",
1435            )
1436            .unwrap(),
1437            OnAckReceivedOutcome {
1438                lost_packets: 0,
1439                lost_bytes: 0,
1440                acked_bytes: 1000,
1441                spurious_losses: 1,
1442            }
1443        );
1444        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1445        assert_eq!(r.bytes_in_flight(), 0);
1446        assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(20));
1447
1448        // Spurious loss.
1449        assert_eq!(r.lost_count(), 1);
1450        assert_eq!(r.lost_spurious_count(), 1);
1451
1452        // Packet threshold was increased.
1453        assert_eq!(r.pkt_thresh().unwrap(), 4);
1454        assert_eq!(r.time_thresh(), PACKET_REORDER_TIME_THRESHOLD);
1455
1456        // Wait 1 RTT.
1457        now += r.rtt();
1458
1459        // All packets have been ACKed so dont expect additional lost packets
1460        assert_eq!(
1461            r.detect_lost_packets_for_test(packet::Epoch::Application, now),
1462            (0, 0)
1463        );
1464        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1465
1466        if cc_algorithm_name == "reno" || cc_algorithm_name == "cubic" {
1467            assert!(r.startup_exit().is_some());
1468            assert_eq!(r.startup_exit().unwrap().reason, StartupExitReason::Loss);
1469        } else {
1470            assert_eq!(r.startup_exit(), None);
1471        }
1472    }
1473
1474    // TODO: This should run agains both `congestion` and `gcongestion`.
1475    // `congestion` and `gcongestion` behave differently. That might be ok
1476    // given the different algorithms but it would be ideal to merge and share
1477    // the logic.
1478    #[rstest]
1479    fn time_thresholds_on_reordering(
1480        #[values("bbr2_gcongestion")] cc_algorithm_name: &str,
1481    ) {
1482        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1483        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1484
1485        let mut now = Instant::now();
1486        let mut r = Recovery::new(&cfg);
1487        assert_eq!(r.rtt(), DEFAULT_INITIAL_RTT);
1488
1489        // Pick time between and above thresholds for testing threshold increase.
1490        //
1491        //```
1492        //              between_thresh_ms
1493        //                         |
1494        //    initial_thresh_ms    |     spurious_thresh_ms
1495        //      v                  v             v
1496        // --------------------------------------------------
1497        //      | ................ | ..................... |
1498        //            THRESH_GAP         THRESH_GAP
1499        // ```
1500        // 
1501        // Threshold gap time.
1502        const THRESH_GAP: Duration = Duration::from_millis(30);
1503        // Initial time theshold based on inital RTT.
1504        let initial_thresh_ms =
1505            DEFAULT_INITIAL_RTT.mul_f64(INITIAL_TIME_THRESHOLD);
1506        // The time threshold after spurious loss.
1507        let spurious_thresh_ms: Duration =
1508            DEFAULT_INITIAL_RTT.mul_f64(PACKET_REORDER_TIME_THRESHOLD);
1509        // Time between the two thresholds
1510        let between_thresh_ms = initial_thresh_ms + THRESH_GAP;
1511        assert!(between_thresh_ms > initial_thresh_ms);
1512        assert!(between_thresh_ms < spurious_thresh_ms);
1513        assert!(between_thresh_ms + THRESH_GAP > spurious_thresh_ms);
1514
1515        for i in 0..6 {
1516            let send_time = now + i * between_thresh_ms;
1517
1518            let p = test_utils::helper_packet_sent(i.into(), send_time, 1000);
1519            r.on_packet_sent(
1520                p,
1521                packet::Epoch::Application,
1522                HandshakeStatus::default(),
1523                send_time,
1524                "",
1525            );
1526        }
1527
1528        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 6);
1529        assert_eq!(r.bytes_in_flight(), 6 * 1000);
1530        assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1531        assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1532
1533        // Wait for `between_thresh_ms` after sending to trigger loss based on
1534        // loss threshold.
1535        now += between_thresh_ms;
1536
1537        // Ack packet: 1
1538        //
1539        // [0, 1, 2, 3, 4, 5]
1540        //     ^
1541        let mut acked = RangeSet::default();
1542        acked.insert(1..2);
1543        assert_eq!(
1544            r.on_ack_received(
1545                &acked,
1546                25,
1547                packet::Epoch::Application,
1548                HandshakeStatus::default(),
1549                now,
1550                None,
1551                "",
1552            )
1553            .unwrap(),
1554            OnAckReceivedOutcome {
1555                lost_packets: 1,
1556                lost_bytes: 1000,
1557                acked_bytes: 1000,
1558                spurious_losses: 0,
1559            }
1560        );
1561        assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1562        assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1563
1564        // Ack packet: 0
1565        //
1566        // [0, 1, 2, 3, 4, 5]
1567        //  ^  x
1568        let mut acked = RangeSet::default();
1569        acked.insert(0..1);
1570        assert_eq!(
1571            r.on_ack_received(
1572                &acked,
1573                25,
1574                packet::Epoch::Application,
1575                HandshakeStatus::default(),
1576                now,
1577                None,
1578                "",
1579            )
1580            .unwrap(),
1581            OnAckReceivedOutcome {
1582                lost_packets: 0,
1583                lost_bytes: 0,
1584                acked_bytes: 0,
1585                spurious_losses: 1,
1586            }
1587        );
1588        // The time_thresh after spurious loss
1589        assert_eq!(r.time_thresh(), PACKET_REORDER_TIME_THRESHOLD);
1590
1591        // Wait for `between_thresh_ms` after sending. However, since the
1592        // threshold has increased, we do not expect loss.
1593        now += between_thresh_ms;
1594
1595        // Ack packet: 3
1596        //
1597        // [2, 3, 4, 5]
1598        //     ^
1599        let mut acked = RangeSet::default();
1600        acked.insert(3..4);
1601        assert_eq!(
1602            r.on_ack_received(
1603                &acked,
1604                25,
1605                packet::Epoch::Application,
1606                HandshakeStatus::default(),
1607                now,
1608                None,
1609                "",
1610            )
1611            .unwrap(),
1612            OnAckReceivedOutcome {
1613                lost_packets: 0,
1614                lost_bytes: 0,
1615                acked_bytes: 1000,
1616                spurious_losses: 0,
1617            }
1618        );
1619
1620        // Wait for and additional `plus_overhead` to trigger loss based on the
1621        // new time threshold.
1622        now += THRESH_GAP;
1623
1624        // Ack packet: 4
1625        //
1626        // [2, 3, 4, 5]
1627        //     x  ^
1628        let mut acked = RangeSet::default();
1629        acked.insert(4..5);
1630        assert_eq!(
1631            r.on_ack_received(
1632                &acked,
1633                25,
1634                packet::Epoch::Application,
1635                HandshakeStatus::default(),
1636                now,
1637                None,
1638                "",
1639            )
1640            .unwrap(),
1641            OnAckReceivedOutcome {
1642                lost_packets: 1,
1643                lost_bytes: 1000,
1644                acked_bytes: 1000,
1645                spurious_losses: 0,
1646            }
1647        );
1648    }
1649
1650    // TODO: Implement enable_relaxed_loss_threshold and enable this test for the
1651    // congestion module.
1652    #[rstest]
1653    fn relaxed_thresholds_on_reordering(
1654        #[values("bbr2_gcongestion")] cc_algorithm_name: &str,
1655    ) {
1656        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1657        cfg.enable_relaxed_loss_threshold = true;
1658        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1659
1660        let mut now = Instant::now();
1661        let mut r = Recovery::new(&cfg);
1662        assert_eq!(r.rtt(), DEFAULT_INITIAL_RTT);
1663
1664        // Pick time between and above thresholds for testing threshold increase.
1665        //
1666        //```
1667        //              between_thresh_ms
1668        //                         |
1669        //    initial_thresh_ms    |     spurious_thresh_ms
1670        //      v                  v             v
1671        // --------------------------------------------------
1672        //      | ................ | ..................... |
1673        //            THRESH_GAP         THRESH_GAP
1674        // ```
1675        // Threshold gap time.
1676        const THRESH_GAP: Duration = Duration::from_millis(30);
1677        // Initial time theshold based on inital RTT.
1678        let initial_thresh_ms =
1679            DEFAULT_INITIAL_RTT.mul_f64(INITIAL_TIME_THRESHOLD);
1680        // The time threshold after spurious loss.
1681        let spurious_thresh_ms: Duration =
1682            DEFAULT_INITIAL_RTT.mul_f64(PACKET_REORDER_TIME_THRESHOLD);
1683        // Time between the two thresholds
1684        let between_thresh_ms = initial_thresh_ms + THRESH_GAP;
1685        assert!(between_thresh_ms > initial_thresh_ms);
1686        assert!(between_thresh_ms < spurious_thresh_ms);
1687        assert!(between_thresh_ms + THRESH_GAP > spurious_thresh_ms);
1688
1689        for i in 0..6 {
1690            let send_time = now + i * between_thresh_ms;
1691
1692            let p = test_utils::helper_packet_sent(i.into(), send_time, 1000);
1693            r.on_packet_sent(
1694                p,
1695                packet::Epoch::Application,
1696                HandshakeStatus::default(),
1697                send_time,
1698                "",
1699            );
1700        }
1701
1702        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 6);
1703        assert_eq!(r.bytes_in_flight(), 6 * 1000);
1704        // Intitial thresholds
1705        assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1706        assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1707
1708        // Wait for `between_thresh_ms` after sending to trigger loss based on
1709        // loss threshold.
1710        now += between_thresh_ms;
1711
1712        // Ack packet: 1
1713        //
1714        // [0, 1, 2, 3, 4, 5]
1715        //     ^
1716        let mut acked = RangeSet::default();
1717        acked.insert(1..2);
1718        assert_eq!(
1719            r.on_ack_received(
1720                &acked,
1721                25,
1722                packet::Epoch::Application,
1723                HandshakeStatus::default(),
1724                now,
1725                None,
1726                "",
1727            )
1728            .unwrap(),
1729            OnAckReceivedOutcome {
1730                lost_packets: 1,
1731                lost_bytes: 1000,
1732                acked_bytes: 1000,
1733                spurious_losses: 0,
1734            }
1735        );
1736        // Thresholds after 1st loss
1737        assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1738        assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1739
1740        // Ack packet: 0
1741        //
1742        // [0, 1, 2, 3, 4, 5]
1743        //  ^  x
1744        let mut acked = RangeSet::default();
1745        acked.insert(0..1);
1746        assert_eq!(
1747            r.on_ack_received(
1748                &acked,
1749                25,
1750                packet::Epoch::Application,
1751                HandshakeStatus::default(),
1752                now,
1753                None,
1754                "",
1755            )
1756            .unwrap(),
1757            OnAckReceivedOutcome {
1758                lost_packets: 0,
1759                lost_bytes: 0,
1760                acked_bytes: 0,
1761                spurious_losses: 1,
1762            }
1763        );
1764        // Thresholds after 1st spurious loss
1765        //
1766        // Packet threshold should be disabled. Time threshold overhead should
1767        // stay the same.
1768        assert_eq!(r.pkt_thresh(), None);
1769        assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1770
1771        // Set now to send time of packet 2 so we can trigger spurious loss for
1772        // packet 2.
1773        now += between_thresh_ms;
1774        // Then wait for `between_thresh_ms` after sending packet 2 to trigger
1775        // loss. Since the time threshold has NOT increased, expect a
1776        // loss.
1777        now += between_thresh_ms;
1778
1779        // Ack packet: 3
1780        //
1781        // [2, 3, 4, 5]
1782        //     ^
1783        let mut acked = RangeSet::default();
1784        acked.insert(3..4);
1785        assert_eq!(
1786            r.on_ack_received(
1787                &acked,
1788                25,
1789                packet::Epoch::Application,
1790                HandshakeStatus::default(),
1791                now,
1792                None,
1793                "",
1794            )
1795            .unwrap(),
1796            OnAckReceivedOutcome {
1797                lost_packets: 1,
1798                lost_bytes: 1000,
1799                acked_bytes: 1000,
1800                spurious_losses: 0,
1801            }
1802        );
1803        // Thresholds after 2nd loss.
1804        assert_eq!(r.pkt_thresh(), None);
1805        assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1806
1807        // Wait for and additional `plus_overhead` to trigger loss based on the
1808        // new time threshold.
1809        // now += THRESH_GAP;
1810
1811        // Ack packet: 2
1812        //
1813        // [2, 3, 4, 5]
1814        //  ^  x
1815        let mut acked = RangeSet::default();
1816        acked.insert(2..3);
1817        assert_eq!(
1818            r.on_ack_received(
1819                &acked,
1820                25,
1821                packet::Epoch::Application,
1822                HandshakeStatus::default(),
1823                now,
1824                None,
1825                "",
1826            )
1827            .unwrap(),
1828            OnAckReceivedOutcome {
1829                lost_packets: 0,
1830                lost_bytes: 0,
1831                acked_bytes: 0,
1832                spurious_losses: 1,
1833            }
1834        );
1835        // Thresholds after 2nd spurious loss.
1836        //
1837        // Time threshold overhead should double.
1838        assert_eq!(r.pkt_thresh(), None);
1839        let double_time_thresh_overhead =
1840            1.0 + 2.0 * INITIAL_TIME_THRESHOLD_OVERHEAD;
1841        assert_eq!(r.time_thresh(), double_time_thresh_overhead);
1842    }
1843
1844    #[rstest]
1845    fn pacing(
1846        #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
1847        #[values(false, true)] time_sent_set_to_now: bool,
1848    ) {
1849        let pacing_enabled = cc_algorithm_name == "bbr2" ||
1850            cc_algorithm_name == "bbr2_gcongestion";
1851
1852        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1853        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1854
1855        #[cfg(feature = "internal")]
1856        cfg.set_custom_bbr_params(BbrParams {
1857            time_sent_set_to_now: Some(time_sent_set_to_now),
1858            ..Default::default()
1859        });
1860
1861        let mut r = Recovery::new(&cfg);
1862
1863        let mut now = Instant::now();
1864
1865        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1866
1867        // send out first packet burst (a full initcwnd).
1868        for i in 0..10 {
1869            let p = Sent {
1870                pkt_num: i,
1871                frames: smallvec![],
1872                time_sent: now,
1873                time_acked: None,
1874                time_lost: None,
1875                size: 1200,
1876                ack_eliciting: true,
1877                in_flight: true,
1878                delivered: 0,
1879                delivered_time: now,
1880                first_sent_time: now,
1881                is_app_limited: false,
1882                tx_in_flight: 0,
1883                lost: 0,
1884                has_data: true,
1885                is_pmtud_probe: false,
1886            };
1887
1888            r.on_packet_sent(
1889                p,
1890                packet::Epoch::Application,
1891                HandshakeStatus::default(),
1892                now,
1893                "",
1894            );
1895        }
1896
1897        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 10);
1898        assert_eq!(r.bytes_in_flight(), 12000);
1899        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1900
1901        if !pacing_enabled {
1902            assert_eq!(r.pacing_rate(), 0);
1903        } else {
1904            assert_eq!(r.pacing_rate(), 103963);
1905        }
1906        assert_eq!(r.get_packet_send_time(now), now);
1907
1908        assert_eq!(r.cwnd(), 12000);
1909        assert_eq!(r.cwnd_available(), 0);
1910
1911        // Wait 50ms for ACK.
1912        let initial_rtt = Duration::from_millis(50);
1913        now += initial_rtt;
1914
1915        let mut acked = RangeSet::default();
1916        acked.insert(0..10);
1917
1918        assert_eq!(
1919            r.on_ack_received(
1920                &acked,
1921                10,
1922                packet::Epoch::Application,
1923                HandshakeStatus::default(),
1924                now,
1925                None,
1926                "",
1927            )
1928            .unwrap(),
1929            OnAckReceivedOutcome {
1930                lost_packets: 0,
1931                lost_bytes: 0,
1932                acked_bytes: 12000,
1933                spurious_losses: 0,
1934            }
1935        );
1936
1937        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1938        assert_eq!(r.bytes_in_flight(), 0);
1939        assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
1940        assert_eq!(r.min_rtt(), Some(initial_rtt));
1941        assert_eq!(r.rtt(), initial_rtt);
1942
1943        // 10 MSS increased due to acks.
1944        assert_eq!(r.cwnd(), 12000 + 1200 * 10);
1945
1946        // Send the second packet burst.
1947        let p = Sent {
1948            pkt_num: 10,
1949            frames: smallvec![],
1950            time_sent: now,
1951            time_acked: None,
1952            time_lost: None,
1953            size: 6000,
1954            ack_eliciting: true,
1955            in_flight: true,
1956            delivered: 0,
1957            delivered_time: now,
1958            first_sent_time: now,
1959            is_app_limited: false,
1960            tx_in_flight: 0,
1961            lost: 0,
1962            has_data: true,
1963            is_pmtud_probe: false,
1964        };
1965
1966        r.on_packet_sent(
1967            p,
1968            packet::Epoch::Application,
1969            HandshakeStatus::default(),
1970            now,
1971            "",
1972        );
1973
1974        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
1975        assert_eq!(r.bytes_in_flight(), 6000);
1976        assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
1977
1978        if !pacing_enabled {
1979            // Pacing is disabled.
1980            assert_eq!(r.get_packet_send_time(now), now);
1981        } else {
1982            // Pacing is done from the beginning.
1983            assert_ne!(r.get_packet_send_time(now), now);
1984        }
1985
1986        // Send the third and fourth packet bursts together.
1987        let p = Sent {
1988            pkt_num: 11,
1989            frames: smallvec![],
1990            time_sent: now,
1991            time_acked: None,
1992            time_lost: None,
1993            size: 6000,
1994            ack_eliciting: true,
1995            in_flight: true,
1996            delivered: 0,
1997            delivered_time: now,
1998            first_sent_time: now,
1999            is_app_limited: false,
2000            tx_in_flight: 0,
2001            lost: 0,
2002            has_data: true,
2003            is_pmtud_probe: false,
2004        };
2005
2006        r.on_packet_sent(
2007            p,
2008            packet::Epoch::Application,
2009            HandshakeStatus::default(),
2010            now,
2011            "",
2012        );
2013
2014        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2015        assert_eq!(r.bytes_in_flight(), 12000);
2016        assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
2017
2018        // Send the fourth packet burst.
2019        let p = Sent {
2020            pkt_num: 12,
2021            frames: smallvec![],
2022            time_sent: now,
2023            time_acked: None,
2024            time_lost: None,
2025            size: 1000,
2026            ack_eliciting: true,
2027            in_flight: true,
2028            delivered: 0,
2029            delivered_time: now,
2030            first_sent_time: now,
2031            is_app_limited: false,
2032            tx_in_flight: 0,
2033            lost: 0,
2034            has_data: true,
2035            is_pmtud_probe: false,
2036        };
2037
2038        r.on_packet_sent(
2039            p,
2040            packet::Epoch::Application,
2041            HandshakeStatus::default(),
2042            now,
2043            "",
2044        );
2045
2046        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
2047        assert_eq!(r.bytes_in_flight(), 13000);
2048        assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
2049
2050        // We pace this outgoing packet. as all conditions for pacing
2051        // are passed.
2052        let pacing_rate = if pacing_enabled {
2053            let cwnd_gain: f64 = 2.0;
2054            // Adjust for cwnd_gain.  BW estimate was made before the CWND
2055            // increase.
2056            let bw = r.cwnd() as f64 / cwnd_gain / initial_rtt.as_secs_f64();
2057            bw as u64
2058        } else {
2059            0
2060        };
2061        assert_eq!(r.pacing_rate(), pacing_rate);
2062
2063        let scale_factor = if pacing_enabled {
2064            // For bbr2_gcongestion, send time is almost 13000 / pacing_rate.
2065            // Don't know where 13000 comes from.
2066            1.08333332
2067        } else {
2068            1.0
2069        };
2070        assert_eq!(
2071            r.get_packet_send_time(now) - now,
2072            if pacing_enabled {
2073                Duration::from_secs_f64(
2074                    scale_factor * 12000.0 / pacing_rate as f64,
2075                )
2076            } else {
2077                Duration::ZERO
2078            }
2079        );
2080        assert_eq!(r.startup_exit(), None);
2081
2082        let reduced_rtt = Duration::from_millis(40);
2083        now += reduced_rtt;
2084
2085        let mut acked = RangeSet::default();
2086        acked.insert(10..11);
2087
2088        assert_eq!(
2089            r.on_ack_received(
2090                &acked,
2091                0,
2092                packet::Epoch::Application,
2093                HandshakeStatus::default(),
2094                now,
2095                None,
2096                "",
2097            )
2098            .unwrap(),
2099            OnAckReceivedOutcome {
2100                lost_packets: 0,
2101                lost_bytes: 0,
2102                acked_bytes: 6000,
2103                spurious_losses: 0,
2104            }
2105        );
2106
2107        let expected_srtt = (7 * initial_rtt + reduced_rtt) / 8;
2108        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2109        assert_eq!(r.bytes_in_flight(), 7000);
2110        assert_eq!(r.bytes_in_flight_duration(), initial_rtt + reduced_rtt);
2111        assert_eq!(r.min_rtt(), Some(reduced_rtt));
2112        assert_eq!(r.rtt(), expected_srtt);
2113
2114        let mut acked = RangeSet::default();
2115        acked.insert(11..12);
2116
2117        assert_eq!(
2118            r.on_ack_received(
2119                &acked,
2120                0,
2121                packet::Epoch::Application,
2122                HandshakeStatus::default(),
2123                now,
2124                None,
2125                "",
2126            )
2127            .unwrap(),
2128            OnAckReceivedOutcome {
2129                lost_packets: 0,
2130                lost_bytes: 0,
2131                acked_bytes: 6000,
2132                spurious_losses: 0,
2133            }
2134        );
2135
2136        // When enabled, the pacer adds a 25msec delay to the packet
2137        // sends which will be applied to the sent times tracked by
2138        // the recovery module, bringing down RTT to 15msec.
2139        let expected_min_rtt = if pacing_enabled &&
2140            !time_sent_set_to_now &&
2141            cfg!(feature = "internal")
2142        {
2143            reduced_rtt - Duration::from_millis(25)
2144        } else {
2145            reduced_rtt
2146        };
2147
2148        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
2149        assert_eq!(r.bytes_in_flight(), 1000);
2150        assert_eq!(r.bytes_in_flight_duration(), initial_rtt + reduced_rtt);
2151        assert_eq!(r.min_rtt(), Some(expected_min_rtt));
2152
2153        let expected_srtt = (7 * expected_srtt + expected_min_rtt) / 8;
2154        assert_eq!(r.rtt(), expected_srtt);
2155
2156        let mut acked = RangeSet::default();
2157        acked.insert(12..13);
2158
2159        assert_eq!(
2160            r.on_ack_received(
2161                &acked,
2162                0,
2163                packet::Epoch::Application,
2164                HandshakeStatus::default(),
2165                now,
2166                None,
2167                "",
2168            )
2169            .unwrap(),
2170            OnAckReceivedOutcome {
2171                lost_packets: 0,
2172                lost_bytes: 0,
2173                acked_bytes: 1000,
2174                spurious_losses: 0,
2175            }
2176        );
2177
2178        // Pacer adds 50msec delay to the second packet, resulting in
2179        // an effective RTT of 0.
2180        let expected_min_rtt = if pacing_enabled &&
2181            !time_sent_set_to_now &&
2182            cfg!(feature = "internal")
2183        {
2184            Duration::from_millis(0)
2185        } else {
2186            reduced_rtt
2187        };
2188        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2189        assert_eq!(r.bytes_in_flight(), 0);
2190        assert_eq!(r.bytes_in_flight_duration(), initial_rtt + reduced_rtt);
2191        assert_eq!(r.min_rtt(), Some(expected_min_rtt));
2192
2193        let expected_srtt = (7 * expected_srtt + expected_min_rtt) / 8;
2194        assert_eq!(r.rtt(), expected_srtt);
2195    }
2196
2197    #[rstest]
2198    // initial_cwnd / first_rtt == initial_pacing_rate.  Pacing is 1.0 * bw before
2199    // and after.
2200    #[case::bw_estimate_equal_after_first_rtt(1.0, 1.0)]
2201    // initial_cwnd / first_rtt < initial_pacing_rate.  Pacing decreases from 2 *
2202    // bw to 1.0 * bw.
2203    #[case::bw_estimate_decrease_after_first_rtt(2.0, 1.0)]
2204    // initial_cwnd / first_rtt > initial_pacing_rate from 0.5 * bw to 1.0 * bw.
2205    // Initial pacing remains 0.5 * bw because the initial_pacing_rate parameter
2206    // is used an upper bound for the pacing rate after the first RTT.
2207    // Pacing rate after the first ACK should be:
2208    // min(initial_pacing_rate_bytes_per_second, init_cwnd / first_rtt)
2209    #[case::bw_estimate_increase_after_first_rtt(0.5, 0.5)]
2210    #[cfg(feature = "internal")]
2211    fn initial_pacing_rate_override(
2212        #[case] initial_multipler: f64, #[case] expected_multiplier: f64,
2213    ) {
2214        let rtt = Duration::from_millis(50);
2215        let bw = Bandwidth::from_bytes_and_time_delta(12000, rtt);
2216        let initial_pacing_rate_hint = bw * initial_multipler;
2217        let expected_pacing_with_rtt_measurement = bw * expected_multiplier;
2218
2219        let cc_algorithm_name = "bbr2_gcongestion";
2220        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2221        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2222        cfg.set_custom_bbr_params(BbrParams {
2223            initial_pacing_rate_bytes_per_second: Some(
2224                initial_pacing_rate_hint.to_bytes_per_second(),
2225            ),
2226            ..Default::default()
2227        });
2228
2229        let mut r = Recovery::new(&cfg);
2230
2231        let mut now = Instant::now();
2232
2233        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2234
2235        // send some packets.
2236        for i in 0..2 {
2237            let p = test_utils::helper_packet_sent(i, now, 1200);
2238            r.on_packet_sent(
2239                p,
2240                packet::Epoch::Application,
2241                HandshakeStatus::default(),
2242                now,
2243                "",
2244            );
2245        }
2246
2247        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2248        assert_eq!(r.bytes_in_flight(), 2400);
2249        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
2250
2251        // Initial pacing rate matches the override value.
2252        assert_eq!(
2253            r.pacing_rate(),
2254            initial_pacing_rate_hint.to_bytes_per_second()
2255        );
2256        assert_eq!(r.get_packet_send_time(now), now);
2257
2258        assert_eq!(r.cwnd(), 12000);
2259        assert_eq!(r.cwnd_available(), 9600);
2260
2261        // Wait 1 rtt for ACK.
2262        now += rtt;
2263
2264        let mut acked = RangeSet::default();
2265        acked.insert(0..2);
2266
2267        assert_eq!(
2268            r.on_ack_received(
2269                &acked,
2270                10,
2271                packet::Epoch::Application,
2272                HandshakeStatus::default(),
2273                now,
2274                None,
2275                "",
2276            )
2277            .unwrap(),
2278            OnAckReceivedOutcome {
2279                lost_packets: 0,
2280                lost_bytes: 0,
2281                acked_bytes: 2400,
2282                spurious_losses: 0,
2283            }
2284        );
2285
2286        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2287        assert_eq!(r.bytes_in_flight(), 0);
2288        assert_eq!(r.bytes_in_flight_duration(), rtt);
2289        assert_eq!(r.rtt(), rtt);
2290
2291        // Pacing rate is recalculated based on initial cwnd when the
2292        // first RTT estimate is available.
2293        assert_eq!(
2294            r.pacing_rate(),
2295            expected_pacing_with_rtt_measurement.to_bytes_per_second()
2296        );
2297    }
2298
2299    #[rstest]
2300    fn validate_ack_range_on_ack_received(
2301        #[values("cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2302    ) {
2303        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2304        cfg.set_cc_algorithm_name(cc_algorithm_name).unwrap();
2305
2306        let epoch = packet::Epoch::Application;
2307        let mut r = Recovery::new(&cfg);
2308        let mut now = Instant::now();
2309        assert_eq!(r.sent_packets_len(epoch), 0);
2310
2311        // Send 4 packets
2312        let pkt_size = 1000;
2313        let pkt_count = 4;
2314        for pkt_num in 0..pkt_count {
2315            let sent = test_utils::helper_packet_sent(pkt_num, now, pkt_size);
2316            r.on_packet_sent(sent, epoch, HandshakeStatus::default(), now, "");
2317        }
2318        assert_eq!(r.sent_packets_len(epoch), pkt_count as usize);
2319        assert_eq!(r.bytes_in_flight(), pkt_count as usize * pkt_size);
2320        assert!(r.get_largest_acked_on_epoch(epoch).is_none());
2321        assert_eq!(r.largest_sent_pkt_num_on_path(epoch).unwrap(), 3);
2322
2323        // Wait for 10ms.
2324        now += Duration::from_millis(10);
2325
2326        // ACK 2 packets
2327        let mut acked = RangeSet::default();
2328        acked.insert(0..2);
2329
2330        assert_eq!(
2331            r.on_ack_received(
2332                &acked,
2333                25,
2334                epoch,
2335                HandshakeStatus::default(),
2336                now,
2337                None,
2338                "",
2339            )
2340            .unwrap(),
2341            OnAckReceivedOutcome {
2342                lost_packets: 0,
2343                lost_bytes: 0,
2344                acked_bytes: 2 * 1000,
2345                spurious_losses: 0,
2346            }
2347        );
2348
2349        assert_eq!(r.sent_packets_len(epoch), 2);
2350        assert_eq!(r.bytes_in_flight(), 2 * 1000);
2351
2352        assert_eq!(r.get_largest_acked_on_epoch(epoch).unwrap(), 1);
2353        assert_eq!(r.largest_sent_pkt_num_on_path(epoch).unwrap(), 3);
2354
2355        // ACK large range
2356        let mut acked = RangeSet::default();
2357        acked.insert(0..10);
2358        assert_eq!(
2359            r.on_ack_received(
2360                &acked,
2361                25,
2362                epoch,
2363                HandshakeStatus::default(),
2364                now,
2365                None,
2366                "",
2367            )
2368            .unwrap(),
2369            OnAckReceivedOutcome {
2370                lost_packets: 0,
2371                lost_bytes: 0,
2372                acked_bytes: 2 * 1000,
2373                spurious_losses: 0,
2374            }
2375        );
2376        assert_eq!(r.sent_packets_len(epoch), 0);
2377        assert_eq!(r.bytes_in_flight(), 0);
2378
2379        assert_eq!(r.get_largest_acked_on_epoch(epoch).unwrap(), 3);
2380        assert_eq!(r.largest_sent_pkt_num_on_path(epoch).unwrap(), 3);
2381    }
2382
2383    #[rstest]
2384    fn pmtud_loss_on_timer(
2385        #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2386    ) {
2387        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2388        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2389
2390        let mut r = Recovery::new(&cfg);
2391        assert_eq!(r.cwnd(), 12000);
2392
2393        let mut now = Instant::now();
2394
2395        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2396
2397        // Start by sending a few packets.
2398        let p = Sent {
2399            pkt_num: 0,
2400            frames: smallvec![],
2401            time_sent: now,
2402            time_acked: None,
2403            time_lost: None,
2404            size: 1000,
2405            ack_eliciting: true,
2406            in_flight: true,
2407            delivered: 0,
2408            delivered_time: now,
2409            first_sent_time: now,
2410            is_app_limited: false,
2411            tx_in_flight: 0,
2412            lost: 0,
2413            has_data: false,
2414            is_pmtud_probe: false,
2415        };
2416
2417        r.on_packet_sent(
2418            p,
2419            packet::Epoch::Application,
2420            HandshakeStatus::default(),
2421            now,
2422            "",
2423        );
2424
2425        assert_eq!(r.in_flight_count(packet::Epoch::Application), 1);
2426        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
2427        assert_eq!(r.bytes_in_flight(), 1000);
2428        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
2429
2430        let p = Sent {
2431            pkt_num: 1,
2432            frames: smallvec![],
2433            time_sent: now,
2434            time_acked: None,
2435            time_lost: None,
2436            size: 1000,
2437            ack_eliciting: true,
2438            in_flight: true,
2439            delivered: 0,
2440            delivered_time: now,
2441            first_sent_time: now,
2442            is_app_limited: false,
2443            tx_in_flight: 0,
2444            lost: 0,
2445            has_data: false,
2446            is_pmtud_probe: true,
2447        };
2448
2449        r.on_packet_sent(
2450            p,
2451            packet::Epoch::Application,
2452            HandshakeStatus::default(),
2453            now,
2454            "",
2455        );
2456
2457        assert_eq!(r.in_flight_count(packet::Epoch::Application), 2);
2458
2459        let p = Sent {
2460            pkt_num: 2,
2461            frames: smallvec![],
2462            time_sent: now,
2463            time_acked: None,
2464            time_lost: None,
2465            size: 1000,
2466            ack_eliciting: true,
2467            in_flight: true,
2468            delivered: 0,
2469            delivered_time: now,
2470            first_sent_time: now,
2471            is_app_limited: false,
2472            tx_in_flight: 0,
2473            lost: 0,
2474            has_data: false,
2475            is_pmtud_probe: false,
2476        };
2477
2478        r.on_packet_sent(
2479            p,
2480            packet::Epoch::Application,
2481            HandshakeStatus::default(),
2482            now,
2483            "",
2484        );
2485
2486        assert_eq!(r.in_flight_count(packet::Epoch::Application), 3);
2487
2488        // Wait for 10ms.
2489        now += Duration::from_millis(10);
2490
2491        // Only the first  packets and the last one are acked.
2492        let mut acked = RangeSet::default();
2493        acked.insert(0..1);
2494        acked.insert(2..3);
2495
2496        assert_eq!(
2497            r.on_ack_received(
2498                &acked,
2499                25,
2500                packet::Epoch::Application,
2501                HandshakeStatus::default(),
2502                now,
2503                None,
2504                "",
2505            )
2506            .unwrap(),
2507            OnAckReceivedOutcome {
2508                lost_packets: 0,
2509                lost_bytes: 0,
2510                acked_bytes: 2 * 1000,
2511                spurious_losses: 0,
2512            }
2513        );
2514
2515        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2516        assert_eq!(r.bytes_in_flight(), 1000);
2517        assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(10));
2518        assert_eq!(r.lost_count(), 0);
2519
2520        // Wait until loss detection timer expires.
2521        now = r.loss_detection_timer().unwrap();
2522
2523        // Packet is declared lost.
2524        r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
2525        assert_eq!(r.loss_probes(packet::Epoch::Application), 0);
2526
2527        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2528        assert_eq!(r.in_flight_count(packet::Epoch::Application), 0);
2529        assert_eq!(r.bytes_in_flight(), 0);
2530        assert_eq!(r.bytes_in_flight_duration(), Duration::from_micros(11250));
2531        assert_eq!(r.cwnd(), 12000);
2532
2533        assert_eq!(r.lost_count(), 0);
2534
2535        // Wait 1 RTT.
2536        now += r.rtt();
2537
2538        assert_eq!(
2539            r.detect_lost_packets_for_test(packet::Epoch::Application, now),
2540            (0, 0)
2541        );
2542
2543        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2544        assert_eq!(r.in_flight_count(packet::Epoch::Application), 0);
2545        assert_eq!(r.bytes_in_flight(), 0);
2546        assert_eq!(r.bytes_in_flight_duration(), Duration::from_micros(11250));
2547        assert_eq!(r.lost_count(), 0);
2548        assert_eq!(r.startup_exit(), None);
2549    }
2550
2551    // Modeling delivery_rate for gcongestion is non-trivial so we only test the
2552    // congestion specific algorithms.
2553    #[rstest]
2554    fn congestion_delivery_rate(
2555        #[values("reno", "cubic", "bbr2")] cc_algorithm_name: &str,
2556    ) {
2557        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2558        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2559
2560        let mut r = Recovery::new(&cfg);
2561        assert_eq!(r.cwnd(), 12000);
2562
2563        let now = Instant::now();
2564
2565        let mut total_bytes_sent = 0;
2566        for pn in 0..10 {
2567            // Start by sending a few packets.
2568            let bytes = 1000;
2569            let sent = test_utils::helper_packet_sent(pn, now, bytes);
2570            r.on_packet_sent(
2571                sent,
2572                packet::Epoch::Application,
2573                HandshakeStatus::default(),
2574                now,
2575                "",
2576            );
2577
2578            total_bytes_sent += bytes;
2579        }
2580
2581        // Ack
2582        let interval = Duration::from_secs(10);
2583        let mut acked = RangeSet::default();
2584        acked.insert(0..10);
2585        assert_eq!(
2586            r.on_ack_received(
2587                &acked,
2588                25,
2589                packet::Epoch::Application,
2590                HandshakeStatus::default(),
2591                now + interval,
2592                None,
2593                "",
2594            )
2595            .unwrap(),
2596            OnAckReceivedOutcome {
2597                lost_packets: 0,
2598                lost_bytes: 0,
2599                acked_bytes: total_bytes_sent,
2600                spurious_losses: 0,
2601            }
2602        );
2603        assert_eq!(r.delivery_rate().to_bytes_per_second(), 1000);
2604        assert_eq!(r.min_rtt().unwrap(), interval);
2605        // delivery rate should be in units bytes/sec
2606        assert_eq!(
2607            total_bytes_sent as u64 / interval.as_secs(),
2608            r.delivery_rate().to_bytes_per_second()
2609        );
2610        assert_eq!(r.startup_exit(), None);
2611    }
2612
2613    #[rstest]
2614    fn acks_with_no_retransmittable_data(
2615        #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2616    ) {
2617        let rtt = Duration::from_millis(100);
2618
2619        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2620        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2621
2622        let mut r = Recovery::new(&cfg);
2623
2624        let mut now = Instant::now();
2625
2626        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2627
2628        let mut next_packet = 0;
2629        // send some packets.
2630        for _ in 0..3 {
2631            let p = test_utils::helper_packet_sent(next_packet, now, 1200);
2632            next_packet += 1;
2633            r.on_packet_sent(
2634                p,
2635                packet::Epoch::Application,
2636                HandshakeStatus::default(),
2637                now,
2638                "",
2639            );
2640        }
2641
2642        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
2643        assert_eq!(r.bytes_in_flight(), 3600);
2644        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
2645
2646        assert_eq!(
2647            r.pacing_rate(),
2648            if cc_algorithm_name == "bbr2_gcongestion" {
2649                103963
2650            } else {
2651                0
2652            },
2653        );
2654        assert_eq!(r.get_packet_send_time(now), now);
2655        assert_eq!(r.cwnd(), 12000);
2656        assert_eq!(r.cwnd_available(), 8400);
2657
2658        // Wait 1 rtt for ACK.
2659        now += rtt;
2660
2661        let mut acked = RangeSet::default();
2662        acked.insert(0..3);
2663
2664        assert_eq!(
2665            r.on_ack_received(
2666                &acked,
2667                10,
2668                packet::Epoch::Application,
2669                HandshakeStatus::default(),
2670                now,
2671                None,
2672                "",
2673            )
2674            .unwrap(),
2675            OnAckReceivedOutcome {
2676                lost_packets: 0,
2677                lost_bytes: 0,
2678                acked_bytes: 3600,
2679                spurious_losses: 0,
2680            }
2681        );
2682
2683        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2684        assert_eq!(r.bytes_in_flight(), 0);
2685        assert_eq!(r.bytes_in_flight_duration(), rtt);
2686        assert_eq!(r.rtt(), rtt);
2687
2688        // Pacing rate is recalculated based on initial cwnd when the
2689        // first RTT estimate is available.
2690        assert_eq!(
2691            r.pacing_rate(),
2692            if cc_algorithm_name == "bbr2_gcongestion" {
2693                120000
2694            } else {
2695                0
2696            },
2697        );
2698
2699        // Send some no "in_flight" packets
2700        for iter in 3..1000 {
2701            let mut p = test_utils::helper_packet_sent(next_packet, now, 1200);
2702            // `in_flight = false` marks packets as if they only contained ACK
2703            // frames.
2704            p.in_flight = false;
2705            next_packet += 1;
2706            r.on_packet_sent(
2707                p,
2708                packet::Epoch::Application,
2709                HandshakeStatus::default(),
2710                now,
2711                "",
2712            );
2713
2714            now += rtt;
2715
2716            let mut acked = RangeSet::default();
2717            acked.insert(iter..(iter + 1));
2718
2719            assert_eq!(
2720                r.on_ack_received(
2721                    &acked,
2722                    10,
2723                    packet::Epoch::Application,
2724                    HandshakeStatus::default(),
2725                    now,
2726                    None,
2727                    "",
2728                )
2729                .unwrap(),
2730                OnAckReceivedOutcome {
2731                    lost_packets: 0,
2732                    lost_bytes: 0,
2733                    acked_bytes: 0,
2734                    spurious_losses: 0,
2735                }
2736            );
2737
2738            // Verify that connection has not exited startup.
2739            assert_eq!(r.startup_exit(), None, "{iter}");
2740
2741            // Unchanged metrics.
2742            assert_eq!(
2743                r.sent_packets_len(packet::Epoch::Application),
2744                0,
2745                "{iter}"
2746            );
2747            assert_eq!(r.bytes_in_flight(), 0, "{iter}");
2748            assert_eq!(r.bytes_in_flight_duration(), rtt, "{iter}");
2749            assert_eq!(
2750                r.pacing_rate(),
2751                if cc_algorithm_name == "bbr2_gcongestion" ||
2752                    cc_algorithm_name == "bbr2"
2753                {
2754                    120000
2755                } else {
2756                    0
2757                },
2758                "{iter}"
2759            );
2760        }
2761    }
2762    #[rstest]
2763    fn pto_overflow_reproduction(
2764        #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2765    ) {
2766        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2767        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2768        let mut r = Recovery::new(&cfg);
2769        let now = Instant::now();
2770
2771        // Scenario: Handshake not completed
2772        let handshake_status = HandshakeStatus {
2773            has_handshake_keys: true,
2774            peer_verified_address: true,
2775            completed: false,
2776        };
2777
2778        // 1. Send Initial packet to arm the timer
2779        let p_initial = Sent {
2780            pkt_num: 0,
2781            frames: smallvec::smallvec![],
2782            time_sent: now,
2783            time_acked: None,
2784            time_lost: None,
2785            size: 1000,
2786            ack_eliciting: true,
2787            in_flight: true,
2788            delivered: 0,
2789            delivered_time: now,
2790            first_sent_time: now,
2791            is_app_limited: false,
2792            tx_in_flight: 0,
2793            lost: 0,
2794            has_data: false,
2795            is_pmtud_probe: false,
2796        };
2797        r.on_packet_sent(
2798            p_initial,
2799            packet::Epoch::Initial,
2800            handshake_status,
2801            now,
2802            "",
2803        );
2804
2805        // The timer should now be set for the Initial packet.
2806        assert!(r.loss_detection_timer().is_some());
2807
2808        // 2. Send Application packet (0-RTT)
2809        let p_app = Sent {
2810            pkt_num: 0, // Application space has its own packet numbers
2811            frames: smallvec::smallvec![],
2812            time_sent: now,
2813            time_acked: None,
2814            time_lost: None,
2815            size: 1000,
2816            ack_eliciting: true,
2817            in_flight: true,
2818            delivered: 0,
2819            delivered_time: now,
2820            first_sent_time: now,
2821            is_app_limited: false,
2822            tx_in_flight: 0,
2823
2824            lost: 0,
2825            has_data: true,
2826            is_pmtud_probe: false,
2827        };
2828        r.on_packet_sent(
2829            p_app,
2830            packet::Epoch::Application,
2831            handshake_status,
2832            now,
2833            "",
2834        );
2835
2836        // 3. Acknowledge the Initial packet.
2837        // This empties the Initial space, but Application space still has data in
2838        // flight.
2839        let mut ranges = RangeSet::default();
2840        ranges.insert(0..1);
2841        r.on_ack_received(
2842            &ranges,
2843            0,
2844            packet::Epoch::Initial,
2845            handshake_status,
2846            now,
2847            None,
2848            "",
2849        )
2850        .unwrap();
2851
2852        // The timer should be cleared at this point.
2853        // Although there is Application data in flight, the handshake is not
2854        // confirmed, so it cannot be used to arm the PTO timer. Since there are
2855        // no packets in flight in Initial or Handshake spaces either, no timer
2856        // should be set.
2857        assert!(r.loss_detection_timer().is_none());
2858    }
2859
2860    // Test that consecutive PTOs don't add duplicate frames to lost_frames.
2861    // This validates the fix: `if epoch.lost_frames.is_empty()` guard.
2862    #[rstest]
2863    fn pto_does_not_duplicate_frames_on_consecutive_timeouts(
2864        #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2865    ) {
2866        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2867        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2868
2869        let mut r = Recovery::new(&cfg);
2870        let mut now = Instant::now();
2871
2872        // Send a packet with a STREAM frame.
2873        let frames = smallvec![frame::Frame::Stream {
2874            stream_id: 4,
2875            data: RangeBuf::from(b"test", 0, false),
2876        },];
2877
2878        let p = Sent {
2879            pkt_num: 0,
2880            frames: frames.clone(),
2881            time_sent: now,
2882            time_acked: None,
2883            time_lost: None,
2884            size: 1000,
2885            ack_eliciting: true,
2886            in_flight: true,
2887            delivered: 0,
2888            delivered_time: now,
2889            first_sent_time: now,
2890            is_app_limited: false,
2891            tx_in_flight: 0,
2892            lost: 0,
2893            has_data: true,
2894            is_pmtud_probe: false,
2895        };
2896
2897        r.on_packet_sent(
2898            p,
2899            packet::Epoch::Application,
2900            HandshakeStatus::default(),
2901            now,
2902            "",
2903        );
2904
2905        // Verify initial state
2906        assert_eq!(r.lost_frames_count(packet::Epoch::Application), 0);
2907        assert_eq!(r.lost_count(), 0);
2908
2909        // First PTO - should add frames when lost_frames is empty.
2910        now = r.loss_detection_timer().unwrap();
2911        r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
2912
2913        assert_eq!(r.pto_count(), 1);
2914        let frames_after_first_pto =
2915            r.lost_frames_count(packet::Epoch::Application);
2916        assert_eq!(
2917            frames_after_first_pto, 1,
2918            "First PTO should add exactly 1 frame"
2919        );
2920        assert_eq!(
2921            r.lost_count(),
2922            0,
2923            "PTO doesn't declare packets lost (no CC impact)"
2924        );
2925
2926        // Second PTO while lost_frames is still populated.
2927        // WITHOUT the fix: would add duplicate frame (count becomes 2).
2928        // WITH the fix: skips adding (count stays 1).
2929        now = r.loss_detection_timer().unwrap();
2930        r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
2931
2932        assert_eq!(r.pto_count(), 2);
2933        let frames_after_second_pto =
2934            r.lost_frames_count(packet::Epoch::Application);
2935        assert_eq!(
2936            frames_after_second_pto, frames_after_first_pto,
2937            "Second PTO must NOT add duplicate frames (fix: `if \
2938             lost_frames.is_empty()`)"
2939        );
2940
2941        // Third PTO for extra validation
2942        now = r.loss_detection_timer().unwrap();
2943        r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
2944
2945        assert_eq!(r.pto_count(), 3);
2946        let frames_after_third_pto =
2947            r.lost_frames_count(packet::Epoch::Application);
2948        assert_eq!(
2949            frames_after_third_pto, frames_after_first_pto,
2950            "Third PTO must NOT add duplicate frames"
2951        );
2952
2953        // Verify packets are still tracked (not removed)
2954        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
2955        // Verify lost_count never increased (PTO doesn't trigger CC)
2956        assert_eq!(r.lost_count(), 0);
2957    }
2958
2959    // Test that send_on_path after PTO timeout properly sends retransmissions
2960    // and doesn't mark packets as lost (lost_count should remain 0).
2961    #[rstest]
2962    fn pto_send_on_path_retransmits_without_loss(
2963        #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2964    ) {
2965        use crate::test_utils;
2966
2967        let mut pipe = test_utils::Pipe::new(cc_algorithm_name).unwrap();
2968
2969        // Complete handshake
2970        assert_eq!(pipe.handshake(), Ok(()));
2971
2972        // Client sends stream data
2973        assert_eq!(pipe.client.stream_send(4, b"hello", false), Ok(5));
2974
2975        let mut buf = [0; 65535];
2976
2977        // Send the packet but drop it (don't deliver to server)
2978        let (len1, _) = pipe.client.send(&mut buf).unwrap();
2979        assert!(len1 > 0);
2980
2981        // Verify lost_count is 0 (no losses yet)
2982        let initial_lost_count = pipe
2983            .client
2984            .paths
2985            .get_active()
2986            .unwrap()
2987            .recovery
2988            .lost_count();
2989        assert_eq!(initial_lost_count, 0, "No packets should be lost initially");
2990
2991        // Verify frames are not yet in lost_frames
2992        let initial_lost_frames = pipe
2993            .client
2994            .paths
2995            .get_active()
2996            .unwrap()
2997            .recovery
2998            .lost_frames_count(packet::Epoch::Application);
2999        assert_eq!(
3000            initial_lost_frames, 0,
3001            "No frames should be in lost_frames initially"
3002        );
3003
3004        // Wait for PTO timeout
3005        let timer = pipe.client.timeout().unwrap();
3006        std::thread::sleep(timer + Duration::from_millis(1));
3007
3008        // Trigger PTO via on_timeout()
3009        pipe.client.on_timeout();
3010
3011        // After PTO, frames should be in lost_frames for retransmission
3012        let lost_frames_after_pto = pipe
3013            .client
3014            .paths
3015            .get_active()
3016            .unwrap()
3017            .recovery
3018            .lost_frames_count(packet::Epoch::Application);
3019        assert!(
3020            lost_frames_after_pto > 0,
3021            "PTO should add frames to lost_frames for retransmission"
3022        );
3023
3024        // But lost_count should still be 0 (PTO doesn't declare packets lost)
3025        let lost_count_after_pto = pipe
3026            .client
3027            .paths
3028            .get_active()
3029            .unwrap()
3030            .recovery
3031            .lost_count();
3032        assert_eq!(
3033            lost_count_after_pto, 0,
3034            "PTO should not increment lost_count"
3035        );
3036
3037        // Now send the retransmission via send_on_path
3038        let (len2, _) = pipe.client.send(&mut buf).unwrap();
3039        assert!(len2 > 0, "Should send PTO probe packet");
3040
3041        // After sending, lost_count should still be 0
3042        let lost_count_after_send = pipe
3043            .client
3044            .paths
3045            .get_active()
3046            .unwrap()
3047            .recovery
3048            .lost_count();
3049        assert_eq!(
3050            lost_count_after_send, 0,
3051            "Sending PTO probe should not increment lost_count"
3052        );
3053
3054        // Deliver the retransmission to server
3055        assert_eq!(pipe.server_recv(&mut buf[..len2]), Ok(len2));
3056
3057        // Server should receive the stream data
3058        let mut recv_buf = [0; 100];
3059        assert_eq!(pipe.server.stream_recv(4, &mut recv_buf), Ok((5, false)));
3060        assert_eq!(&recv_buf[..5], b"hello");
3061
3062        // Final verification: lost_count on client should still be 0
3063        let final_lost_count = pipe
3064            .client
3065            .paths
3066            .get_active()
3067            .unwrap()
3068            .recovery
3069            .lost_count();
3070        assert_eq!(
3071            final_lost_count, 0,
3072            "No packets should be marked as lost - PTO only retransmits"
3073        );
3074    }
3075}
3076
3077mod bandwidth;
3078mod bytes_in_flight;
3079mod congestion;
3080mod gcongestion;
3081mod rtt;