mqtt5_protocol/
keepalive.rs1use crate::time::Duration;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub struct KeepaliveConfig {
5 pub ping_interval_percent: u8,
6 pub timeout_percent: u8,
7}
8
9impl Default for KeepaliveConfig {
10 fn default() -> Self {
11 Self {
12 ping_interval_percent: 75,
13 timeout_percent: 150,
14 }
15 }
16}
17
18impl KeepaliveConfig {
19 #[must_use]
20 pub const fn new(ping_interval_percent: u8, timeout_percent: u8) -> Self {
21 Self {
22 ping_interval_percent,
23 timeout_percent,
24 }
25 }
26
27 #[must_use]
28 pub const fn conservative() -> Self {
29 Self {
30 ping_interval_percent: 50,
31 timeout_percent: 150,
32 }
33 }
34
35 #[must_use]
36 #[allow(
37 clippy::cast_possible_truncation,
38 clippy::cast_sign_loss,
39 clippy::cast_precision_loss
40 )]
41 pub fn ping_interval(&self, keepalive: Duration) -> Duration {
42 let millis = keepalive.as_millis() as u64;
43 let ping_millis = millis * u64::from(self.ping_interval_percent) / 100;
44 Duration::from_millis(ping_millis)
45 }
46
47 #[must_use]
48 #[allow(
49 clippy::cast_possible_truncation,
50 clippy::cast_sign_loss,
51 clippy::cast_precision_loss
52 )]
53 pub fn timeout_duration(&self, keepalive: Duration) -> Duration {
54 let millis = keepalive.as_millis() as u64;
55 let timeout_millis = millis * u64::from(self.timeout_percent) / 100;
56 Duration::from_millis(timeout_millis)
57 }
58}
59
60#[must_use]
61#[allow(clippy::cast_possible_truncation)]
62pub fn calculate_ping_interval(keepalive: Duration, percent: u8) -> Duration {
63 let millis = keepalive.as_millis() as u64;
64 let ping_millis = millis * u64::from(percent) / 100;
65 Duration::from_millis(ping_millis)
66}
67
68#[must_use]
69pub fn is_keepalive_timeout(
70 time_since_last_ping: Duration,
71 last_pong_received: bool,
72 keepalive: Duration,
73 timeout_percent: u8,
74) -> bool {
75 let config = KeepaliveConfig::new(0, timeout_percent);
76 let timeout = config.timeout_duration(keepalive);
77 !last_pong_received && time_since_last_ping > timeout
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn test_default_config() {
86 let config = KeepaliveConfig::default();
87 assert_eq!(config.ping_interval_percent, 75);
88 assert_eq!(config.timeout_percent, 150);
89 }
90
91 #[test]
92 fn test_conservative_config() {
93 let config = KeepaliveConfig::conservative();
94 assert_eq!(config.ping_interval_percent, 50);
95 assert_eq!(config.timeout_percent, 150);
96 }
97
98 #[test]
99 fn test_ping_interval_calculation() {
100 let config = KeepaliveConfig::default();
101 let keepalive = Duration::from_secs(60);
102 let ping_interval = config.ping_interval(keepalive);
103 assert_eq!(ping_interval, Duration::from_secs(45));
104 }
105
106 #[test]
107 fn test_ping_interval_50_percent() {
108 let config = KeepaliveConfig::conservative();
109 let keepalive = Duration::from_secs(60);
110 let ping_interval = config.ping_interval(keepalive);
111 assert_eq!(ping_interval, Duration::from_secs(30));
112 }
113
114 #[test]
115 fn test_timeout_duration() {
116 let config = KeepaliveConfig::default();
117 let keepalive = Duration::from_secs(60);
118 let timeout = config.timeout_duration(keepalive);
119 assert_eq!(timeout, Duration::from_secs(90));
120 }
121
122 #[test]
123 fn test_calculate_ping_interval_function() {
124 let keepalive = Duration::from_secs(60);
125 assert_eq!(
126 calculate_ping_interval(keepalive, 75),
127 Duration::from_secs(45)
128 );
129 assert_eq!(
130 calculate_ping_interval(keepalive, 50),
131 Duration::from_secs(30)
132 );
133 }
134
135 #[test]
136 fn test_is_keepalive_timeout_no_pong() {
137 let keepalive = Duration::from_secs(60);
138 let time_since_ping = Duration::from_secs(100);
139 assert!(is_keepalive_timeout(time_since_ping, false, keepalive, 150));
140 }
141
142 #[test]
143 fn test_is_keepalive_timeout_with_pong() {
144 let keepalive = Duration::from_secs(60);
145 let time_since_ping = Duration::from_secs(100);
146 assert!(!is_keepalive_timeout(time_since_ping, true, keepalive, 150));
147 }
148
149 #[test]
150 fn test_is_keepalive_timeout_not_expired() {
151 let keepalive = Duration::from_secs(60);
152 let time_since_ping = Duration::from_secs(80);
153 assert!(!is_keepalive_timeout(
154 time_since_ping,
155 false,
156 keepalive,
157 150
158 ));
159 }
160}