1use core::time::Duration;
2
3#[derive(Debug, Clone, Copy)]
5#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
6pub struct SseRetryConfig {
7 pub max_retries: u32,
9 pub max_backoff_ms: u32,
11 pub min_sleep_ms: u32,
13 pub backoff_multiplier: f32,
15 pub jitter: bool,
17}
18
19impl SseRetryConfig {
20 #[inline]
35 #[must_use]
36 pub const fn new() -> Self {
37 Self {
38 max_retries: 20,
39 max_backoff_ms: 60_000,
40 min_sleep_ms: 200,
41 backoff_multiplier: 2.0,
42 jitter: true,
43 }
44 }
45
46 #[inline]
48 #[must_use]
49 pub const fn disabled() -> Self {
50 Self {
51 max_retries: 0,
52 ..Self::new()
53 }
54 }
55
56 #[must_use]
60 pub fn calculate_backoff_with_factor(
61 &self,
62 reconnect_time_ms: u32,
63 attempt: u32,
64 jitter_factor: f32,
65 ) -> Option<Duration> {
66 if self.max_retries <= attempt {
67 return None;
68 }
69
70 let reconnect_time_ms = reconnect_time_ms.max(self.min_sleep_ms) as f32;
71 let mut sleep_ms =
72 match self.backoff_multiplier.is_finite() && 1.0 <= self.backoff_multiplier {
73 true => reconnect_time_ms * self.backoff_multiplier.powi(attempt as _),
74 false => reconnect_time_ms,
75 };
76
77 if !sleep_ms.is_finite() || (self.max_backoff_ms as f32) <= sleep_ms {
78 sleep_ms = self.max_backoff_ms as _;
79 }
80
81 if self.jitter && reconnect_time_ms < sleep_ms {
82 let jitter_factor =
83 match jitter_factor.is_finite() && (0.0..=1.0).contains(&jitter_factor) {
84 true => jitter_factor,
85 false => 1.0,
86 };
87 sleep_ms = reconnect_time_ms + jitter_factor * (sleep_ms - reconnect_time_ms)
88 }
89
90 Some(Duration::from_millis(sleep_ms as _))
91 }
92
93 #[must_use]
97 #[cfg(feature = "fastrand")]
98 pub fn calculate_backoff(&self, reconnect_time_ms: u32, attempt: u32) -> Option<Duration> {
99 self.calculate_backoff_with_factor(reconnect_time_ms, attempt, fastrand::f32())
100 }
101}
102
103impl Default for SseRetryConfig {
104 fn default() -> Self {
105 Self::new()
106 }
107}