use std::time::Duration;
pub const KEEP_ALIVE_INTERVAL_MIN: Duration = Duration::from_secs(15);
pub const KEEP_ALIVE_INTERVAL_MAX: Duration = Duration::from_secs(30);
pub const KEEP_ALIVE_RESPONSE_DEADLINE: Duration = Duration::from_secs(20);
pub const DEAD_SOCKET_TIME: Duration = Duration::from_secs(20);
pub fn ms_since(timestamp_ms: u64) -> Option<u64> {
if timestamp_ms == 0 {
return None;
}
let now = crate::time::now_millis().max(0) as u64;
Some(now.saturating_sub(timestamp_ms))
}
pub fn is_dead_socket(last_sent_ms: u64, last_received_ms: u64) -> bool {
if last_sent_ms == 0 {
return false;
}
if last_received_ms >= last_sent_ms {
return false;
}
ms_since(last_sent_ms)
.map(|elapsed| elapsed > DEAD_SOCKET_TIME.as_millis() as u64)
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ms_since_never_set() {
assert_eq!(ms_since(0), None);
}
#[test]
fn ms_since_recent() {
let now_ms = crate::time::now_millis().max(0) as u64;
let elapsed = ms_since(now_ms).unwrap();
assert!(elapsed < 100, "should be near-zero, got {elapsed}ms");
}
#[test]
fn ms_since_stale() {
let thirty_sec_ago = (crate::time::now_millis().max(0) as u64).saturating_sub(30_000);
let elapsed = ms_since(thirty_sec_ago).unwrap();
assert!(
(29_000..=31_000).contains(&elapsed),
"should be ~30s, got {elapsed}ms"
);
}
#[test]
fn dead_socket_never_sent() {
assert!(!is_dead_socket(0, 0));
}
#[test]
fn dead_socket_received_after_send() {
let t = crate::time::now_millis().max(0) as u64;
assert!(!is_dead_socket(t, t + 1));
}
#[test]
fn dead_socket_sent_recently() {
let now = crate::time::now_millis().max(0) as u64;
assert!(!is_dead_socket(now, 0));
}
#[test]
fn dead_socket_sent_long_ago_no_reply() {
let thirty_ago = (crate::time::now_millis().max(0) as u64).saturating_sub(30_000);
assert!(is_dead_socket(thirty_ago, 0));
}
#[test]
fn dead_socket_sent_long_ago_old_reply() {
let thirty_ago = (crate::time::now_millis().max(0) as u64).saturating_sub(30_000);
let thirty_one_ago = thirty_ago.saturating_sub(1_000);
assert!(is_dead_socket(thirty_ago, thirty_one_ago));
}
#[test]
fn dead_socket_sent_long_ago_recent_reply() {
let thirty_ago = (crate::time::now_millis().max(0) as u64).saturating_sub(30_000);
let one_ago = (crate::time::now_millis().max(0) as u64).saturating_sub(1_000);
assert!(!is_dead_socket(thirty_ago, one_ago));
}
#[test]
fn constants_match_wa_web() {
assert_eq!(KEEP_ALIVE_INTERVAL_MIN, Duration::from_secs(15));
assert_eq!(KEEP_ALIVE_INTERVAL_MAX, Duration::from_secs(30));
assert_eq!(DEAD_SOCKET_TIME, Duration::from_secs(20));
}
}