// The gRPC client keepalive state machine (← gRPC `keepalive_time` / `keepalive_timeout`): keep a
// long-lived connection healthy by sending a PING when it has been idle, and declaring it dead if
// the PING's ACK does not come back in time. This is the pure decision core — it takes the current
// time and answers "ping now?" / "dead now?"; the Channel drives it, sending the actual HTTP/2 PING
// frame and closing the connection. Time is in the caller's unit (the same clock passed throughout).
///|
/// Keepalive state for one connection: the idle threshold that triggers a PING, the ACK deadline
/// that declares the connection dead, and the timers tracking the last activity and any in-flight
/// PING.
pub struct Keepalive {
keepalive_time : Int64
keepalive_timeout : Int64
mut last_activity : Int64
mut ping_outstanding : Bool
mut ping_sent_at : Int64
}
///|
/// A keepalive that pings after `keepalive_time` of idleness and gives a PING `keepalive_timeout`
/// to be acknowledged. `now` seeds the activity clock.
pub fn Keepalive::new(
keepalive_time~ : Int64,
keepalive_timeout~ : Int64,
now? : Int64 = 0L,
) -> Keepalive {
{
keepalive_time,
keepalive_timeout,
last_activity: now,
ping_outstanding: false,
ping_sent_at: 0L,
}
}
///|
/// Record connection activity (a frame sent or received) at `now`, resetting the idle timer.
pub fn Keepalive::on_activity(self : Keepalive, now : Int64) -> Unit {
self.last_activity = now
}
///|
/// Whether a keepalive PING should be sent at `now`: the connection has been idle at least
/// `keepalive_time` and no PING is already awaiting its ACK.
pub fn Keepalive::should_ping(self : Keepalive, now : Int64) -> Bool {
!self.ping_outstanding && now - self.last_activity >= self.keepalive_time
}
///|
/// Record that a keepalive PING was sent at `now`.
pub fn Keepalive::on_ping_sent(self : Keepalive, now : Int64) -> Unit {
self.ping_outstanding = true
self.ping_sent_at = now
}
///|
/// Record that the PING's ACK arrived at `now` — the connection is alive, and this counts as
/// activity.
pub fn Keepalive::on_ping_ack(self : Keepalive, now : Int64) -> Unit {
self.ping_outstanding = false
self.last_activity = now
}
///|
/// Whether the connection is dead at `now`: a PING is outstanding and its ACK has not arrived within
/// `keepalive_timeout` of sending it.
pub fn Keepalive::is_timed_out(self : Keepalive, now : Int64) -> Bool {
self.ping_outstanding && now - self.ping_sent_at >= self.keepalive_timeout
}