use crate::Duration;
use crate::connection::spaces::PendingAcks;
use crate::frame::AckFrequency;
use crate::transport_parameters::TransportParameters;
use crate::{AckFrequencyConfig, TIMER_GRANULARITY, TransportError, VarInt};
pub(super) struct AckFrequencyState {
in_flight_ack_frequency_frame: Option<(u64, Duration)>,
next_outgoing_sequence_number: VarInt,
pub(super) peer_max_ack_delay: Duration,
last_ack_frequency_frame: Option<u64>,
pub(super) max_ack_delay: Duration,
}
impl AckFrequencyState {
pub(super) fn new(default_max_ack_delay: Duration) -> Self {
Self {
in_flight_ack_frequency_frame: None,
next_outgoing_sequence_number: VarInt(0),
peer_max_ack_delay: default_max_ack_delay,
last_ack_frequency_frame: None,
max_ack_delay: default_max_ack_delay,
}
}
pub(super) fn candidate_max_ack_delay(
&self,
rtt: Duration,
config: &AckFrequencyConfig,
peer_params: &TransportParameters,
) -> Duration {
let min_ack_delay =
Duration::from_micros(peer_params.min_ack_delay.map_or(0, |x| x.into()));
config
.max_ack_delay
.unwrap_or(self.peer_max_ack_delay)
.clamp(min_ack_delay, rtt.max(MIN_AUTOMATIC_ACK_DELAY))
}
pub(super) fn max_ack_delay_for_pto(&self) -> Duration {
if let Some((_, max_ack_delay)) = self.in_flight_ack_frequency_frame {
self.peer_max_ack_delay.max(max_ack_delay)
} else {
self.peer_max_ack_delay
}
}
pub(super) fn next_sequence_number(&mut self) -> VarInt {
assert!(self.next_outgoing_sequence_number <= VarInt::MAX);
let seq = self.next_outgoing_sequence_number;
self.next_outgoing_sequence_number.0 += 1;
seq
}
pub(super) fn should_send_ack_frequency(
&self,
rtt: Duration,
config: &AckFrequencyConfig,
peer_params: &TransportParameters,
) -> bool {
if self.next_outgoing_sequence_number.0 == 0 {
return true;
}
let current = self
.in_flight_ack_frequency_frame
.map_or(self.peer_max_ack_delay, |(_, pending)| pending);
let desired = self.candidate_max_ack_delay(rtt, config, peer_params);
let error = (desired.as_secs_f32() / current.as_secs_f32()) - 1.0;
error.abs() > MAX_RTT_ERROR
}
pub(super) fn ack_frequency_sent(&mut self, pn: u64, requested_max_ack_delay: Duration) {
self.in_flight_ack_frequency_frame = Some((pn, requested_max_ack_delay));
}
pub(super) fn on_acked(&mut self, pn: u64) {
match self.in_flight_ack_frequency_frame {
Some((number, requested_max_ack_delay)) if number == pn => {
self.in_flight_ack_frequency_frame = None;
self.peer_max_ack_delay = requested_max_ack_delay;
}
_ => {}
}
}
pub(super) fn ack_frequency_received(
&mut self,
frame: &AckFrequency,
pending_acks: &mut PendingAcks,
) -> Result<bool, TransportError> {
if self
.last_ack_frequency_frame
.is_some_and(|highest_sequence_nr| frame.sequence.into_inner() <= highest_sequence_nr)
{
return Ok(false);
}
self.last_ack_frequency_frame = Some(frame.sequence.into_inner());
let max_ack_delay = Duration::from_micros(frame.request_max_ack_delay.into_inner());
if max_ack_delay < TIMER_GRANULARITY {
return Err(TransportError::PROTOCOL_VIOLATION(
"Requested Max Ack Delay in ACK_FREQUENCY frame is less than min_ack_delay",
));
}
self.max_ack_delay = max_ack_delay;
pending_acks.set_ack_frequency_params(frame);
Ok(true)
}
}
const MAX_RTT_ERROR: f32 = 0.2;
const MIN_AUTOMATIC_ACK_DELAY: Duration = Duration::from_millis(25);
#[cfg(test)]
mod tests {
use super::*;
fn ack_state() -> AckFrequencyState {
AckFrequencyState::new(Duration::from_millis(25))
}
fn default_config() -> AckFrequencyConfig {
AckFrequencyConfig::default()
}
fn default_params() -> TransportParameters {
TransportParameters::default()
}
#[test]
fn new_default_max_ack_delay() {
let state = AckFrequencyState::new(Duration::from_millis(25));
assert_eq!(state.max_ack_delay, Duration::from_millis(25));
assert_eq!(state.peer_max_ack_delay, Duration::from_millis(25));
assert!(state.in_flight_ack_frequency_frame.is_none());
}
#[test]
fn new_sequence_starts_at_zero() {
let state = AckFrequencyState::new(Duration::from_millis(25));
assert_eq!(state.next_outgoing_sequence_number.0, 0);
}
#[test]
fn next_sequence_increments() {
let mut state = ack_state();
assert_eq!(state.next_sequence_number().into_inner(), 0);
assert_eq!(state.next_sequence_number().into_inner(), 1);
assert_eq!(state.next_sequence_number().into_inner(), 2);
}
#[test]
fn should_send_at_startup() {
let state = ack_state();
let rtt = Duration::from_millis(100);
assert!(state.should_send_ack_frequency(rtt, &default_config(), &default_params()));
}
#[test]
fn should_not_send_after_sending_without_significant_change() {
let mut state = ack_state();
let rtt = Duration::from_millis(100);
state.ack_frequency_sent(1, Duration::from_millis(25));
state.next_outgoing_sequence_number.0 = 1;
assert!(!state.should_send_ack_frequency(rtt, &default_config(), &default_params()));
}
#[test]
fn candidate_uses_peer_delay_when_no_config() {
let state = ack_state();
let rtt = Duration::from_millis(100);
let delay = state.candidate_max_ack_delay(rtt, &default_config(), &default_params());
assert_eq!(delay, Duration::from_millis(25));
}
#[test]
fn candidate_uses_config_when_set() {
let state = ack_state();
let mut config = default_config();
config.max_ack_delay = Some(Duration::from_millis(50));
let rtt = Duration::from_millis(100);
let delay = state.candidate_max_ack_delay(rtt, &config, &default_params());
assert_eq!(delay, Duration::from_millis(50));
}
#[test]
fn pto_delay_equals_peer_delay_when_no_in_flight() {
let state = ack_state();
assert_eq!(state.max_ack_delay_for_pto(), Duration::from_millis(25));
}
#[test]
fn pto_delay_takes_max_when_in_flight() {
let mut state = ack_state();
state.ack_frequency_sent(1, Duration::from_millis(100));
assert_eq!(state.max_ack_delay_for_pto(), Duration::from_millis(100));
}
#[test]
fn on_acked_matching_pn_updates_peer_delay() {
let mut state = ack_state();
state.ack_frequency_sent(42, Duration::from_millis(100));
assert!(state.in_flight_ack_frequency_frame.is_some());
state.on_acked(42);
assert!(state.in_flight_ack_frequency_frame.is_none());
assert_eq!(state.peer_max_ack_delay, Duration::from_millis(100));
}
#[test]
fn on_acked_non_matching_pn_noop() {
let mut state = ack_state();
state.ack_frequency_sent(42, Duration::from_millis(100));
state.on_acked(99);
assert!(state.in_flight_ack_frequency_frame.is_some());
assert_eq!(state.peer_max_ack_delay, Duration::from_millis(25));
}
#[test]
fn sent_tracks_pn_and_delay() {
let mut state = ack_state();
state.ack_frequency_sent(10, Duration::from_millis(50));
assert_eq!(
state.in_flight_ack_frequency_frame,
Some((10, Duration::from_millis(50)))
);
}
#[test]
fn min_automatic_ack_delay_is_25ms() {
assert_eq!(MIN_AUTOMATIC_ACK_DELAY, Duration::from_millis(25));
}
#[test]
fn max_rtt_error_is_20_percent() {
assert!((MAX_RTT_ERROR - 0.2).abs() < f32::EPSILON);
}
}