1use 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#[cfg(feature = "internal")]
50pub use gcongestion::BbrRttJumpDetector;
51
52const INITIAL_PACKET_THRESHOLD: u64 = 3;
54
55const MAX_PACKET_THRESHOLD: u64 = 20;
56
57const INITIAL_TIME_THRESHOLD: f64 = 9.0 / 8.0;
61
62const PACKET_REORDER_TIME_THRESHOLD: f64 = 5.0 / 4.0;
74
75const INITIAL_TIME_THRESHOLD_OVERHEAD: f64 = 1.0 / 8.0;
82const TIME_THRESHOLD_OVERHEAD_MULTIPLIER: f64 = 2.0;
86
87const GRANULARITY: Duration = Duration::from_millis(1);
88
89const MAX_PTO_PROBES_COUNT: usize = 2;
90
91const MINIMUM_WINDOW_PACKETS: usize = 2;
92
93const LOSS_REDUCTION_FACTOR: f64 = 0.5;
94
95pub(super) const MAX_OUTSTANDING_NON_ACK_ELICITING: usize = 24;
98
99#[derive(Default)]
100struct LossDetectionTimer {
101 time: Option<Instant>,
102}
103
104impl LossDetectionTimer {
105 fn update(&mut self, timeout: Instant) {
106 self.time = Some(timeout);
107 }
108
109 fn clear(&mut self) {
110 self.time = None;
111 }
112}
113
114impl std::fmt::Debug for LossDetectionTimer {
115 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
116 match self.time {
117 Some(v) => {
118 let now = Instant::now();
119 if v > now {
120 let d = v.duration_since(now);
121 write!(f, "{d:?}")
122 } else {
123 write!(f, "exp")
124 }
125 },
126 None => write!(f, "none"),
127 }
128 }
129}
130
131#[derive(Clone, Copy, PartialEq)]
132pub struct RecoveryConfig {
133 pub initial_rtt: Duration,
134 pub max_send_udp_payload_size: usize,
135 pub max_ack_delay: Duration,
136 pub cc_algorithm: CongestionControlAlgorithm,
137 pub custom_bbr_params: Option<BbrParams>,
138 pub hystart: bool,
139 pub pacing: bool,
140 pub max_pacing_rate: Option<u64>,
141 pub initial_congestion_window_packets: usize,
142 pub unchecked_congestion_window: usize,
144 pub enable_relaxed_loss_threshold: bool,
145 pub enable_cubic_idle_restart_fix: bool,
146}
147
148impl RecoveryConfig {
149 pub fn from_config(config: &Config) -> Self {
150 Self {
151 initial_rtt: config.initial_rtt,
152 max_send_udp_payload_size: config.max_send_udp_payload_size,
153 max_ack_delay: Duration::ZERO,
154 cc_algorithm: config.cc_algorithm,
155 custom_bbr_params: config.custom_bbr_params,
156 hystart: config.hystart,
157 pacing: config.pacing,
158 max_pacing_rate: config.max_pacing_rate,
159 initial_congestion_window_packets: config
160 .initial_congestion_window_packets,
161 unchecked_congestion_window: config.unchecked_congestion_window,
162 enable_relaxed_loss_threshold: config.enable_relaxed_loss_threshold,
163 enable_cubic_idle_restart_fix: config.enable_cubic_idle_restart_fix,
164 }
165 }
166
167 pub(crate) fn update_max_send_udp_payload_size(&mut self, peer_max: usize) {
168 let new_max = peer_max.max(crate::MAX_SEND_UDP_PAYLOAD_SIZE);
172 self.max_send_udp_payload_size =
173 self.max_send_udp_payload_size.min(new_max);
174 }
175}
176
177#[enum_dispatch::enum_dispatch(RecoveryOps)]
178#[allow(clippy::large_enum_variant)]
179#[derive(Debug)]
180pub(crate) enum Recovery {
181 Legacy(LegacyRecovery),
182 GCongestion(GRecovery),
183}
184
185#[derive(Debug, Default, PartialEq)]
186pub struct OnAckReceivedOutcome {
187 pub lost_packets: usize,
188 pub lost_bytes: usize,
189 pub acked_bytes: usize,
190 pub spurious_losses: usize,
191}
192
193#[derive(Debug, Default)]
194pub struct OnLossDetectionTimeoutOutcome {
195 pub lost_packets: usize,
196 pub lost_bytes: usize,
197}
198
199#[enum_dispatch::enum_dispatch]
200pub trait RecoveryOps {
202 fn lost_count(&self) -> usize;
203 fn bytes_lost(&self) -> u64;
204
205 fn should_elicit_ack(&self, epoch: packet::Epoch) -> bool;
208
209 fn next_acked_frame(&mut self, epoch: packet::Epoch) -> Option<frame::Frame>;
210
211 fn next_lost_frame(&mut self, epoch: packet::Epoch) -> Option<frame::Frame>;
212
213 fn get_largest_acked_on_epoch(&self, epoch: packet::Epoch) -> Option<u64>;
214 fn has_lost_frames(&self, epoch: packet::Epoch) -> bool;
215 fn loss_probes(&self, epoch: packet::Epoch) -> usize;
216 #[cfg(test)]
217 fn inc_loss_probes(&mut self, epoch: packet::Epoch);
218 #[cfg(test)]
219 fn lost_frames_count(&self, epoch: packet::Epoch) -> usize;
220
221 fn ping_sent(&mut self, epoch: packet::Epoch);
222
223 fn on_packet_sent(
224 &mut self, pkt: Sent, epoch: packet::Epoch,
225 handshake_status: HandshakeStatus, now: Instant, trace_id: &str,
226 );
227 fn get_packet_send_time(&self, now: Instant) -> Instant;
228
229 #[allow(clippy::too_many_arguments)]
230 fn on_ack_received(
231 &mut self, ranges: &RangeSet, ack_delay: u64, epoch: packet::Epoch,
232 handshake_status: HandshakeStatus, now: Instant, skip_pn: Option<u64>,
233 trace_id: &str,
234 ) -> Result<OnAckReceivedOutcome>;
235
236 fn on_loss_detection_timeout(
237 &mut self, handshake_status: HandshakeStatus, now: Instant,
238 trace_id: &str,
239 ) -> OnLossDetectionTimeoutOutcome;
240 fn on_pkt_num_space_discarded(
241 &mut self, epoch: packet::Epoch, handshake_status: HandshakeStatus,
242 now: Instant,
243 );
244 fn on_path_change(
245 &mut self, epoch: packet::Epoch, now: Instant, _trace_id: &str,
246 ) -> (usize, usize);
247 fn loss_detection_timer(&self) -> Option<Instant>;
248 fn cwnd(&self) -> usize;
249 fn cwnd_available(&self) -> usize;
250 fn rtt(&self) -> Duration;
251
252 fn min_rtt(&self) -> Option<Duration>;
253
254 fn max_rtt(&self) -> Option<Duration>;
255
256 fn rttvar(&self) -> Duration;
257
258 fn pto(&self) -> Duration;
259
260 fn delivery_rate(&self) -> Bandwidth;
262
263 fn max_bandwidth(&self) -> Option<Bandwidth>;
265
266 fn rtt_persistent_jump_count(&self) -> u64;
268
269 fn startup_exit(&self) -> Option<StartupExit>;
271
272 fn max_datagram_size(&self) -> usize;
273
274 fn pmtud_update_max_datagram_size(&mut self, new_max_datagram_size: usize);
275
276 fn update_max_datagram_size(&mut self, new_max_datagram_size: usize);
277
278 fn on_app_limited(&mut self);
279
280 #[cfg(test)]
283 fn largest_sent_pkt_num_on_path(&self, epoch: packet::Epoch) -> Option<u64>;
284
285 #[cfg(any(test, feature = "qlog"))]
286 fn app_limited(&self) -> bool;
287
288 fn sent_packets_len(&self, epoch: packet::Epoch) -> usize;
289
290 fn bytes_in_flight(&self) -> usize;
291
292 fn bytes_in_flight_duration(&self) -> Duration;
293
294 #[cfg(test)]
295 fn in_flight_count(&self, epoch: packet::Epoch) -> usize;
296
297 #[cfg(test)]
298 fn pacing_rate(&self) -> u64;
299
300 #[cfg(test)]
301 fn pto_count(&self) -> u32;
302
303 #[cfg(test)]
306 fn pkt_thresh(&self) -> Option<u64>;
307
308 #[cfg(test)]
309 fn time_thresh(&self) -> f64;
310
311 #[cfg(test)]
312 fn lost_spurious_count(&self) -> usize;
313
314 #[cfg(test)]
315 fn detect_lost_packets_for_test(
316 &mut self, epoch: packet::Epoch, now: Instant,
317 ) -> (usize, usize);
318
319 fn update_app_limited(&mut self, v: bool);
320
321 fn delivery_rate_update_app_limited(&mut self, v: bool);
322
323 fn update_max_ack_delay(&mut self, max_ack_delay: Duration);
324
325 #[cfg(feature = "qlog")]
326 fn state_str(&self, now: Instant) -> &'static str;
327
328 #[cfg(feature = "qlog")]
329 fn get_updated_qlog_event_data(&mut self) -> Option<EventData>;
330
331 #[cfg(feature = "qlog")]
332 fn get_updated_qlog_cc_state(&mut self, now: Instant)
333 -> Option<&'static str>;
334
335 fn send_quantum(&self) -> usize;
336
337 fn get_next_release_time(&self) -> ReleaseDecision;
338
339 fn gcongestion_enabled(&self) -> bool;
340}
341
342impl Recovery {
343 pub fn new_with_config(recovery_config: &RecoveryConfig) -> Self {
344 let grecovery = GRecovery::new(recovery_config);
345 if let Some(grecovery) = grecovery {
346 Recovery::from(grecovery)
347 } else {
348 Recovery::from(LegacyRecovery::new_with_config(recovery_config))
349 }
350 }
351
352 #[cfg(feature = "qlog")]
353 pub fn maybe_qlog(
354 &mut self, qlog: &mut qlog::streamer::QlogStreamer, now: Instant,
355 ) {
356 if let Some(ev_data) = self.get_updated_qlog_event_data() {
357 qlog.add_event_data_with_instant(ev_data, now).ok();
358 }
359
360 if let Some(cc_state) = self.get_updated_qlog_cc_state(now) {
361 let ev_data = EventData::QuicCongestionStateUpdated(
362 qlog::events::quic::CongestionStateUpdated {
363 old: None,
364 new: cc_state.to_string(),
365 trigger: None,
366 },
367 );
368
369 qlog.add_event_data_with_instant(ev_data, now).ok();
370 }
371 }
372
373 #[cfg(test)]
374 pub fn new(config: &Config) -> Self {
375 Self::new_with_config(&RecoveryConfig::from_config(config))
376 }
377}
378
379#[derive(Debug, Copy, Clone, PartialEq, Eq)]
388#[repr(C)]
389pub enum CongestionControlAlgorithm {
390 Reno = 0,
392 CUBIC = 1,
394 Bbr2Gcongestion = 4,
397 #[cfg(feature = "congestion_window_unchecked_available")]
414 #[cfg_attr(
415 docsrs,
416 doc(cfg(feature = "congestion_window_unchecked_available"))
417 )]
418 CongestionWindowUnchecked = 5,
419}
420
421impl FromStr for CongestionControlAlgorithm {
422 type Err = crate::Error;
423
424 fn from_str(name: &str) -> std::result::Result<Self, Self::Err> {
428 match name {
429 "reno" => Ok(CongestionControlAlgorithm::Reno),
430 "cubic" => Ok(CongestionControlAlgorithm::CUBIC),
431 "bbr" => Ok(CongestionControlAlgorithm::Bbr2Gcongestion),
432 "bbr2" => Ok(CongestionControlAlgorithm::Bbr2Gcongestion),
433 "bbr2_gcongestion" => Ok(CongestionControlAlgorithm::Bbr2Gcongestion),
434 #[cfg(feature = "congestion_window_unchecked_available")]
435 "congestion_window_unchecked" =>
436 Ok(CongestionControlAlgorithm::CongestionWindowUnchecked),
437 _ => Err(crate::Error::CongestionControl),
438 }
439 }
440}
441
442#[derive(Clone)]
443pub struct Sent {
444 pub pkt_num: u64,
445
446 pub frames: SmallVec<[frame::Frame; 1]>,
447
448 pub time_sent: Instant,
449
450 pub time_acked: Option<Instant>,
451
452 pub time_lost: Option<Instant>,
453
454 pub size: usize,
455
456 pub ack_eliciting: bool,
457
458 pub in_flight: bool,
459
460 pub delivered: usize,
461
462 pub delivered_time: Instant,
463
464 pub first_sent_time: Instant,
465
466 pub is_app_limited: bool,
467
468 pub tx_in_flight: usize,
469
470 pub lost: u64,
471
472 pub has_data: bool,
473
474 pub is_pmtud_probe: bool,
475}
476
477impl std::fmt::Debug for Sent {
478 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
479 write!(f, "pkt_num={:?} ", self.pkt_num)?;
480 write!(f, "pkt_sent_time={:?} ", self.time_sent)?;
481 write!(f, "pkt_size={:?} ", self.size)?;
482 write!(f, "delivered={:?} ", self.delivered)?;
483 write!(f, "delivered_time={:?} ", self.delivered_time)?;
484 write!(f, "first_sent_time={:?} ", self.first_sent_time)?;
485 write!(f, "is_app_limited={} ", self.is_app_limited)?;
486 write!(f, "tx_in_flight={} ", self.tx_in_flight)?;
487 write!(f, "lost={} ", self.lost)?;
488 write!(f, "has_data={} ", self.has_data)?;
489 write!(f, "is_pmtud_probe={}", self.is_pmtud_probe)?;
490
491 Ok(())
492 }
493}
494
495#[derive(Clone, Copy, Debug)]
496pub struct HandshakeStatus {
497 pub has_handshake_keys: bool,
498
499 pub peer_verified_address: bool,
500
501 pub completed: bool,
502}
503
504#[cfg(test)]
505impl Default for HandshakeStatus {
506 fn default() -> HandshakeStatus {
507 HandshakeStatus {
508 has_handshake_keys: true,
509
510 peer_verified_address: true,
511
512 completed: true,
513 }
514 }
515}
516
517#[derive(Default)]
522#[cfg(feature = "qlog")]
523struct QlogMetrics {
524 min_rtt: Duration,
525 smoothed_rtt: Duration,
526 latest_rtt: Duration,
527 rttvar: Duration,
528 cwnd: u64,
529 bytes_in_flight: u64,
530 ssthresh: Option<u64>,
531 pacing_rate: Option<u64>,
532 delivery_rate: Option<u64>,
533 send_rate: Option<u64>,
534 ack_rate: Option<u64>,
535 lost_packets: Option<u64>,
536 lost_bytes: Option<u64>,
537 pto_count: Option<u32>,
538 app_limited: Option<bool>,
539}
540
541#[cfg(feature = "qlog")]
542trait CustomCfQlogField {
543 fn name(&self) -> &'static str;
544 fn as_json_value(&self) -> serde_json::Value;
545}
546
547#[cfg(feature = "qlog")]
548#[serde_with::skip_serializing_none]
549#[derive(Serialize)]
550struct TotalAndDelta {
551 total: Option<u64>,
552 delta: Option<u64>,
553}
554
555#[cfg(feature = "qlog")]
556struct CustomQlogField<T> {
557 name: &'static str,
558 value: T,
559}
560
561#[cfg(feature = "qlog")]
562impl<T> CustomQlogField<T> {
563 fn new(name: &'static str, value: T) -> Self {
564 Self { name, value }
565 }
566}
567
568#[cfg(feature = "qlog")]
569impl<T: Serialize> CustomCfQlogField for CustomQlogField<T> {
570 fn name(&self) -> &'static str {
571 self.name
572 }
573
574 fn as_json_value(&self) -> serde_json::Value {
575 serde_json::json!(&self.value)
576 }
577}
578
579#[cfg(feature = "qlog")]
580struct CfExData(qlog::events::ExData);
581
582#[cfg(feature = "qlog")]
583impl CfExData {
584 fn new() -> Self {
585 Self(qlog::events::ExData::new())
586 }
587
588 fn insert<T: Serialize>(&mut self, name: &'static str, value: T) {
589 let field = CustomQlogField::new(name, value);
590 self.0
591 .insert(field.name().to_string(), field.as_json_value());
592 }
593
594 fn into_inner(self) -> qlog::events::ExData {
595 self.0
596 }
597}
598
599#[cfg(feature = "qlog")]
600impl QlogMetrics {
601 fn maybe_update(&mut self, latest: Self) -> Option<EventData> {
607 let mut emit_event = false;
608
609 let new_min_rtt = if self.min_rtt != latest.min_rtt {
610 self.min_rtt = latest.min_rtt;
611 emit_event = true;
612 Some(latest.min_rtt.as_secs_f32() * 1000.0)
613 } else {
614 None
615 };
616
617 let new_smoothed_rtt = if self.smoothed_rtt != latest.smoothed_rtt {
618 self.smoothed_rtt = latest.smoothed_rtt;
619 emit_event = true;
620 Some(latest.smoothed_rtt.as_secs_f32() * 1000.0)
621 } else {
622 None
623 };
624
625 let new_latest_rtt = if self.latest_rtt != latest.latest_rtt {
626 self.latest_rtt = latest.latest_rtt;
627 emit_event = true;
628 Some(latest.latest_rtt.as_secs_f32() * 1000.0)
629 } else {
630 None
631 };
632
633 let new_rttvar = if self.rttvar != latest.rttvar {
634 self.rttvar = latest.rttvar;
635 emit_event = true;
636 Some(latest.rttvar.as_secs_f32() * 1000.0)
637 } else {
638 None
639 };
640
641 let new_cwnd = if self.cwnd != latest.cwnd {
642 self.cwnd = latest.cwnd;
643 emit_event = true;
644 Some(latest.cwnd)
645 } else {
646 None
647 };
648
649 let new_bytes_in_flight =
650 if self.bytes_in_flight != latest.bytes_in_flight {
651 self.bytes_in_flight = latest.bytes_in_flight;
652 emit_event = true;
653 Some(latest.bytes_in_flight)
654 } else {
655 None
656 };
657
658 let new_ssthresh = if self.ssthresh != latest.ssthresh {
659 self.ssthresh = latest.ssthresh;
660 emit_event = true;
661 latest.ssthresh
662 } else {
663 None
664 };
665
666 let new_pacing_rate = if self.pacing_rate != latest.pacing_rate {
667 self.pacing_rate = latest.pacing_rate;
668 emit_event = true;
669 latest.pacing_rate
670 } else {
671 None
672 };
673
674 let new_pto_count =
675 if latest.pto_count.is_some() && self.pto_count != latest.pto_count {
676 self.pto_count = latest.pto_count;
677 emit_event = true;
678 latest.pto_count.map(|v| v as u16)
679 } else {
680 None
681 };
682
683 let mut ex_data = CfExData::new();
685 if self.app_limited != latest.app_limited {
686 if let Some(app_limited) = latest.app_limited {
687 self.app_limited = latest.app_limited;
688 emit_event = true;
689 ex_data.insert("cf_app_limited", app_limited);
690 }
691 }
692 if self.delivery_rate != latest.delivery_rate {
693 if let Some(rate) = latest.delivery_rate {
694 self.delivery_rate = latest.delivery_rate;
695 emit_event = true;
696 ex_data.insert("cf_delivery_rate", rate);
697 }
698 }
699 if self.send_rate != latest.send_rate {
700 if let Some(rate) = latest.send_rate {
701 self.send_rate = latest.send_rate;
702 emit_event = true;
703 ex_data.insert("cf_send_rate", rate);
704 }
705 }
706 if self.ack_rate != latest.ack_rate {
707 if let Some(rate) = latest.ack_rate {
708 self.ack_rate = latest.ack_rate;
709 emit_event = true;
710 ex_data.insert("cf_ack_rate", rate);
711 }
712 }
713
714 if self.lost_packets != latest.lost_packets {
715 if let Some(val) = latest.lost_packets {
716 emit_event = true;
717 ex_data.insert("cf_lost_packets", TotalAndDelta {
718 total: latest.lost_packets,
719 delta: Some(val - self.lost_packets.unwrap_or(0)),
720 });
721 self.lost_packets = latest.lost_packets;
722 }
723 }
724 if self.lost_bytes != latest.lost_bytes {
725 if let Some(val) = latest.lost_bytes {
726 emit_event = true;
727 ex_data.insert("cf_lost_bytes", TotalAndDelta {
728 total: latest.lost_bytes,
729 delta: Some(val - self.lost_bytes.unwrap_or(0)),
730 });
731 self.lost_bytes = latest.lost_bytes;
732 }
733 }
734
735 if emit_event {
736 return Some(EventData::QuicMetricsUpdated(
737 qlog::events::quic::RecoveryMetricsUpdated {
738 min_rtt: new_min_rtt,
739 smoothed_rtt: new_smoothed_rtt,
740 latest_rtt: new_latest_rtt,
741 rtt_variance: new_rttvar,
742 congestion_window: new_cwnd,
743 bytes_in_flight: new_bytes_in_flight,
744 ssthresh: new_ssthresh,
745 pacing_rate: new_pacing_rate,
746 pto_count: new_pto_count,
747 ex_data: ex_data.into_inner(),
748 ..Default::default()
749 },
750 ));
751 }
752
753 None
754 }
755}
756
757#[derive(Debug, Clone, Copy, PartialEq, Eq)]
759pub enum ReleaseTime {
760 Immediate,
761 At(Instant),
762}
763
764#[derive(Clone, Copy, Debug, PartialEq, Eq)]
766pub struct ReleaseDecision {
767 time: ReleaseTime,
768 allow_burst: bool,
769}
770
771impl ReleaseTime {
772 fn inc(&mut self, delay: Duration) {
774 match self {
775 ReleaseTime::Immediate => {},
776 ReleaseTime::At(time) => *time += delay,
777 }
778 }
779
780 fn set_max(&mut self, other: Instant) {
782 match self {
783 ReleaseTime::Immediate => *self = ReleaseTime::At(other),
784 ReleaseTime::At(time) => *self = ReleaseTime::At(other.max(*time)),
785 }
786 }
787}
788
789impl ReleaseDecision {
790 pub(crate) const EQUAL_THRESHOLD: Duration = Duration::from_micros(50);
791
792 #[inline]
795 pub fn time(&self, now: Instant) -> Option<Instant> {
796 match self.time {
797 ReleaseTime::Immediate => None,
798 ReleaseTime::At(other) => other.gt(&now).then_some(other),
799 }
800 }
801
802 #[inline]
804 pub fn can_burst(&self) -> bool {
805 self.allow_burst
806 }
807
808 #[inline]
810 pub fn time_eq(&self, other: &Self, now: Instant) -> bool {
811 let delta = match (self.time(now), other.time(now)) {
812 (None, None) => Duration::ZERO,
813 (Some(t), None) | (None, Some(t)) => t.duration_since(now),
814 (Some(t1), Some(t2)) if t1 < t2 => t2.duration_since(t1),
815 (Some(t1), Some(t2)) => t1.duration_since(t2),
816 };
817
818 delta <= Self::EQUAL_THRESHOLD
819 }
820}
821
822#[derive(Default, Debug)]
824pub struct RecoveryStats {
825 startup_exit: Option<StartupExit>,
826}
827
828impl RecoveryStats {
829 pub fn set_startup_exit(&mut self, startup_exit: StartupExit) {
831 if self.startup_exit.is_none() {
832 self.startup_exit = Some(startup_exit);
833 }
834 }
835}
836
837#[derive(Debug, Clone, Copy, PartialEq)]
839pub struct StartupExit {
840 pub cwnd: usize,
842
843 pub bandwidth: Option<u64>,
845
846 pub reason: StartupExitReason,
848}
849
850impl StartupExit {
851 fn new(
852 cwnd: usize, bandwidth: Option<Bandwidth>, reason: StartupExitReason,
853 ) -> Self {
854 let bandwidth = bandwidth.map(Bandwidth::to_bytes_per_second);
855 Self {
856 cwnd,
857 bandwidth,
858 reason,
859 }
860 }
861}
862
863#[derive(Debug, Clone, Copy, PartialEq)]
865pub enum StartupExitReason {
866 Loss,
868
869 BandwidthPlateau,
871
872 PersistentQueue,
874
875 ConservativeSlowStartRounds,
877}
878
879#[cfg(test)]
880mod tests {
881 use super::*;
882 use crate::packet;
883 use crate::range_buf::RangeBuf;
884 use crate::test_utils;
885 use crate::CongestionControlAlgorithm;
886 #[cfg(feature = "congestion_window_unchecked_available")]
887 use crate::DEFAULT_INITIAL_CONGESTION_WINDOW_PACKETS;
888 use crate::DEFAULT_INITIAL_RTT;
889 #[cfg(feature = "congestion_window_unchecked_available")]
890 use crate::MAX_SEND_UDP_PAYLOAD_SIZE;
891 use rstest::rstest;
892 use smallvec::smallvec;
893 use std::str::FromStr;
894
895 fn recovery_for_alg(algo: CongestionControlAlgorithm) -> Recovery {
896 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
897 cfg.set_cc_algorithm(algo);
898 Recovery::new(&cfg)
899 }
900
901 #[cfg(feature = "qlog")]
902 fn app_limited_value(event: EventData) -> Option<serde_json::Value> {
903 let EventData::QuicMetricsUpdated(metrics) = event else {
904 panic!("expected recovery metrics updated event");
905 };
906
907 metrics.ex_data.get("cf_app_limited").cloned()
908 }
909
910 #[cfg(feature = "qlog")]
911 #[test]
912 fn qlog_app_limited_emits_initial_false_and_transitions() {
913 let mut metrics = QlogMetrics::default();
914
915 let event = metrics
916 .maybe_update(QlogMetrics {
917 app_limited: Some(false),
918 ..Default::default()
919 })
920 .unwrap();
921 assert_eq!(app_limited_value(event), Some(false.into()));
922
923 let event = metrics
924 .maybe_update(QlogMetrics {
925 app_limited: Some(true),
926 ..Default::default()
927 })
928 .unwrap();
929 assert_eq!(app_limited_value(event), Some(true.into()));
930
931 let event = metrics
932 .maybe_update(QlogMetrics {
933 app_limited: Some(false),
934 ..Default::default()
935 })
936 .unwrap();
937 assert_eq!(app_limited_value(event), Some(false.into()));
938 }
939
940 #[cfg(feature = "qlog")]
941 #[test]
942 fn qlog_app_limited_suppresses_unchanged_values() {
943 let mut metrics = QlogMetrics::default();
944 metrics
945 .maybe_update(QlogMetrics {
946 app_limited: Some(false),
947 ..Default::default()
948 })
949 .unwrap();
950
951 assert!(metrics
952 .maybe_update(QlogMetrics {
953 app_limited: Some(false),
954 ..Default::default()
955 })
956 .is_none());
957
958 let event = metrics
959 .maybe_update(QlogMetrics {
960 cwnd: 1,
961 app_limited: Some(false),
962 ..Default::default()
963 })
964 .unwrap();
965 assert_eq!(app_limited_value(event), None);
966 }
967
968 #[test]
969 fn lookup_cc_algo_ok() {
970 let algo = CongestionControlAlgorithm::from_str("reno").unwrap();
971 assert_eq!(algo, CongestionControlAlgorithm::Reno);
972 assert!(!recovery_for_alg(algo).gcongestion_enabled());
973
974 let algo = CongestionControlAlgorithm::from_str("cubic").unwrap();
975 assert_eq!(algo, CongestionControlAlgorithm::CUBIC);
976 assert!(!recovery_for_alg(algo).gcongestion_enabled());
977
978 let algo = CongestionControlAlgorithm::from_str("bbr").unwrap();
979 assert_eq!(algo, CongestionControlAlgorithm::Bbr2Gcongestion);
980 assert!(recovery_for_alg(algo).gcongestion_enabled());
981
982 let algo = CongestionControlAlgorithm::from_str("bbr2").unwrap();
983 assert_eq!(algo, CongestionControlAlgorithm::Bbr2Gcongestion);
984 assert!(recovery_for_alg(algo).gcongestion_enabled());
985
986 let algo =
987 CongestionControlAlgorithm::from_str("bbr2_gcongestion").unwrap();
988 assert_eq!(algo, CongestionControlAlgorithm::Bbr2Gcongestion);
989 assert!(recovery_for_alg(algo).gcongestion_enabled());
990 }
991
992 #[test]
993 fn lookup_cc_algo_bad() {
994 assert_eq!(
995 CongestionControlAlgorithm::from_str("???"),
996 Err(crate::Error::CongestionControl)
997 );
998 }
999
1000 #[cfg(feature = "congestion_window_unchecked_available")]
1001 #[test]
1002 fn congestion_window_unchecked_discards_packet_space() {
1003 let mut config = Config::new(crate::PROTOCOL_VERSION)
1004 .expect("create transport configuration");
1005 config.set_cc_algorithm(
1006 CongestionControlAlgorithm::CongestionWindowUnchecked,
1007 );
1008 let packet_size = config.max_send_udp_payload_size;
1009 let window = packet_size * MINIMUM_WINDOW_PACKETS;
1010 config.set_unchecked_congestion_window(window);
1011 let mut recovery = Recovery::new(&config);
1012 let now = Instant::now();
1013 assert_eq!(recovery.cwnd(), window);
1014 assert_eq!(recovery.cwnd_available(), window);
1015 recovery.on_packet_sent(
1016 test_utils::helper_packet_sent(0, now, packet_size),
1017 packet::Epoch::Initial,
1018 HandshakeStatus::default(),
1019 now,
1020 "",
1021 );
1022 assert_eq!(recovery.bytes_in_flight(), packet_size);
1023 assert_eq!(recovery.cwnd(), window);
1024 assert_eq!(recovery.cwnd_available(), window - packet_size);
1025 recovery.on_pkt_num_space_discarded(
1026 packet::Epoch::Initial,
1027 HandshakeStatus::default(),
1028 now + Duration::from_secs(1),
1029 );
1030 assert_eq!(recovery.bytes_in_flight(), 0);
1031 assert_eq!(recovery.cwnd(), window);
1032 assert_eq!(recovery.cwnd_available(), window);
1033 }
1034
1035 #[cfg(feature = "congestion_window_unchecked_available")]
1036 #[test]
1037 fn congestion_window_unchecked_uses_configured_capacity() {
1038 let mut config = Config::new(crate::PROTOCOL_VERSION)
1039 .expect("create transport configuration");
1040 config.set_cc_algorithm(
1041 CongestionControlAlgorithm::CongestionWindowUnchecked,
1042 );
1043 let window = config.max_send_udp_payload_size * 2;
1044 config.set_unchecked_congestion_window(window);
1045 let mut recovery = Recovery::new(&config);
1046 let now = Instant::now();
1047
1048 assert_eq!(recovery.cwnd(), window);
1049 assert_eq!(recovery.cwnd_available(), window);
1050 for packet_number in 0..2 {
1051 recovery.on_packet_sent(
1052 test_utils::helper_packet_sent(
1053 packet_number,
1054 now,
1055 config.max_send_udp_payload_size,
1056 ),
1057 packet::Epoch::Application,
1058 HandshakeStatus::default(),
1059 now,
1060 "",
1061 );
1062 }
1063 assert_eq!(recovery.cwnd(), window);
1064 assert_eq!(recovery.cwnd_available(), 0);
1065 let mut acked = RangeSet::default();
1066 acked.insert(0..2);
1067 recovery
1068 .on_ack_received(
1069 &acked,
1070 0,
1071 packet::Epoch::Application,
1072 HandshakeStatus::default(),
1073 now + Duration::from_millis(100),
1074 None,
1075 "",
1076 )
1077 .expect("acknowledge outstanding packets");
1078 assert_eq!(recovery.cwnd(), window);
1079 assert_eq!(recovery.cwnd_available(), window);
1080 }
1081
1082 #[rstest]
1083 fn unchecked_window_configuration_does_not_change_other_algorithms(
1084 #[values("reno", "cubic", "bbr2_gcongestion")] algorithm: &str,
1085 ) {
1086 let mut config = Config::new(crate::PROTOCOL_VERSION)
1087 .expect("create transport configuration");
1088 config
1089 .set_cc_algorithm_name(algorithm)
1090 .expect("select congestion control algorithm");
1091 config.set_unchecked_congestion_window(40 * 1024 * 1024);
1092 let recovery = Recovery::new(&config);
1093 assert_eq!(
1094 recovery.cwnd(),
1095 config.max_send_udp_payload_size *
1096 config.initial_congestion_window_packets,
1097 );
1098 }
1099
1100 #[cfg(feature = "congestion_window_unchecked_available")]
1101 #[rstest]
1102 fn unchecked_window_is_fixed_across_mtu_updates(
1103 #[values(
1104 usize::MAX,
1105 MAX_SEND_UDP_PAYLOAD_SIZE * DEFAULT_INITIAL_CONGESTION_WINDOW_PACKETS,
1106 MAX_SEND_UDP_PAYLOAD_SIZE * DEFAULT_INITIAL_CONGESTION_WINDOW_PACKETS + 1
1107 )]
1108 window: usize,
1109 ) {
1110 let mut config = Config::new(crate::PROTOCOL_VERSION)
1111 .expect("create transport configuration");
1112 config.set_cc_algorithm(
1113 CongestionControlAlgorithm::CongestionWindowUnchecked,
1114 );
1115 config.set_max_send_udp_payload_size(MAX_SEND_UDP_PAYLOAD_SIZE);
1116 config.set_unchecked_congestion_window(window);
1117 let mut recovery = Recovery::new(&config);
1118
1119 recovery.pmtud_update_max_datagram_size(MAX_SEND_UDP_PAYLOAD_SIZE + 200);
1120 assert_eq!(recovery.cwnd(), window);
1121 assert_eq!(
1122 recovery.max_datagram_size(),
1123 MAX_SEND_UDP_PAYLOAD_SIZE + 200
1124 );
1125 recovery.update_max_datagram_size(MAX_SEND_UDP_PAYLOAD_SIZE);
1126 assert_eq!(recovery.cwnd(), window);
1127 assert_eq!(recovery.max_datagram_size(), MAX_SEND_UDP_PAYLOAD_SIZE);
1128 }
1129
1130 #[rstest]
1131 #[case::reno("reno")]
1132 #[case::cubic("cubic")]
1133 #[case::bbr2_gcongestion("bbr2_gcongestion")]
1134 #[cfg_attr(
1135 feature = "congestion_window_unchecked_available",
1136 case::congestion_window_unchecked("congestion_window_unchecked")
1137 )]
1138 fn loss_on_pto(#[case] cc_algorithm_name: &str) {
1139 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1140 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1141
1142 let mut r = Recovery::new(&cfg);
1143
1144 let mut now = Instant::now();
1145
1146 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1147
1148 let p = Sent {
1150 pkt_num: 0,
1151 frames: smallvec![],
1152 time_sent: now,
1153 time_acked: None,
1154 time_lost: None,
1155 size: 1000,
1156 ack_eliciting: true,
1157 in_flight: true,
1158 delivered: 0,
1159 delivered_time: now,
1160 first_sent_time: now,
1161 is_app_limited: false,
1162 tx_in_flight: 0,
1163 lost: 0,
1164 has_data: false,
1165 is_pmtud_probe: false,
1166 };
1167
1168 r.on_packet_sent(
1169 p,
1170 packet::Epoch::Application,
1171 HandshakeStatus::default(),
1172 now,
1173 "",
1174 );
1175
1176 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
1177 assert_eq!(r.bytes_in_flight(), 1000);
1178 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1179
1180 let p = Sent {
1181 pkt_num: 1,
1182 frames: smallvec![],
1183 time_sent: now,
1184 time_acked: None,
1185 time_lost: None,
1186 size: 1000,
1187 ack_eliciting: true,
1188 in_flight: true,
1189 delivered: 0,
1190 delivered_time: now,
1191 first_sent_time: now,
1192 is_app_limited: false,
1193 tx_in_flight: 0,
1194 lost: 0,
1195 has_data: false,
1196 is_pmtud_probe: false,
1197 };
1198
1199 r.on_packet_sent(
1200 p,
1201 packet::Epoch::Application,
1202 HandshakeStatus::default(),
1203 now,
1204 "",
1205 );
1206
1207 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1208 assert_eq!(r.bytes_in_flight(), 2000);
1209 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1210
1211 let p = Sent {
1212 pkt_num: 2,
1213 frames: smallvec![],
1214 time_sent: now,
1215 time_acked: None,
1216 time_lost: None,
1217 size: 1000,
1218 ack_eliciting: true,
1219 in_flight: true,
1220 delivered: 0,
1221 delivered_time: now,
1222 first_sent_time: now,
1223 is_app_limited: false,
1224 tx_in_flight: 0,
1225 lost: 0,
1226 has_data: false,
1227 is_pmtud_probe: false,
1228 };
1229
1230 r.on_packet_sent(
1231 p,
1232 packet::Epoch::Application,
1233 HandshakeStatus::default(),
1234 now,
1235 "",
1236 );
1237 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
1238 assert_eq!(r.bytes_in_flight(), 3000);
1239 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1240
1241 let p = Sent {
1242 pkt_num: 3,
1243 frames: smallvec![],
1244 time_sent: now,
1245 time_acked: None,
1246 time_lost: None,
1247 size: 1000,
1248 ack_eliciting: true,
1249 in_flight: true,
1250 delivered: 0,
1251 delivered_time: now,
1252 first_sent_time: now,
1253 is_app_limited: false,
1254 tx_in_flight: 0,
1255 lost: 0,
1256 has_data: false,
1257 is_pmtud_probe: false,
1258 };
1259
1260 r.on_packet_sent(
1261 p,
1262 packet::Epoch::Application,
1263 HandshakeStatus::default(),
1264 now,
1265 "",
1266 );
1267 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1268 assert_eq!(r.bytes_in_flight(), 4000);
1269 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1270
1271 now += Duration::from_millis(10);
1273
1274 let mut acked = RangeSet::default();
1276 acked.insert(0..2);
1277
1278 assert_eq!(
1279 r.on_ack_received(
1280 &acked,
1281 25,
1282 packet::Epoch::Application,
1283 HandshakeStatus::default(),
1284 now,
1285 None,
1286 "",
1287 )
1288 .unwrap(),
1289 OnAckReceivedOutcome {
1290 lost_packets: 0,
1291 lost_bytes: 0,
1292 acked_bytes: 2 * 1000,
1293 spurious_losses: 0,
1294 }
1295 );
1296
1297 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1298 assert_eq!(r.bytes_in_flight(), 2000);
1299 assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(10));
1300 assert_eq!(r.lost_count(), 0);
1301
1302 now = r.loss_detection_timer().unwrap();
1304
1305 r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
1307 assert_eq!(r.loss_probes(packet::Epoch::Application), 1);
1308 assert_eq!(r.lost_count(), 0);
1309 assert_eq!(r.pto_count(), 1);
1310
1311 let p = Sent {
1312 pkt_num: 4,
1313 frames: smallvec![],
1314 time_sent: now,
1315 time_acked: None,
1316 time_lost: None,
1317 size: 1000,
1318 ack_eliciting: true,
1319 in_flight: true,
1320 delivered: 0,
1321 delivered_time: now,
1322 first_sent_time: now,
1323 is_app_limited: false,
1324 tx_in_flight: 0,
1325 lost: 0,
1326 has_data: false,
1327 is_pmtud_probe: false,
1328 };
1329
1330 r.on_packet_sent(
1331 p,
1332 packet::Epoch::Application,
1333 HandshakeStatus::default(),
1334 now,
1335 "",
1336 );
1337 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
1338 assert_eq!(r.bytes_in_flight(), 3000);
1339 assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(30));
1340
1341 let p = Sent {
1342 pkt_num: 5,
1343 frames: smallvec![],
1344 time_sent: now,
1345 time_acked: None,
1346 time_lost: None,
1347 size: 1000,
1348 ack_eliciting: true,
1349 in_flight: true,
1350 delivered: 0,
1351 delivered_time: now,
1352 first_sent_time: now,
1353 is_app_limited: false,
1354 tx_in_flight: 0,
1355 lost: 0,
1356 has_data: false,
1357 is_pmtud_probe: false,
1358 };
1359
1360 r.on_packet_sent(
1361 p,
1362 packet::Epoch::Application,
1363 HandshakeStatus::default(),
1364 now,
1365 "",
1366 );
1367 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1368 assert_eq!(r.bytes_in_flight(), 4000);
1369 assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(30));
1370 assert_eq!(r.lost_count(), 0);
1371
1372 now += Duration::from_millis(10);
1374
1375 let mut acked = RangeSet::default();
1377 acked.insert(4..6);
1378
1379 assert_eq!(
1380 r.on_ack_received(
1381 &acked,
1382 25,
1383 packet::Epoch::Application,
1384 HandshakeStatus::default(),
1385 now,
1386 None,
1387 "",
1388 )
1389 .unwrap(),
1390 OnAckReceivedOutcome {
1391 lost_packets: 2,
1392 lost_bytes: 2000,
1393 acked_bytes: 2 * 1000,
1394 spurious_losses: 0,
1395 }
1396 );
1397
1398 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1399 assert_eq!(r.bytes_in_flight(), 0);
1400 assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(40));
1401
1402 assert_eq!(r.lost_count(), 2);
1403
1404 now += r.rtt();
1406
1407 assert_eq!(
1408 r.detect_lost_packets_for_test(packet::Epoch::Application, now),
1409 (0, 0)
1410 );
1411
1412 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1413 if matches!(cc_algorithm_name, "reno" | "cubic") {
1414 assert!(r.startup_exit().is_some());
1415 assert_eq!(r.startup_exit().unwrap().reason, StartupExitReason::Loss);
1416 } else {
1417 assert_eq!(r.startup_exit(), None);
1418 }
1419 }
1420
1421 #[rstest]
1422 #[case::reno("reno")]
1423 #[case::cubic("cubic")]
1424 #[case::bbr2_gcongestion("bbr2_gcongestion")]
1425 #[cfg_attr(
1426 feature = "congestion_window_unchecked_available",
1427 case::congestion_window_unchecked("congestion_window_unchecked")
1428 )]
1429 fn loss_on_timer(#[case] cc_algorithm_name: &str) {
1430 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1431 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1432
1433 let mut r = Recovery::new(&cfg);
1434
1435 let mut now = Instant::now();
1436
1437 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1438
1439 let p = Sent {
1441 pkt_num: 0,
1442 frames: smallvec![],
1443 time_sent: now,
1444 time_acked: None,
1445 time_lost: None,
1446 size: 1000,
1447 ack_eliciting: true,
1448 in_flight: true,
1449 delivered: 0,
1450 delivered_time: now,
1451 first_sent_time: now,
1452 is_app_limited: false,
1453 tx_in_flight: 0,
1454 lost: 0,
1455 has_data: false,
1456 is_pmtud_probe: false,
1457 };
1458
1459 r.on_packet_sent(
1460 p,
1461 packet::Epoch::Application,
1462 HandshakeStatus::default(),
1463 now,
1464 "",
1465 );
1466 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
1467 assert_eq!(r.bytes_in_flight(), 1000);
1468 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1469
1470 let p = Sent {
1471 pkt_num: 1,
1472 frames: smallvec![],
1473 time_sent: now,
1474 time_acked: None,
1475 time_lost: None,
1476 size: 1000,
1477 ack_eliciting: true,
1478 in_flight: true,
1479 delivered: 0,
1480 delivered_time: now,
1481 first_sent_time: now,
1482 is_app_limited: false,
1483 tx_in_flight: 0,
1484 lost: 0,
1485 has_data: false,
1486 is_pmtud_probe: false,
1487 };
1488
1489 r.on_packet_sent(
1490 p,
1491 packet::Epoch::Application,
1492 HandshakeStatus::default(),
1493 now,
1494 "",
1495 );
1496 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1497 assert_eq!(r.bytes_in_flight(), 2000);
1498 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1499
1500 let p = Sent {
1501 pkt_num: 2,
1502 frames: smallvec![],
1503 time_sent: now,
1504 time_acked: None,
1505 time_lost: None,
1506 size: 1000,
1507 ack_eliciting: true,
1508 in_flight: true,
1509 delivered: 0,
1510 delivered_time: now,
1511 first_sent_time: now,
1512 is_app_limited: false,
1513 tx_in_flight: 0,
1514 lost: 0,
1515 has_data: false,
1516 is_pmtud_probe: false,
1517 };
1518
1519 r.on_packet_sent(
1520 p,
1521 packet::Epoch::Application,
1522 HandshakeStatus::default(),
1523 now,
1524 "",
1525 );
1526 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
1527 assert_eq!(r.bytes_in_flight(), 3000);
1528 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1529
1530 let p = Sent {
1531 pkt_num: 3,
1532 frames: smallvec![],
1533 time_sent: now,
1534 time_acked: None,
1535 time_lost: None,
1536 size: 1000,
1537 ack_eliciting: true,
1538 in_flight: true,
1539 delivered: 0,
1540 delivered_time: now,
1541 first_sent_time: now,
1542 is_app_limited: false,
1543 tx_in_flight: 0,
1544 lost: 0,
1545 has_data: false,
1546 is_pmtud_probe: false,
1547 };
1548
1549 r.on_packet_sent(
1550 p,
1551 packet::Epoch::Application,
1552 HandshakeStatus::default(),
1553 now,
1554 "",
1555 );
1556 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1557 assert_eq!(r.bytes_in_flight(), 4000);
1558 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1559
1560 now += Duration::from_millis(10);
1562
1563 let mut acked = RangeSet::default();
1565 acked.insert(0..2);
1566 acked.insert(3..4);
1567
1568 assert_eq!(
1569 r.on_ack_received(
1570 &acked,
1571 25,
1572 packet::Epoch::Application,
1573 HandshakeStatus::default(),
1574 now,
1575 None,
1576 "",
1577 )
1578 .unwrap(),
1579 OnAckReceivedOutcome {
1580 lost_packets: 0,
1581 lost_bytes: 0,
1582 acked_bytes: 3 * 1000,
1583 spurious_losses: 0,
1584 }
1585 );
1586
1587 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1588 assert_eq!(r.bytes_in_flight(), 1000);
1589 assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(10));
1590 assert_eq!(r.lost_count(), 0);
1591
1592 now = r.loss_detection_timer().unwrap();
1594
1595 r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
1597 assert_eq!(r.loss_probes(packet::Epoch::Application), 0);
1598
1599 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1600 assert_eq!(r.bytes_in_flight(), 0);
1601 assert_eq!(r.bytes_in_flight_duration(), Duration::from_micros(11250));
1602
1603 assert_eq!(r.lost_count(), 1);
1604
1605 now += r.rtt();
1607
1608 assert_eq!(
1609 r.detect_lost_packets_for_test(packet::Epoch::Application, now),
1610 (0, 0)
1611 );
1612
1613 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1614 if matches!(cc_algorithm_name, "reno" | "cubic") {
1615 assert!(r.startup_exit().is_some());
1616 assert_eq!(r.startup_exit().unwrap().reason, StartupExitReason::Loss);
1617 } else {
1618 assert_eq!(r.startup_exit(), None);
1619 }
1620 }
1621
1622 #[rstest]
1623 #[case::reno("reno")]
1624 #[case::cubic("cubic")]
1625 #[case::bbr2_gcongestion("bbr2_gcongestion")]
1626 #[cfg_attr(
1627 feature = "congestion_window_unchecked_available",
1628 case::congestion_window_unchecked("congestion_window_unchecked")
1629 )]
1630 fn loss_on_reordering(#[case] cc_algorithm_name: &str) {
1631 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1632 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1633
1634 let mut r = Recovery::new(&cfg);
1635 let initial_cwnd = r.cwnd();
1636
1637 let mut now = Instant::now();
1638
1639 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1640
1641 for i in 0..4 {
1645 let p = test_utils::helper_packet_sent(i, now, 1000);
1646 r.on_packet_sent(
1647 p,
1648 packet::Epoch::Application,
1649 HandshakeStatus::default(),
1650 now,
1651 "",
1652 );
1653
1654 let pkt_count = (i + 1) as usize;
1655 assert_eq!(r.sent_packets_len(packet::Epoch::Application), pkt_count);
1656 assert_eq!(r.bytes_in_flight(), pkt_count * 1000);
1657 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1658 if cc_algorithm_name == "congestion_window_unchecked" {
1659 assert_eq!(r.cwnd(), usize::MAX);
1660 assert_eq!(r.cwnd_available(), usize::MAX - r.bytes_in_flight());
1661 }
1662 }
1663
1664 now += Duration::from_millis(10);
1666
1667 let mut acked = RangeSet::default();
1669 acked.insert(2..4);
1670 assert_eq!(
1671 r.on_ack_received(
1672 &acked,
1673 25,
1674 packet::Epoch::Application,
1675 HandshakeStatus::default(),
1676 now,
1677 None,
1678 "",
1679 )
1680 .unwrap(),
1681 OnAckReceivedOutcome {
1682 lost_packets: 1,
1683 lost_bytes: 1000,
1684 acked_bytes: 1000 * 2,
1685 spurious_losses: 0,
1686 }
1687 );
1688 if cc_algorithm_name == "congestion_window_unchecked" {
1689 assert_eq!(r.cwnd(), initial_cwnd);
1690 assert_eq!(r.rtt(), Duration::from_millis(10));
1691 assert_eq!(r.min_rtt(), Some(Duration::from_millis(10)));
1692 }
1693 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1696 assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1697 assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1698
1699 now += Duration::from_millis(10);
1701
1702 let mut acked = RangeSet::default();
1704 acked.insert(0..2);
1705 assert_eq!(
1706 r.on_ack_received(
1707 &acked,
1708 25,
1709 packet::Epoch::Application,
1710 HandshakeStatus::default(),
1711 now,
1712 None,
1713 "",
1714 )
1715 .unwrap(),
1716 OnAckReceivedOutcome {
1717 lost_packets: 0,
1718 lost_bytes: 0,
1719 acked_bytes: 1000,
1720 spurious_losses: 1,
1721 }
1722 );
1723 if cc_algorithm_name == "congestion_window_unchecked" {
1724 assert_eq!(r.cwnd(), initial_cwnd);
1725 }
1726 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1727 assert_eq!(r.bytes_in_flight(), 0);
1728 assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(20));
1729
1730 assert_eq!(r.lost_count(), 1);
1732 assert_eq!(r.lost_spurious_count(), 1);
1733
1734 assert_eq!(r.pkt_thresh().unwrap(), 4);
1736 assert_eq!(r.time_thresh(), PACKET_REORDER_TIME_THRESHOLD);
1737
1738 now += r.rtt();
1740
1741 assert_eq!(
1743 r.detect_lost_packets_for_test(packet::Epoch::Application, now),
1744 (0, 0)
1745 );
1746 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1747
1748 if matches!(cc_algorithm_name, "reno" | "cubic") {
1749 assert!(r.startup_exit().is_some());
1750 assert_eq!(r.startup_exit().unwrap().reason, StartupExitReason::Loss);
1751 } else {
1752 assert_eq!(r.startup_exit(), None);
1753 }
1754 }
1755
1756 #[rstest]
1761 fn time_thresholds_on_reordering(
1762 #[values("bbr2_gcongestion")] cc_algorithm_name: &str,
1763 ) {
1764 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1765 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1766
1767 let mut now = Instant::now();
1768 let mut r = Recovery::new(&cfg);
1769 assert_eq!(r.rtt(), DEFAULT_INITIAL_RTT);
1770
1771 const THRESH_GAP: Duration = Duration::from_millis(30);
1785 let initial_thresh_ms =
1787 DEFAULT_INITIAL_RTT.mul_f64(INITIAL_TIME_THRESHOLD);
1788 let spurious_thresh_ms: Duration =
1790 DEFAULT_INITIAL_RTT.mul_f64(PACKET_REORDER_TIME_THRESHOLD);
1791 let between_thresh_ms = initial_thresh_ms + THRESH_GAP;
1793 assert!(between_thresh_ms > initial_thresh_ms);
1794 assert!(between_thresh_ms < spurious_thresh_ms);
1795 assert!(between_thresh_ms + THRESH_GAP > spurious_thresh_ms);
1796
1797 for i in 0..6 {
1798 let send_time = now + i * between_thresh_ms;
1799
1800 let p = test_utils::helper_packet_sent(i.into(), send_time, 1000);
1801 r.on_packet_sent(
1802 p,
1803 packet::Epoch::Application,
1804 HandshakeStatus::default(),
1805 send_time,
1806 "",
1807 );
1808 }
1809
1810 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 6);
1811 assert_eq!(r.bytes_in_flight(), 6 * 1000);
1812 assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1813 assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1814
1815 now += between_thresh_ms;
1818
1819 let mut acked = RangeSet::default();
1824 acked.insert(1..2);
1825 assert_eq!(
1826 r.on_ack_received(
1827 &acked,
1828 25,
1829 packet::Epoch::Application,
1830 HandshakeStatus::default(),
1831 now,
1832 None,
1833 "",
1834 )
1835 .unwrap(),
1836 OnAckReceivedOutcome {
1837 lost_packets: 1,
1838 lost_bytes: 1000,
1839 acked_bytes: 1000,
1840 spurious_losses: 0,
1841 }
1842 );
1843 assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1844 assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1845
1846 let mut acked = RangeSet::default();
1851 acked.insert(0..1);
1852 assert_eq!(
1853 r.on_ack_received(
1854 &acked,
1855 25,
1856 packet::Epoch::Application,
1857 HandshakeStatus::default(),
1858 now,
1859 None,
1860 "",
1861 )
1862 .unwrap(),
1863 OnAckReceivedOutcome {
1864 lost_packets: 0,
1865 lost_bytes: 0,
1866 acked_bytes: 0,
1867 spurious_losses: 1,
1868 }
1869 );
1870 assert_eq!(r.time_thresh(), PACKET_REORDER_TIME_THRESHOLD);
1872
1873 now += between_thresh_ms;
1876
1877 let mut acked = RangeSet::default();
1882 acked.insert(3..4);
1883 assert_eq!(
1884 r.on_ack_received(
1885 &acked,
1886 25,
1887 packet::Epoch::Application,
1888 HandshakeStatus::default(),
1889 now,
1890 None,
1891 "",
1892 )
1893 .unwrap(),
1894 OnAckReceivedOutcome {
1895 lost_packets: 0,
1896 lost_bytes: 0,
1897 acked_bytes: 1000,
1898 spurious_losses: 0,
1899 }
1900 );
1901
1902 now += THRESH_GAP;
1905
1906 let mut acked = RangeSet::default();
1911 acked.insert(4..5);
1912 assert_eq!(
1913 r.on_ack_received(
1914 &acked,
1915 25,
1916 packet::Epoch::Application,
1917 HandshakeStatus::default(),
1918 now,
1919 None,
1920 "",
1921 )
1922 .unwrap(),
1923 OnAckReceivedOutcome {
1924 lost_packets: 1,
1925 lost_bytes: 1000,
1926 acked_bytes: 1000,
1927 spurious_losses: 0,
1928 }
1929 );
1930 }
1931
1932 #[rstest]
1935 fn relaxed_thresholds_on_reordering(
1936 #[values("bbr2_gcongestion")] cc_algorithm_name: &str,
1937 ) {
1938 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1939 cfg.enable_relaxed_loss_threshold = true;
1940 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1941
1942 let mut now = Instant::now();
1943 let mut r = Recovery::new(&cfg);
1944 assert_eq!(r.rtt(), DEFAULT_INITIAL_RTT);
1945
1946 const THRESH_GAP: Duration = Duration::from_millis(30);
1959 let initial_thresh_ms =
1961 DEFAULT_INITIAL_RTT.mul_f64(INITIAL_TIME_THRESHOLD);
1962 let spurious_thresh_ms: Duration =
1964 DEFAULT_INITIAL_RTT.mul_f64(PACKET_REORDER_TIME_THRESHOLD);
1965 let between_thresh_ms = initial_thresh_ms + THRESH_GAP;
1967 assert!(between_thresh_ms > initial_thresh_ms);
1968 assert!(between_thresh_ms < spurious_thresh_ms);
1969 assert!(between_thresh_ms + THRESH_GAP > spurious_thresh_ms);
1970
1971 for i in 0..6 {
1972 let send_time = now + i * between_thresh_ms;
1973
1974 let p = test_utils::helper_packet_sent(i.into(), send_time, 1000);
1975 r.on_packet_sent(
1976 p,
1977 packet::Epoch::Application,
1978 HandshakeStatus::default(),
1979 send_time,
1980 "",
1981 );
1982 }
1983
1984 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 6);
1985 assert_eq!(r.bytes_in_flight(), 6 * 1000);
1986 assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1988 assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1989
1990 now += between_thresh_ms;
1993
1994 let mut acked = RangeSet::default();
1999 acked.insert(1..2);
2000 assert_eq!(
2001 r.on_ack_received(
2002 &acked,
2003 25,
2004 packet::Epoch::Application,
2005 HandshakeStatus::default(),
2006 now,
2007 None,
2008 "",
2009 )
2010 .unwrap(),
2011 OnAckReceivedOutcome {
2012 lost_packets: 1,
2013 lost_bytes: 1000,
2014 acked_bytes: 1000,
2015 spurious_losses: 0,
2016 }
2017 );
2018 assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
2020 assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
2021
2022 let mut acked = RangeSet::default();
2027 acked.insert(0..1);
2028 assert_eq!(
2029 r.on_ack_received(
2030 &acked,
2031 25,
2032 packet::Epoch::Application,
2033 HandshakeStatus::default(),
2034 now,
2035 None,
2036 "",
2037 )
2038 .unwrap(),
2039 OnAckReceivedOutcome {
2040 lost_packets: 0,
2041 lost_bytes: 0,
2042 acked_bytes: 0,
2043 spurious_losses: 1,
2044 }
2045 );
2046 assert_eq!(r.pkt_thresh(), None);
2051 assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
2052
2053 now += between_thresh_ms;
2056 now += between_thresh_ms;
2060
2061 let mut acked = RangeSet::default();
2066 acked.insert(3..4);
2067 assert_eq!(
2068 r.on_ack_received(
2069 &acked,
2070 25,
2071 packet::Epoch::Application,
2072 HandshakeStatus::default(),
2073 now,
2074 None,
2075 "",
2076 )
2077 .unwrap(),
2078 OnAckReceivedOutcome {
2079 lost_packets: 1,
2080 lost_bytes: 1000,
2081 acked_bytes: 1000,
2082 spurious_losses: 0,
2083 }
2084 );
2085 assert_eq!(r.pkt_thresh(), None);
2087 assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
2088
2089 let mut acked = RangeSet::default();
2098 acked.insert(2..3);
2099 assert_eq!(
2100 r.on_ack_received(
2101 &acked,
2102 25,
2103 packet::Epoch::Application,
2104 HandshakeStatus::default(),
2105 now,
2106 None,
2107 "",
2108 )
2109 .unwrap(),
2110 OnAckReceivedOutcome {
2111 lost_packets: 0,
2112 lost_bytes: 0,
2113 acked_bytes: 0,
2114 spurious_losses: 1,
2115 }
2116 );
2117 assert_eq!(r.pkt_thresh(), None);
2121 let double_time_thresh_overhead =
2122 1.0 + 2.0 * INITIAL_TIME_THRESHOLD_OVERHEAD;
2123 assert_eq!(r.time_thresh(), double_time_thresh_overhead);
2124 }
2125
2126 #[rstest]
2127 fn pacing(
2128 #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2129 #[values(false, true)] time_sent_set_to_now: bool,
2130 ) {
2131 let pacing_enabled = cc_algorithm_name == "bbr2" ||
2132 cc_algorithm_name == "bbr2_gcongestion";
2133
2134 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2135 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2136
2137 #[cfg(feature = "internal")]
2138 cfg.set_custom_bbr_params(BbrParams {
2139 time_sent_set_to_now: Some(time_sent_set_to_now),
2140 ..Default::default()
2141 });
2142
2143 let mut r = Recovery::new(&cfg);
2144
2145 let mut now = Instant::now();
2146
2147 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2148
2149 for i in 0..10 {
2151 let p = Sent {
2152 pkt_num: i,
2153 frames: smallvec![],
2154 time_sent: now,
2155 time_acked: None,
2156 time_lost: None,
2157 size: 1200,
2158 ack_eliciting: true,
2159 in_flight: true,
2160 delivered: 0,
2161 delivered_time: now,
2162 first_sent_time: now,
2163 is_app_limited: false,
2164 tx_in_flight: 0,
2165 lost: 0,
2166 has_data: true,
2167 is_pmtud_probe: false,
2168 };
2169
2170 r.on_packet_sent(
2171 p,
2172 packet::Epoch::Application,
2173 HandshakeStatus::default(),
2174 now,
2175 "",
2176 );
2177 }
2178
2179 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 10);
2180 assert_eq!(r.bytes_in_flight(), 12000);
2181 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
2182
2183 if !pacing_enabled {
2184 assert_eq!(r.pacing_rate(), 0);
2185 } else {
2186 assert_eq!(r.pacing_rate(), 103963);
2187 }
2188 assert_eq!(r.get_packet_send_time(now), now);
2189
2190 assert_eq!(r.cwnd(), 12000);
2191 assert_eq!(r.cwnd_available(), 0);
2192
2193 let initial_rtt = Duration::from_millis(50);
2195 now += initial_rtt;
2196
2197 let mut acked = RangeSet::default();
2198 acked.insert(0..10);
2199
2200 assert_eq!(
2201 r.on_ack_received(
2202 &acked,
2203 10,
2204 packet::Epoch::Application,
2205 HandshakeStatus::default(),
2206 now,
2207 None,
2208 "",
2209 )
2210 .unwrap(),
2211 OnAckReceivedOutcome {
2212 lost_packets: 0,
2213 lost_bytes: 0,
2214 acked_bytes: 12000,
2215 spurious_losses: 0,
2216 }
2217 );
2218
2219 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2220 assert_eq!(r.bytes_in_flight(), 0);
2221 assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
2222 assert_eq!(r.min_rtt(), Some(initial_rtt));
2223 assert_eq!(r.rtt(), initial_rtt);
2224
2225 assert_eq!(r.cwnd(), 12000 + 1200 * 10);
2227
2228 let p = Sent {
2230 pkt_num: 10,
2231 frames: smallvec![],
2232 time_sent: now,
2233 time_acked: None,
2234 time_lost: None,
2235 size: 6000,
2236 ack_eliciting: true,
2237 in_flight: true,
2238 delivered: 0,
2239 delivered_time: now,
2240 first_sent_time: now,
2241 is_app_limited: false,
2242 tx_in_flight: 0,
2243 lost: 0,
2244 has_data: true,
2245 is_pmtud_probe: false,
2246 };
2247
2248 r.on_packet_sent(
2249 p,
2250 packet::Epoch::Application,
2251 HandshakeStatus::default(),
2252 now,
2253 "",
2254 );
2255
2256 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
2257 assert_eq!(r.bytes_in_flight(), 6000);
2258 assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
2259
2260 if !pacing_enabled {
2261 assert_eq!(r.get_packet_send_time(now), now);
2263 } else {
2264 assert_ne!(r.get_packet_send_time(now), now);
2266 }
2267
2268 let p = Sent {
2270 pkt_num: 11,
2271 frames: smallvec![],
2272 time_sent: now,
2273 time_acked: None,
2274 time_lost: None,
2275 size: 6000,
2276 ack_eliciting: true,
2277 in_flight: true,
2278 delivered: 0,
2279 delivered_time: now,
2280 first_sent_time: now,
2281 is_app_limited: false,
2282 tx_in_flight: 0,
2283 lost: 0,
2284 has_data: true,
2285 is_pmtud_probe: false,
2286 };
2287
2288 r.on_packet_sent(
2289 p,
2290 packet::Epoch::Application,
2291 HandshakeStatus::default(),
2292 now,
2293 "",
2294 );
2295
2296 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2297 assert_eq!(r.bytes_in_flight(), 12000);
2298 assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
2299
2300 let p = Sent {
2302 pkt_num: 12,
2303 frames: smallvec![],
2304 time_sent: now,
2305 time_acked: None,
2306 time_lost: None,
2307 size: 1000,
2308 ack_eliciting: true,
2309 in_flight: true,
2310 delivered: 0,
2311 delivered_time: now,
2312 first_sent_time: now,
2313 is_app_limited: false,
2314 tx_in_flight: 0,
2315 lost: 0,
2316 has_data: true,
2317 is_pmtud_probe: false,
2318 };
2319
2320 r.on_packet_sent(
2321 p,
2322 packet::Epoch::Application,
2323 HandshakeStatus::default(),
2324 now,
2325 "",
2326 );
2327
2328 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
2329 assert_eq!(r.bytes_in_flight(), 13000);
2330 assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
2331
2332 let pacing_rate = if pacing_enabled {
2335 let cwnd_gain: f64 = 2.0;
2336 let bw = r.cwnd() as f64 / cwnd_gain / initial_rtt.as_secs_f64();
2339 bw as u64
2340 } else {
2341 0
2342 };
2343 assert_eq!(r.pacing_rate(), pacing_rate);
2344
2345 let scale_factor = if pacing_enabled {
2346 1.08333332
2349 } else {
2350 1.0
2351 };
2352 assert_eq!(
2353 r.get_packet_send_time(now) - now,
2354 if pacing_enabled {
2355 Duration::from_secs_f64(
2356 scale_factor * 12000.0 / pacing_rate as f64,
2357 )
2358 } else {
2359 Duration::ZERO
2360 }
2361 );
2362 assert_eq!(r.startup_exit(), None);
2363
2364 let reduced_rtt = Duration::from_millis(40);
2365 now += reduced_rtt;
2366
2367 let mut acked = RangeSet::default();
2368 acked.insert(10..11);
2369
2370 assert_eq!(
2371 r.on_ack_received(
2372 &acked,
2373 0,
2374 packet::Epoch::Application,
2375 HandshakeStatus::default(),
2376 now,
2377 None,
2378 "",
2379 )
2380 .unwrap(),
2381 OnAckReceivedOutcome {
2382 lost_packets: 0,
2383 lost_bytes: 0,
2384 acked_bytes: 6000,
2385 spurious_losses: 0,
2386 }
2387 );
2388
2389 let expected_srtt = (7 * initial_rtt + reduced_rtt) / 8;
2390 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2391 assert_eq!(r.bytes_in_flight(), 7000);
2392 assert_eq!(r.bytes_in_flight_duration(), initial_rtt + reduced_rtt);
2393 assert_eq!(r.min_rtt(), Some(reduced_rtt));
2394 assert_eq!(r.rtt(), expected_srtt);
2395
2396 let mut acked = RangeSet::default();
2397 acked.insert(11..12);
2398
2399 assert_eq!(
2400 r.on_ack_received(
2401 &acked,
2402 0,
2403 packet::Epoch::Application,
2404 HandshakeStatus::default(),
2405 now,
2406 None,
2407 "",
2408 )
2409 .unwrap(),
2410 OnAckReceivedOutcome {
2411 lost_packets: 0,
2412 lost_bytes: 0,
2413 acked_bytes: 6000,
2414 spurious_losses: 0,
2415 }
2416 );
2417
2418 let expected_min_rtt = if pacing_enabled &&
2422 !time_sent_set_to_now &&
2423 cfg!(feature = "internal")
2424 {
2425 reduced_rtt - Duration::from_millis(25)
2426 } else {
2427 reduced_rtt
2428 };
2429
2430 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
2431 assert_eq!(r.bytes_in_flight(), 1000);
2432 assert_eq!(r.bytes_in_flight_duration(), initial_rtt + reduced_rtt);
2433 assert_eq!(r.min_rtt(), Some(expected_min_rtt));
2434
2435 let expected_srtt = (7 * expected_srtt + expected_min_rtt) / 8;
2436 assert_eq!(r.rtt(), expected_srtt);
2437
2438 let mut acked = RangeSet::default();
2439 acked.insert(12..13);
2440
2441 assert_eq!(
2442 r.on_ack_received(
2443 &acked,
2444 0,
2445 packet::Epoch::Application,
2446 HandshakeStatus::default(),
2447 now,
2448 None,
2449 "",
2450 )
2451 .unwrap(),
2452 OnAckReceivedOutcome {
2453 lost_packets: 0,
2454 lost_bytes: 0,
2455 acked_bytes: 1000,
2456 spurious_losses: 0,
2457 }
2458 );
2459
2460 let expected_min_rtt = if pacing_enabled &&
2463 !time_sent_set_to_now &&
2464 cfg!(feature = "internal")
2465 {
2466 Duration::from_millis(0)
2467 } else {
2468 reduced_rtt
2469 };
2470 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2471 assert_eq!(r.bytes_in_flight(), 0);
2472 assert_eq!(r.bytes_in_flight_duration(), initial_rtt + reduced_rtt);
2473 assert_eq!(r.min_rtt(), Some(expected_min_rtt));
2474
2475 let expected_srtt = (7 * expected_srtt + expected_min_rtt) / 8;
2476 assert_eq!(r.rtt(), expected_srtt);
2477 }
2478
2479 #[rstest]
2480 #[case::bw_estimate_equal_after_first_rtt(1.0, 1.0)]
2483 #[case::bw_estimate_decrease_after_first_rtt(2.0, 1.0)]
2486 #[case::bw_estimate_increase_after_first_rtt(0.5, 0.5)]
2492 #[cfg(feature = "internal")]
2493 fn initial_pacing_rate_override(
2494 #[case] initial_multipler: f64, #[case] expected_multiplier: f64,
2495 ) {
2496 let rtt = Duration::from_millis(50);
2497 let bw = Bandwidth::from_bytes_and_time_delta(12000, rtt);
2498 let initial_pacing_rate_hint = bw * initial_multipler;
2499 let expected_pacing_with_rtt_measurement = bw * expected_multiplier;
2500
2501 let cc_algorithm_name = "bbr2_gcongestion";
2502 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2503 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2504 cfg.set_custom_bbr_params(BbrParams {
2505 initial_pacing_rate_bytes_per_second: Some(
2506 initial_pacing_rate_hint.to_bytes_per_second(),
2507 ),
2508 ..Default::default()
2509 });
2510
2511 let mut r = Recovery::new(&cfg);
2512
2513 let mut now = Instant::now();
2514
2515 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2516
2517 for i in 0..2 {
2519 let p = test_utils::helper_packet_sent(i, now, 1200);
2520 r.on_packet_sent(
2521 p,
2522 packet::Epoch::Application,
2523 HandshakeStatus::default(),
2524 now,
2525 "",
2526 );
2527 }
2528
2529 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2530 assert_eq!(r.bytes_in_flight(), 2400);
2531 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
2532
2533 assert_eq!(
2535 r.pacing_rate(),
2536 initial_pacing_rate_hint.to_bytes_per_second()
2537 );
2538 assert_eq!(r.get_packet_send_time(now), now);
2539
2540 assert_eq!(r.cwnd(), 12000);
2541 assert_eq!(r.cwnd_available(), 9600);
2542
2543 now += rtt;
2545
2546 let mut acked = RangeSet::default();
2547 acked.insert(0..2);
2548
2549 assert_eq!(
2550 r.on_ack_received(
2551 &acked,
2552 10,
2553 packet::Epoch::Application,
2554 HandshakeStatus::default(),
2555 now,
2556 None,
2557 "",
2558 )
2559 .unwrap(),
2560 OnAckReceivedOutcome {
2561 lost_packets: 0,
2562 lost_bytes: 0,
2563 acked_bytes: 2400,
2564 spurious_losses: 0,
2565 }
2566 );
2567
2568 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2569 assert_eq!(r.bytes_in_flight(), 0);
2570 assert_eq!(r.bytes_in_flight_duration(), rtt);
2571 assert_eq!(r.rtt(), rtt);
2572
2573 assert_eq!(
2576 r.pacing_rate(),
2577 expected_pacing_with_rtt_measurement.to_bytes_per_second()
2578 );
2579 }
2580
2581 #[rstest]
2582 fn validate_ack_range_on_ack_received(
2583 #[values("cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2584 ) {
2585 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2586 cfg.set_cc_algorithm_name(cc_algorithm_name).unwrap();
2587
2588 let epoch = packet::Epoch::Application;
2589 let mut r = Recovery::new(&cfg);
2590 let mut now = Instant::now();
2591 assert_eq!(r.sent_packets_len(epoch), 0);
2592
2593 let pkt_size = 1000;
2595 let pkt_count = 4;
2596 for pkt_num in 0..pkt_count {
2597 let sent = test_utils::helper_packet_sent(pkt_num, now, pkt_size);
2598 r.on_packet_sent(sent, epoch, HandshakeStatus::default(), now, "");
2599 }
2600 assert_eq!(r.sent_packets_len(epoch), pkt_count as usize);
2601 assert_eq!(r.bytes_in_flight(), pkt_count as usize * pkt_size);
2602 assert!(r.get_largest_acked_on_epoch(epoch).is_none());
2603 assert_eq!(r.largest_sent_pkt_num_on_path(epoch).unwrap(), 3);
2604
2605 now += Duration::from_millis(10);
2607
2608 let mut acked = RangeSet::default();
2610 acked.insert(0..2);
2611
2612 assert_eq!(
2613 r.on_ack_received(
2614 &acked,
2615 25,
2616 epoch,
2617 HandshakeStatus::default(),
2618 now,
2619 None,
2620 "",
2621 )
2622 .unwrap(),
2623 OnAckReceivedOutcome {
2624 lost_packets: 0,
2625 lost_bytes: 0,
2626 acked_bytes: 2 * 1000,
2627 spurious_losses: 0,
2628 }
2629 );
2630
2631 assert_eq!(r.sent_packets_len(epoch), 2);
2632 assert_eq!(r.bytes_in_flight(), 2 * 1000);
2633
2634 assert_eq!(r.get_largest_acked_on_epoch(epoch).unwrap(), 1);
2635 assert_eq!(r.largest_sent_pkt_num_on_path(epoch).unwrap(), 3);
2636
2637 let mut acked = RangeSet::default();
2639 acked.insert(0..10);
2640 assert_eq!(
2641 r.on_ack_received(
2642 &acked,
2643 25,
2644 epoch,
2645 HandshakeStatus::default(),
2646 now,
2647 None,
2648 "",
2649 )
2650 .unwrap(),
2651 OnAckReceivedOutcome {
2652 lost_packets: 0,
2653 lost_bytes: 0,
2654 acked_bytes: 2 * 1000,
2655 spurious_losses: 0,
2656 }
2657 );
2658 assert_eq!(r.sent_packets_len(epoch), 0);
2659 assert_eq!(r.bytes_in_flight(), 0);
2660
2661 assert_eq!(r.get_largest_acked_on_epoch(epoch).unwrap(), 3);
2662 assert_eq!(r.largest_sent_pkt_num_on_path(epoch).unwrap(), 3);
2663 }
2664
2665 #[rstest]
2666 fn pmtud_loss_on_timer(
2667 #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2668 ) {
2669 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2670 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2671
2672 let mut r = Recovery::new(&cfg);
2673 assert_eq!(r.cwnd(), 12000);
2674
2675 let mut now = Instant::now();
2676
2677 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2678
2679 let p = Sent {
2681 pkt_num: 0,
2682 frames: smallvec![],
2683 time_sent: now,
2684 time_acked: None,
2685 time_lost: None,
2686 size: 1000,
2687 ack_eliciting: true,
2688 in_flight: true,
2689 delivered: 0,
2690 delivered_time: now,
2691 first_sent_time: now,
2692 is_app_limited: false,
2693 tx_in_flight: 0,
2694 lost: 0,
2695 has_data: false,
2696 is_pmtud_probe: false,
2697 };
2698
2699 r.on_packet_sent(
2700 p,
2701 packet::Epoch::Application,
2702 HandshakeStatus::default(),
2703 now,
2704 "",
2705 );
2706
2707 assert_eq!(r.in_flight_count(packet::Epoch::Application), 1);
2708 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
2709 assert_eq!(r.bytes_in_flight(), 1000);
2710 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
2711
2712 let p = Sent {
2713 pkt_num: 1,
2714 frames: smallvec![],
2715 time_sent: now,
2716 time_acked: None,
2717 time_lost: None,
2718 size: 1000,
2719 ack_eliciting: true,
2720 in_flight: true,
2721 delivered: 0,
2722 delivered_time: now,
2723 first_sent_time: now,
2724 is_app_limited: false,
2725 tx_in_flight: 0,
2726 lost: 0,
2727 has_data: false,
2728 is_pmtud_probe: true,
2729 };
2730
2731 r.on_packet_sent(
2732 p,
2733 packet::Epoch::Application,
2734 HandshakeStatus::default(),
2735 now,
2736 "",
2737 );
2738
2739 assert_eq!(r.in_flight_count(packet::Epoch::Application), 2);
2740
2741 let p = Sent {
2742 pkt_num: 2,
2743 frames: smallvec![],
2744 time_sent: now,
2745 time_acked: None,
2746 time_lost: None,
2747 size: 1000,
2748 ack_eliciting: true,
2749 in_flight: true,
2750 delivered: 0,
2751 delivered_time: now,
2752 first_sent_time: now,
2753 is_app_limited: false,
2754 tx_in_flight: 0,
2755 lost: 0,
2756 has_data: false,
2757 is_pmtud_probe: false,
2758 };
2759
2760 r.on_packet_sent(
2761 p,
2762 packet::Epoch::Application,
2763 HandshakeStatus::default(),
2764 now,
2765 "",
2766 );
2767
2768 assert_eq!(r.in_flight_count(packet::Epoch::Application), 3);
2769
2770 now += Duration::from_millis(10);
2772
2773 let mut acked = RangeSet::default();
2775 acked.insert(0..1);
2776 acked.insert(2..3);
2777
2778 assert_eq!(
2779 r.on_ack_received(
2780 &acked,
2781 25,
2782 packet::Epoch::Application,
2783 HandshakeStatus::default(),
2784 now,
2785 None,
2786 "",
2787 )
2788 .unwrap(),
2789 OnAckReceivedOutcome {
2790 lost_packets: 0,
2791 lost_bytes: 0,
2792 acked_bytes: 2 * 1000,
2793 spurious_losses: 0,
2794 }
2795 );
2796
2797 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2798 assert_eq!(r.bytes_in_flight(), 1000);
2799 assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(10));
2800 assert_eq!(r.lost_count(), 0);
2801
2802 now = r.loss_detection_timer().unwrap();
2804
2805 r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
2807 assert_eq!(r.loss_probes(packet::Epoch::Application), 0);
2808
2809 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2810 assert_eq!(r.in_flight_count(packet::Epoch::Application), 0);
2811 assert_eq!(r.bytes_in_flight(), 0);
2812 assert_eq!(r.bytes_in_flight_duration(), Duration::from_micros(11250));
2813 assert_eq!(r.cwnd(), 12000);
2814
2815 assert_eq!(r.lost_count(), 0);
2816
2817 now += r.rtt();
2819
2820 assert_eq!(
2821 r.detect_lost_packets_for_test(packet::Epoch::Application, now),
2822 (0, 0)
2823 );
2824
2825 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2826 assert_eq!(r.in_flight_count(packet::Epoch::Application), 0);
2827 assert_eq!(r.bytes_in_flight(), 0);
2828 assert_eq!(r.bytes_in_flight_duration(), Duration::from_micros(11250));
2829 assert_eq!(r.lost_count(), 0);
2830 assert_eq!(r.startup_exit(), None);
2831 }
2832
2833 #[rstest]
2836 fn congestion_delivery_rate(
2837 #[values("reno", "cubic", "bbr2")] cc_algorithm_name: &str,
2838 ) {
2839 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2840 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2841
2842 let mut r = Recovery::new(&cfg);
2843 assert_eq!(r.cwnd(), 12000);
2844
2845 let now = Instant::now();
2846
2847 let mut total_bytes_sent = 0;
2848 for pn in 0..10 {
2849 let bytes = 1000;
2851 let sent = test_utils::helper_packet_sent(pn, now, bytes);
2852 r.on_packet_sent(
2853 sent,
2854 packet::Epoch::Application,
2855 HandshakeStatus::default(),
2856 now,
2857 "",
2858 );
2859
2860 total_bytes_sent += bytes;
2861 }
2862
2863 let interval = Duration::from_secs(10);
2865 let mut acked = RangeSet::default();
2866 acked.insert(0..10);
2867 assert_eq!(
2868 r.on_ack_received(
2869 &acked,
2870 25,
2871 packet::Epoch::Application,
2872 HandshakeStatus::default(),
2873 now + interval,
2874 None,
2875 "",
2876 )
2877 .unwrap(),
2878 OnAckReceivedOutcome {
2879 lost_packets: 0,
2880 lost_bytes: 0,
2881 acked_bytes: total_bytes_sent,
2882 spurious_losses: 0,
2883 }
2884 );
2885 assert_eq!(r.delivery_rate().to_bytes_per_second(), 1000);
2886 assert_eq!(r.min_rtt().unwrap(), interval);
2887 assert_eq!(
2889 total_bytes_sent as u64 / interval.as_secs(),
2890 r.delivery_rate().to_bytes_per_second()
2891 );
2892 assert_eq!(r.startup_exit(), None);
2893 }
2894
2895 #[rstest]
2896 #[case::reno("reno")]
2897 #[case::cubic("cubic")]
2898 #[case::bbr2_gcongestion("bbr2_gcongestion")]
2899 #[cfg_attr(
2900 feature = "congestion_window_unchecked_available",
2901 case::congestion_window_unchecked("congestion_window_unchecked")
2902 )]
2903 fn acks_with_no_retransmittable_data(#[case] cc_algorithm_name: &str) {
2904 let rtt = Duration::from_millis(100);
2905
2906 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2907 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2908
2909 let mut r = Recovery::new(&cfg);
2910
2911 let mut now = Instant::now();
2912
2913 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2914
2915 let mut next_packet = 0;
2916 for _ in 0..3 {
2918 let p = test_utils::helper_packet_sent(next_packet, now, 1200);
2919 next_packet += 1;
2920 r.on_packet_sent(
2921 p,
2922 packet::Epoch::Application,
2923 HandshakeStatus::default(),
2924 now,
2925 "",
2926 );
2927 }
2928
2929 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
2930 assert_eq!(r.bytes_in_flight(), 3600);
2931 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
2932
2933 assert_eq!(
2934 r.pacing_rate(),
2935 if cc_algorithm_name == "bbr2_gcongestion" {
2936 103963
2937 } else {
2938 0
2939 },
2940 );
2941 assert_eq!(r.get_packet_send_time(now), now);
2942 let initial_cwnd = if cc_algorithm_name == "congestion_window_unchecked" {
2943 usize::MAX
2944 } else {
2945 12000
2946 };
2947 assert_eq!(r.cwnd(), initial_cwnd);
2948 assert_eq!(r.cwnd_available(), initial_cwnd - r.bytes_in_flight());
2949
2950 now += rtt;
2952
2953 let mut acked = RangeSet::default();
2954 acked.insert(0..3);
2955
2956 assert_eq!(
2957 r.on_ack_received(
2958 &acked,
2959 10,
2960 packet::Epoch::Application,
2961 HandshakeStatus::default(),
2962 now,
2963 None,
2964 "",
2965 )
2966 .unwrap(),
2967 OnAckReceivedOutcome {
2968 lost_packets: 0,
2969 lost_bytes: 0,
2970 acked_bytes: 3600,
2971 spurious_losses: 0,
2972 }
2973 );
2974
2975 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2976 assert_eq!(r.bytes_in_flight(), 0);
2977 assert_eq!(r.bytes_in_flight_duration(), rtt);
2978 assert_eq!(r.rtt(), rtt);
2979 if cc_algorithm_name == "congestion_window_unchecked" {
2980 assert_eq!(r.cwnd(), usize::MAX);
2981 assert_eq!(r.min_rtt(), Some(rtt));
2982 }
2983
2984 assert_eq!(
2987 r.pacing_rate(),
2988 if cc_algorithm_name == "bbr2_gcongestion" {
2989 120000
2990 } else {
2991 0
2992 },
2993 );
2994
2995 for iter in 3..1000 {
2997 let mut p = test_utils::helper_packet_sent(next_packet, now, 1200);
2998 p.in_flight = false;
3001 next_packet += 1;
3002 r.on_packet_sent(
3003 p,
3004 packet::Epoch::Application,
3005 HandshakeStatus::default(),
3006 now,
3007 "",
3008 );
3009
3010 now += rtt;
3011
3012 let mut acked = RangeSet::default();
3013 acked.insert(iter..(iter + 1));
3014
3015 assert_eq!(
3016 r.on_ack_received(
3017 &acked,
3018 10,
3019 packet::Epoch::Application,
3020 HandshakeStatus::default(),
3021 now,
3022 None,
3023 "",
3024 )
3025 .unwrap(),
3026 OnAckReceivedOutcome {
3027 lost_packets: 0,
3028 lost_bytes: 0,
3029 acked_bytes: 0,
3030 spurious_losses: 0,
3031 }
3032 );
3033
3034 assert_eq!(r.startup_exit(), None, "{iter}");
3036
3037 assert_eq!(
3039 r.sent_packets_len(packet::Epoch::Application),
3040 0,
3041 "{iter}"
3042 );
3043 assert_eq!(r.bytes_in_flight(), 0, "{iter}");
3044 assert_eq!(r.bytes_in_flight_duration(), rtt, "{iter}");
3045 assert_eq!(
3046 r.pacing_rate(),
3047 if cc_algorithm_name == "bbr2_gcongestion" ||
3048 cc_algorithm_name == "bbr2"
3049 {
3050 120000
3051 } else {
3052 0
3053 },
3054 "{iter}"
3055 );
3056 }
3057 }
3058 #[rstest]
3059 fn pto_overflow_reproduction(
3060 #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
3061 ) {
3062 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
3063 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
3064 let mut r = Recovery::new(&cfg);
3065 let now = Instant::now();
3066
3067 let handshake_status = HandshakeStatus {
3069 has_handshake_keys: true,
3070 peer_verified_address: true,
3071 completed: false,
3072 };
3073
3074 let p_initial = Sent {
3076 pkt_num: 0,
3077 frames: smallvec::smallvec![],
3078 time_sent: now,
3079 time_acked: None,
3080 time_lost: None,
3081 size: 1000,
3082 ack_eliciting: true,
3083 in_flight: true,
3084 delivered: 0,
3085 delivered_time: now,
3086 first_sent_time: now,
3087 is_app_limited: false,
3088 tx_in_flight: 0,
3089 lost: 0,
3090 has_data: false,
3091 is_pmtud_probe: false,
3092 };
3093 r.on_packet_sent(
3094 p_initial,
3095 packet::Epoch::Initial,
3096 handshake_status,
3097 now,
3098 "",
3099 );
3100
3101 assert!(r.loss_detection_timer().is_some());
3103
3104 let p_app = Sent {
3106 pkt_num: 0, frames: smallvec::smallvec![],
3108 time_sent: now,
3109 time_acked: None,
3110 time_lost: None,
3111 size: 1000,
3112 ack_eliciting: true,
3113 in_flight: true,
3114 delivered: 0,
3115 delivered_time: now,
3116 first_sent_time: now,
3117 is_app_limited: false,
3118 tx_in_flight: 0,
3119
3120 lost: 0,
3121 has_data: true,
3122 is_pmtud_probe: false,
3123 };
3124 r.on_packet_sent(
3125 p_app,
3126 packet::Epoch::Application,
3127 handshake_status,
3128 now,
3129 "",
3130 );
3131
3132 let mut ranges = RangeSet::default();
3136 ranges.insert(0..1);
3137 r.on_ack_received(
3138 &ranges,
3139 0,
3140 packet::Epoch::Initial,
3141 handshake_status,
3142 now,
3143 None,
3144 "",
3145 )
3146 .unwrap();
3147
3148 assert!(r.loss_detection_timer().is_none());
3154 }
3155
3156 #[rstest]
3159 fn pto_does_not_duplicate_frames_on_consecutive_timeouts(
3160 #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
3161 ) {
3162 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
3163 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
3164
3165 let mut r = Recovery::new(&cfg);
3166 let mut now = Instant::now();
3167
3168 let frames = smallvec![frame::Frame::Stream {
3170 stream_id: 4,
3171 data: RangeBuf::from(b"test", 0, false),
3172 },];
3173
3174 let p = Sent {
3175 pkt_num: 0,
3176 frames: frames.clone(),
3177 time_sent: now,
3178 time_acked: None,
3179 time_lost: None,
3180 size: 1000,
3181 ack_eliciting: true,
3182 in_flight: true,
3183 delivered: 0,
3184 delivered_time: now,
3185 first_sent_time: now,
3186 is_app_limited: false,
3187 tx_in_flight: 0,
3188 lost: 0,
3189 has_data: true,
3190 is_pmtud_probe: false,
3191 };
3192
3193 r.on_packet_sent(
3194 p,
3195 packet::Epoch::Application,
3196 HandshakeStatus::default(),
3197 now,
3198 "",
3199 );
3200
3201 assert_eq!(r.lost_frames_count(packet::Epoch::Application), 0);
3203 assert_eq!(r.lost_count(), 0);
3204
3205 now = r.loss_detection_timer().unwrap();
3207 r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
3208
3209 assert_eq!(r.pto_count(), 1);
3210 let frames_after_first_pto =
3211 r.lost_frames_count(packet::Epoch::Application);
3212 assert_eq!(
3213 frames_after_first_pto, 1,
3214 "First PTO should add exactly 1 frame"
3215 );
3216 assert_eq!(
3217 r.lost_count(),
3218 0,
3219 "PTO doesn't declare packets lost (no CC impact)"
3220 );
3221
3222 now = r.loss_detection_timer().unwrap();
3226 r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
3227
3228 assert_eq!(r.pto_count(), 2);
3229 let frames_after_second_pto =
3230 r.lost_frames_count(packet::Epoch::Application);
3231 assert_eq!(
3232 frames_after_second_pto, frames_after_first_pto,
3233 "Second PTO must NOT add duplicate frames (fix: `if \
3234 lost_frames.is_empty()`)"
3235 );
3236
3237 now = r.loss_detection_timer().unwrap();
3239 r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
3240
3241 assert_eq!(r.pto_count(), 3);
3242 let frames_after_third_pto =
3243 r.lost_frames_count(packet::Epoch::Application);
3244 assert_eq!(
3245 frames_after_third_pto, frames_after_first_pto,
3246 "Third PTO must NOT add duplicate frames"
3247 );
3248
3249 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
3251 assert_eq!(r.lost_count(), 0);
3253 }
3254
3255 #[rstest]
3258 fn pto_send_on_path_retransmits_without_loss(
3259 #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
3260 ) {
3261 use crate::test_utils;
3262
3263 let mut pipe = test_utils::Pipe::new(cc_algorithm_name).unwrap();
3264
3265 assert_eq!(pipe.handshake(), Ok(()));
3267
3268 assert_eq!(pipe.client.stream_send(4, b"hello", false), Ok(5));
3270
3271 let mut buf = [0; 65535];
3272
3273 let (len1, _) = pipe.client.send(&mut buf).unwrap();
3275 assert!(len1 > 0);
3276
3277 let initial_lost_count = pipe
3279 .client
3280 .paths
3281 .get_active()
3282 .unwrap()
3283 .recovery
3284 .lost_count();
3285 assert_eq!(initial_lost_count, 0, "No packets should be lost initially");
3286
3287 let initial_lost_frames = pipe
3289 .client
3290 .paths
3291 .get_active()
3292 .unwrap()
3293 .recovery
3294 .lost_frames_count(packet::Epoch::Application);
3295 assert_eq!(
3296 initial_lost_frames, 0,
3297 "No frames should be in lost_frames initially"
3298 );
3299
3300 let timer = pipe.client.timeout().unwrap();
3302 std::thread::sleep(timer + Duration::from_millis(1));
3303
3304 pipe.client.on_timeout();
3306
3307 let lost_frames_after_pto = pipe
3309 .client
3310 .paths
3311 .get_active()
3312 .unwrap()
3313 .recovery
3314 .lost_frames_count(packet::Epoch::Application);
3315 assert!(
3316 lost_frames_after_pto > 0,
3317 "PTO should add frames to lost_frames for retransmission"
3318 );
3319
3320 let lost_count_after_pto = pipe
3322 .client
3323 .paths
3324 .get_active()
3325 .unwrap()
3326 .recovery
3327 .lost_count();
3328 assert_eq!(
3329 lost_count_after_pto, 0,
3330 "PTO should not increment lost_count"
3331 );
3332
3333 let (len2, _) = pipe.client.send(&mut buf).unwrap();
3335 assert!(len2 > 0, "Should send PTO probe packet");
3336
3337 let lost_count_after_send = pipe
3339 .client
3340 .paths
3341 .get_active()
3342 .unwrap()
3343 .recovery
3344 .lost_count();
3345 assert_eq!(
3346 lost_count_after_send, 0,
3347 "Sending PTO probe should not increment lost_count"
3348 );
3349
3350 assert_eq!(pipe.server_recv(&mut buf[..len2]), Ok(len2));
3352
3353 let mut recv_buf = [0; 100];
3355 assert_eq!(pipe.server.stream_recv(4, &mut recv_buf), Ok((5, false)));
3356 assert_eq!(&recv_buf[..5], b"hello");
3357
3358 let final_lost_count = pipe
3360 .client
3361 .paths
3362 .get_active()
3363 .unwrap()
3364 .recovery
3365 .lost_count();
3366 assert_eq!(
3367 final_lost_count, 0,
3368 "No packets should be marked as lost - PTO only retransmits"
3369 );
3370 }
3371}
3372
3373mod bandwidth;
3374mod bytes_in_flight;
3375mod congestion;
3376mod gcongestion;
3377mod rtt;