#[cfg(feature = "alloc")]
use crate::application::ServerName;
use crate::{
ack,
event::{api::SocketAddress, IntoEvent},
inet, recovery, stream,
transport::parameters::{
AckDelayExponent, ActiveConnectionIdLimit, InitialFlowControlLimits, InitialMaxData,
InitialMaxStreamDataBidiLocal, InitialMaxStreamDataBidiRemote, InitialMaxStreamDataUni,
InitialMaxStreamsBidi, InitialMaxStreamsUni, InitialStreamLimits, MaxAckDelay,
MaxDatagramFrameSize, MaxIdleTimeout, MigrationSupport, TransportParameters,
},
};
#[cfg(feature = "alloc")]
use bytes::Bytes;
use core::time::Duration;
use s2n_codec::decoder_invariant;
pub use crate::transport::parameters::ValidationError;
const MAX_HANDSHAKE_DURATION_DEFAULT: Duration = Duration::from_secs(10);
const MAX_KEEP_ALIVE_PERIOD_DEFAULT: Duration = Duration::from_secs(30);
pub const ANTI_AMPLIFICATION_MULTIPLIER: u8 = 3;
pub const DEFAULT_STREAM_BATCH_SIZE: u8 = 1;
pub const DEFAULT_PACKET_BUFFER_SIZE: usize = 0;
pub const MAX_PTO_JITTER_PERCENTAGE: u8 = 50;
pub const DEFAULT_PTO_JITTER_PERCENTAGE: u8 = 0;
#[non_exhaustive]
#[derive(Debug)]
pub struct ConnectionInfo<'a> {
pub remote_address: SocketAddress<'a>,
}
impl<'a> ConnectionInfo<'a> {
#[inline]
#[doc(hidden)]
pub fn new(remote_address: &'a inet::SocketAddress) -> Self {
Self {
remote_address: remote_address.into_event(),
}
}
}
#[non_exhaustive]
#[derive(Debug)]
#[cfg(feature = "alloc")]
pub struct HandshakeInfo<'a> {
pub remote_address: SocketAddress<'a>,
pub server_name: Option<&'a ServerName>,
pub application_protocol: &'a Bytes,
}
#[cfg(feature = "alloc")]
impl<'a> HandshakeInfo<'a> {
pub fn new(
remote_address: &'a inet::SocketAddress,
server_name: Option<&'a ServerName>,
application_protocol: &'a Bytes,
) -> HandshakeInfo<'a> {
Self {
remote_address: remote_address.into_event(),
server_name,
application_protocol,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct Limits {
pub(crate) max_idle_timeout: MaxIdleTimeout,
pub(crate) data_window: InitialMaxData,
pub(crate) bidirectional_local_data_window: InitialMaxStreamDataBidiLocal,
pub(crate) bidirectional_remote_data_window: InitialMaxStreamDataBidiRemote,
pub(crate) unidirectional_data_window: InitialMaxStreamDataUni,
pub(crate) max_open_local_bidirectional_streams: stream::limits::LocalBidirectional,
pub(crate) max_open_local_unidirectional_streams: stream::limits::LocalUnidirectional,
pub(crate) max_open_remote_bidirectional_streams: InitialMaxStreamsBidi,
pub(crate) max_open_remote_unidirectional_streams: InitialMaxStreamsUni,
pub(crate) max_ack_delay: MaxAckDelay,
pub(crate) ack_delay_exponent: AckDelayExponent,
pub(crate) max_active_connection_ids: ActiveConnectionIdLimit,
pub(crate) ack_elicitation_interval: u8,
pub(crate) ack_ranges_limit: u8,
pub(crate) max_send_buffer_size: stream::limits::MaxSendBufferSize,
pub(crate) max_handshake_duration: Duration,
pub(crate) max_keep_alive_period: Duration,
pub(crate) max_datagram_frame_size: MaxDatagramFrameSize,
pub(crate) initial_round_trip_time: Duration,
pub(crate) migration_support: MigrationSupport,
pub(crate) anti_amplification_multiplier: u8,
pub(crate) stream_batch_size: u8,
pub(crate) pto_jitter_percentage: u8,
pub(crate) packet_buffer_size: usize,
}
impl Default for Limits {
fn default() -> Self {
Self::new()
}
}
macro_rules! setter {
($(#[doc = $doc:literal])* $name:ident, $field:ident, $inner:ty $(, |$validate_value:ident| $validation:block)?) => {
$(#[doc = $doc])*
pub fn $name(mut self, value: $inner) -> Result<Self, ValidationError> {
$(
let $validate_value = value;
$validation
)?
self.$field = value.try_into()?;
Ok(self)
}
};
}
impl Limits {
pub const fn new() -> Self {
Self {
max_idle_timeout: MaxIdleTimeout::RECOMMENDED,
data_window: InitialMaxData::RECOMMENDED,
bidirectional_local_data_window: InitialMaxStreamDataBidiLocal::RECOMMENDED,
bidirectional_remote_data_window: InitialMaxStreamDataBidiRemote::RECOMMENDED,
unidirectional_data_window: InitialMaxStreamDataUni::RECOMMENDED,
max_open_local_bidirectional_streams: stream::limits::LocalBidirectional::RECOMMENDED,
max_open_local_unidirectional_streams: stream::limits::LocalUnidirectional::RECOMMENDED,
max_open_remote_bidirectional_streams: InitialMaxStreamsBidi::RECOMMENDED,
max_open_remote_unidirectional_streams: InitialMaxStreamsUni::RECOMMENDED,
max_ack_delay: MaxAckDelay::RECOMMENDED,
ack_delay_exponent: AckDelayExponent::RECOMMENDED,
max_active_connection_ids: ActiveConnectionIdLimit::RECOMMENDED,
ack_elicitation_interval: ack::Settings::RECOMMENDED.ack_elicitation_interval,
ack_ranges_limit: ack::Settings::RECOMMENDED.ack_ranges_limit,
max_send_buffer_size: stream::Limits::RECOMMENDED.max_send_buffer_size,
max_handshake_duration: MAX_HANDSHAKE_DURATION_DEFAULT,
max_keep_alive_period: MAX_KEEP_ALIVE_PERIOD_DEFAULT,
max_datagram_frame_size: MaxDatagramFrameSize::DEFAULT,
initial_round_trip_time: recovery::DEFAULT_INITIAL_RTT,
migration_support: MigrationSupport::RECOMMENDED,
anti_amplification_multiplier: ANTI_AMPLIFICATION_MULTIPLIER,
stream_batch_size: DEFAULT_STREAM_BATCH_SIZE,
pto_jitter_percentage: DEFAULT_PTO_JITTER_PERCENTAGE,
packet_buffer_size: DEFAULT_PACKET_BUFFER_SIZE,
}
}
setter!(with_data_window, data_window, u64, |validate_value| {
decoder_invariant!(
validate_value <= u32::MAX.into(),
"data_window must be <= u32::MAX"
);
});
setter!(
with_bidirectional_local_data_window,
bidirectional_local_data_window,
u64,
|validate_value| {
decoder_invariant!(
validate_value <= u32::MAX.into(),
"bidirectional_local_data_window must be <= u32::MAX"
);
}
);
setter!(
with_bidirectional_remote_data_window,
bidirectional_remote_data_window,
u64,
|validate_value| {
decoder_invariant!(
validate_value <= u32::MAX.into(),
"bidirectional_remote_data_window must be <= u32::MAX"
);
}
);
setter!(
with_unidirectional_data_window,
unidirectional_data_window,
u64,
|validate_value| {
decoder_invariant!(
validate_value <= u32::MAX.into(),
"unidirectional_data_window must be <= u32::MAX"
);
}
);
setter!(
with_max_idle_timeout,
max_idle_timeout,
Duration);
#[deprecated(
note = "use with_max_open_local_bidirectional_streams and with_max_open_remote_bidirectional_streams instead"
)]
pub fn with_max_open_bidirectional_streams(
mut self,
value: u64,
) -> Result<Self, ValidationError> {
self.max_open_local_bidirectional_streams = value.try_into()?;
self.max_open_remote_bidirectional_streams = value.try_into()?;
Ok(self)
}
pub fn with_max_open_local_bidirectional_streams(
mut self,
value: u64,
) -> Result<Self, ValidationError> {
self.max_open_local_bidirectional_streams = value.try_into()?;
Ok(self)
}
pub fn with_max_open_remote_bidirectional_streams(
mut self,
value: u64,
) -> Result<Self, ValidationError> {
self.max_open_remote_bidirectional_streams = value.try_into()?;
Ok(self)
}
setter!(
with_max_open_local_unidirectional_streams,
max_open_local_unidirectional_streams,
u64
);
setter!(
with_max_open_remote_unidirectional_streams,
max_open_remote_unidirectional_streams,
u64
);
setter!(with_max_ack_delay, max_ack_delay, Duration);
setter!(
with_max_active_connection_ids,
max_active_connection_ids,
u64
);
setter!(with_stream_batch_size, stream_batch_size, u8);
setter!(
with_packet_buffer_size,
packet_buffer_size,
u32,
|validate_value| {
decoder_invariant!(
validate_value <= 10_000,
"Cannot ever store more than 10,000 bytes across packets"
);
}
);
setter!(with_ack_elicitation_interval, ack_elicitation_interval, u8);
setter!(with_max_ack_ranges, ack_ranges_limit, u8);
setter!(
with_max_send_buffer_size,
max_send_buffer_size,
u32
);
setter!(
with_max_handshake_duration,
max_handshake_duration,
Duration
);
setter!(
with_max_keep_alive_period,
max_keep_alive_period,
Duration);
pub fn with_active_connection_migration(
mut self,
enabled: bool,
) -> Result<Self, ValidationError> {
if enabled {
self.migration_support = MigrationSupport::Enabled
} else {
self.migration_support = MigrationSupport::Disabled
}
Ok(self)
}
pub fn with_initial_round_trip_time(
mut self,
value: Duration,
) -> Result<Self, ValidationError> {
ensure!(
value >= recovery::MIN_RTT,
Err(ValidationError(
"provided value must be at least 1 microsecond",
))
);
self.initial_round_trip_time = value;
Ok(self)
}
#[cfg(feature = "unstable-limits")]
setter!(
with_anti_amplification_multiplier,
anti_amplification_multiplier,
u8
);
pub fn with_pto_jitter_percentage(mut self, value: u8) -> Result<Self, ValidationError> {
ensure!(
value <= MAX_PTO_JITTER_PERCENTAGE,
Err(ValidationError(
"PTO jitter percentage must be between 0 and 50"
))
);
self.pto_jitter_percentage = value;
Ok(self)
}
#[doc(hidden)]
#[inline]
pub fn pto_jitter_percentage(&self) -> u8 {
self.pto_jitter_percentage
}
#[doc(hidden)]
#[inline]
pub fn load_peer<A, B, C, D>(&mut self, peer_parameters: &TransportParameters<A, B, C, D>) {
self.max_idle_timeout
.load_peer(&peer_parameters.max_idle_timeout);
}
#[doc(hidden)]
#[inline]
pub const fn ack_settings(&self) -> ack::Settings {
ack::Settings {
ack_delay_exponent: self.ack_delay_exponent.as_u8(),
max_ack_delay: self.max_ack_delay.as_duration(),
ack_ranges_limit: self.ack_ranges_limit,
ack_elicitation_interval: self.ack_elicitation_interval,
}
}
#[doc(hidden)]
#[inline]
pub const fn initial_flow_control_limits(&self) -> InitialFlowControlLimits {
InitialFlowControlLimits {
stream_limits: self.initial_stream_limits(),
max_data: self.data_window.as_varint(),
max_open_remote_bidirectional_streams: self
.max_open_remote_bidirectional_streams
.as_varint(),
max_open_remote_unidirectional_streams: self
.max_open_remote_unidirectional_streams
.as_varint(),
}
}
#[doc(hidden)]
#[inline]
pub const fn initial_stream_limits(&self) -> InitialStreamLimits {
InitialStreamLimits {
max_data_bidi_local: self.bidirectional_local_data_window.as_varint(),
max_data_bidi_remote: self.bidirectional_remote_data_window.as_varint(),
max_data_uni: self.unidirectional_data_window.as_varint(),
}
}
#[doc(hidden)]
#[inline]
pub fn stream_limits(&self) -> stream::Limits {
stream::Limits {
max_send_buffer_size: self.max_send_buffer_size,
max_open_local_unidirectional_streams: self.max_open_local_unidirectional_streams,
max_open_local_bidirectional_streams: self.max_open_local_bidirectional_streams,
}
}
#[doc(hidden)]
#[inline]
pub fn max_idle_timeout(&self) -> Option<Duration> {
self.max_idle_timeout.as_duration()
}
#[doc(hidden)]
#[inline]
pub fn max_handshake_duration(&self) -> Duration {
self.max_handshake_duration
}
#[doc(hidden)]
#[inline]
pub fn max_keep_alive_period(&self) -> Duration {
self.max_keep_alive_period
}
#[doc(hidden)]
#[inline]
pub fn initial_round_trip_time(&self) -> Duration {
self.initial_round_trip_time
}
#[doc(hidden)]
#[inline]
pub fn active_migration_enabled(&self) -> bool {
matches!(self.migration_support, MigrationSupport::Enabled)
}
#[doc(hidden)]
#[inline]
pub fn anti_amplification_multiplier(&self) -> u8 {
self.anti_amplification_multiplier
}
#[doc(hidden)]
#[inline]
pub fn stream_batch_size(&self) -> u8 {
self.stream_batch_size
}
#[doc(hidden)]
#[inline]
pub fn packet_buffer_size(&self) -> usize {
self.packet_buffer_size
}
}
#[must_use]
#[derive(Debug)]
pub struct UpdatableLimits<'a>(&'a mut Limits);
impl<'a> UpdatableLimits<'a> {
pub fn new(limits: &'a mut Limits) -> UpdatableLimits<'a> {
UpdatableLimits(limits)
}
pub fn with_stream_batch_size(&mut self, size: u8) {
self.0.stream_batch_size = size;
}
}
pub trait Limiter: 'static + Send {
fn on_connection(&mut self, info: &ConnectionInfo) -> Limits;
#[inline]
#[cfg(feature = "alloc")]
fn on_post_handshake(&mut self, info: &HandshakeInfo, limits: &mut UpdatableLimits) {
let _ = info;
let _ = limits;
}
}
impl Limiter for Limits {
fn on_connection(&mut self, _into: &ConnectionInfo) -> Limits {
*self
}
#[cfg(feature = "alloc")]
fn on_post_handshake(&mut self, _info: &HandshakeInfo, _limits: &mut UpdatableLimits) {}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn limit_validation() {
let mut data = u32::MAX as u64 + 1;
let limits = Limits::default();
assert!(limits.with_data_window(data).is_err());
assert!(limits.with_bidirectional_local_data_window(data).is_err());
assert!(limits.with_bidirectional_remote_data_window(data).is_err());
assert!(limits.with_unidirectional_data_window(data).is_err());
data = u32::MAX as u64;
assert!(limits.with_data_window(data).is_ok());
assert!(limits.with_bidirectional_local_data_window(data).is_ok());
assert!(limits.with_bidirectional_remote_data_window(data).is_ok());
assert!(limits.with_unidirectional_data_window(data).is_ok());
}
#[test]
fn updatable_limits() {
let mut limits = Limits::default();
assert_eq!(limits.stream_batch_size, 1);
let mut updatable_limits = UpdatableLimits::new(&mut limits);
let new_size = 10;
updatable_limits.with_stream_batch_size(new_size);
assert_eq!(limits.stream_batch_size, new_size);
}
#[test]
fn pto_jitter_percentage_default() {
let limits = Limits::new();
assert_eq!(
limits.pto_jitter_percentage(),
DEFAULT_PTO_JITTER_PERCENTAGE
);
let limits = Limits::default();
assert_eq!(
limits.pto_jitter_percentage(),
DEFAULT_PTO_JITTER_PERCENTAGE
);
}
#[test]
fn pto_jitter_percentage_valid_values() {
let limits = Limits::new();
for value in 0..=MAX_PTO_JITTER_PERCENTAGE {
let result = limits.with_pto_jitter_percentage(value);
assert!(result.is_ok(), "Value {} should be valid", value);
let limits = result.unwrap();
assert_eq!(limits.pto_jitter_percentage(), value);
}
}
#[test]
fn pto_jitter_percentage_invalid_values() {
let limits = Limits::new();
for value in (MAX_PTO_JITTER_PERCENTAGE + 1)..=255 {
let result = limits.with_pto_jitter_percentage(value);
assert!(result.is_err(), "Value {} should be invalid", value);
if let Err(ValidationError(msg)) = result {
assert_eq!(msg, "PTO jitter percentage must be between 0 and 50");
} else {
panic!("Expected ValidationError for value {}", value);
}
}
}
#[test]
fn pto_jitter_percentage_edge_cases() {
let limits = Limits::new();
let result = limits.with_pto_jitter_percentage(0);
assert!(result.is_ok());
assert_eq!(result.unwrap().pto_jitter_percentage(), 0);
let result = limits.with_pto_jitter_percentage(MAX_PTO_JITTER_PERCENTAGE);
assert!(result.is_ok());
assert_eq!(
result.unwrap().pto_jitter_percentage(),
MAX_PTO_JITTER_PERCENTAGE
);
let result = limits.with_pto_jitter_percentage(MAX_PTO_JITTER_PERCENTAGE + 1);
assert!(result.is_err());
}
#[test]
fn pto_jitter_percentage_chaining() {
let result = Limits::new()
.with_pto_jitter_percentage(25)
.and_then(|l| l.with_stream_batch_size(5));
assert!(result.is_ok());
let limits = result.unwrap();
assert_eq!(limits.pto_jitter_percentage(), 25);
assert_eq!(limits.stream_batch_size(), 5);
}
#[test]
fn pto_jitter_percentage_getter() {
let mut limits = Limits::new();
assert_eq!(limits.pto_jitter_percentage(), 0);
limits = limits.with_pto_jitter_percentage(30).unwrap();
assert_eq!(limits.pto_jitter_percentage(), 30);
limits = limits.with_pto_jitter_percentage(15).unwrap();
assert_eq!(limits.pto_jitter_percentage(), 15);
}
}