// Client keepalive as a pure state machine over a clock: send a PING once the connection
// has been idle for `keepalive_time`, and declare it dead if the ACK does not return within
// `keepalive_timeout`. This walks a millisecond timeline through a healthy ping/ack cycle
// and then a missed ACK that times the connection out — the decisions the Channel drives
// (it sends the real PING frame and closes the socket).
//
// moon run src/examples/22-keepalive
///|
fn run() -> Unit {
// Ping after 1000 ms idle; give the ACK 500 ms.
let ka = @moonrpc.Keepalive::new(
keepalive_time=1000L,
keepalive_timeout=500L,
now=0L,
)
println("keepalive: ping after 1000ms idle, ACK deadline 500ms")
println(" t=500 should_ping: \{ka.should_ping(500L)} (only 500ms idle)")
println(
" t=1000 should_ping: \{ka.should_ping(1000L)} (idle threshold reached)",
)
// Send the PING, get the ACK in time — the connection is healthy.
ka.on_ping_sent(1000L)
println(" t=1000 PING sent")
println(" t=1200 timed_out: \{ka.is_timed_out(1200L)} (ACK still has time)")
ka.on_ping_ack(1200L)
println(" t=1200 ACK received -> alive, idle timer reset")
println(
" t=1500 should_ping: \{ka.should_ping(1500L)} (idle timer restarted at ACK)",
)
// Real traffic keeps it idle-free; then it goes quiet again and a PING is due.
ka.on_activity(1800L)
println(" t=1800 activity recorded")
println(
" t=2800 should_ping: \{ka.should_ping(2800L)} (idle 1000ms since activity)",
)
ka.on_ping_sent(2800L)
println(" t=2800 PING sent, no ACK this time")
println(
" t=3200 timed_out: \{ka.is_timed_out(3200L)} (400ms < 500ms deadline)",
)
println(
" t=3300 timed_out: \{ka.is_timed_out(3300L)} (500ms deadline hit -> dead)",
)
}
///|
#coverage.skip
fn main {
run()
}