1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use crate::{
transport::parameters::{AckDelayExponent, MaxAckDelay},
varint::VarInt,
};
use core::{convert::TryInto, time::Duration};
// After running simulations, this seemed to be a good baseline
// TODO experiment more with this
/// The recommended value for the ack_elicitation_interval setting
const RECOMMENDED_ELICITATION_INTERVAL: u8 = 4;
// TODO experiment more with this
/// The recommended number of packet number ranges that an endpoint should store
const RECOMMENDED_RANGES_LIMIT: u8 = 10;
/// Settings for ACK frames
#[derive(Clone, Copy, Debug)]
pub struct Settings {
/// The maximum ACK delay indicates the maximum amount of time by which the
/// endpoint will delay sending acknowledgments.
pub max_ack_delay: Duration,
/// The ACK delay exponent is an integer value indicating an exponent used
/// to decode the ACK Delay field in the ACK frame
pub ack_delay_exponent: u8,
//= https://www.rfc-editor.org/rfc/rfc9000#section-13.2.4
//# A receiver that sends only non-ack-eliciting packets, such as ACK
//# frames, might not receive an acknowledgement for a long period of
//# time. This could cause the receiver to maintain state for a large
//# number of ACK frames for a long period of time, and ACK frames it
//# sends could be unnecessarily large. In such a case, a receiver could
//# send a PING or other small ack-eliciting frame occasionally, such as
//# once per round trip, to elicit an ACK from the peer.
/// The number of packets received before sending an ACK-eliciting packet
pub ack_elicitation_interval: u8,
/// The number of packet number intervals an endpoint is willing to store
pub ack_ranges_limit: u8,
}
impl Default for Settings {
fn default() -> Self {
Self::RECOMMENDED
}
}
//= https://www.rfc-editor.org/rfc/rfc9000#section-19.3
//# ACK Delay: A variable-length integer encoding the acknowledgement
//# delay in microseconds; see Section 13.2.5. It is decoded by
//# multiplying the value in the field by 2 to the power of the
//# ack_delay_exponent transport parameter sent by the sender of the
//# ACK frame; see Section 18.2. Compared to simply expressing the
//# delay as an integer, this encoding allows for a larger range of
//# values within the same number of bytes, at the cost of lower
//# resolution.
impl Settings {
//= https://www.rfc-editor.org/rfc/rfc9000#section-13.2.1
//# An endpoint MUST acknowledge all ack-eliciting Initial and Handshake
//# packets immediately
pub const EARLY: Self = Self {
max_ack_delay: Duration::from_secs(0),
ack_delay_exponent: 0,
..Self::RECOMMENDED
};
pub const RECOMMENDED: Self = Self {
max_ack_delay: MaxAckDelay::RECOMMENDED.as_duration(),
ack_delay_exponent: AckDelayExponent::RECOMMENDED.as_u8(),
ack_elicitation_interval: RECOMMENDED_ELICITATION_INTERVAL,
ack_ranges_limit: RECOMMENDED_RANGES_LIMIT,
};
/// Decodes the peer's `Ack Delay` field
pub fn decode_ack_delay(&self, delay: VarInt) -> Duration {
Duration::from_micros(*delay) * self.scale()
}
/// Encodes the local `Ack Delay` field
pub fn encode_ack_delay(&self, delay: Duration) -> VarInt {
let micros = delay.as_micros();
let scale = self.scale() as u128;
(micros / scale).try_into().unwrap_or(VarInt::MAX)
}
/// Computes the scale from the exponent
fn scale(&self) -> u32 {
2u32.pow(self.ack_delay_exponent as u32)
}
}
#[cfg(test)]
mod ack_settings_tests {
use super::*;
#[test]
#[cfg_attr(miri, ignore)] // this test is too expensive for miri
fn ack_settings_test() {
for ack_delay_exponent in 0..=20 {
let settings = Settings {
ack_delay_exponent,
..Default::default()
};
// use an epsilon instead of comparing the values directly,
// as there will be some precision loss
let epsilon = settings.scale() as u128;
for delay in (0..1000).map(|v| v * 100).map(Duration::from_micros) {
let delay_varint = settings.encode_ack_delay(delay);
let expected_us = delay.as_micros();
let actual_us = settings.decode_ack_delay(delay_varint).as_micros();
let actual_difference = expected_us - actual_us;
assert!(actual_difference < epsilon);
}
// ensure MAX values are handled correctly and don't overflow
let delay = settings.decode_ack_delay(VarInt::MAX);
let delay_varint = settings.encode_ack_delay(delay);
assert_eq!(VarInt::MAX, delay_varint);
}
}
}