Skip to main content

Module session_timer

Module session_timer 

Source
Expand description

RFC 4028 session timers — keep long calls from outliving a dead dialog.

Without session timers, a call whose dialog silently died (peer crashed, NAT binding dropped, proxy lost state) lives forever: nothing on the signaling path re-validates the dialog, so an unattended consumer (voice bot, AI agent) keeps streaming RTP into the void. RFC 4028 bounds that window: one side periodically refreshes the session with a re-INVITE, and the other side tears the call down with BYE if no refresh arrives before the negotiated session interval lapses.

This module has three layers:

  1. Pure header logicSessionExpires parse/build for the Session-Expires header (with its ;refresher=uac|uas param), min_se_in for Min-SE, and supports_timer for the Supported: timer option tag. rsip 0.4 has no typed variants for these, so they are parsed manually from rsip::Header::Other — same style as the crate’s SDP and RTP parsing.
  2. Negotiationnegotiate_uac (from a 2xx response, caller side) and negotiate_uas (from an INVITE, callee side) decide the SessionTimer: the negotiated interval and whether we are the refresher. SessionTimer::refresh_after / SessionTimer::expiry_after give the RFC 4028 §10 schedule.
  3. Runtimesession_timer_loop, shaped like crate::Registrar::keepalive_loop: a select! over sleeps and a CancellationToken that either sends the periodic refresh re-INVITE (when we are the refresher) or watches for the peer’s refreshes and sends BYE when the session lapses.

§Wiring it up

crate::Caller and crate::IncomingCall negotiate for you: crate::Call::session_timer carries the result. Drive the loop against the call’s shareable dialog handle (crate::Call::session_handle) so it runs alongside the audio path:

if let Some(timer) = call.session_timer() {
    let handle = call.session_handle();
    let refreshed = std::sync::Arc::new(tokio::sync::Notify::new());
    let sdp = build_sdp(local_ip, rtp_port); // repeat our offer
    tokio::spawn(async move {
        let outcome =
            session_timer_loop(&handle, timer, Some(sdp), refreshed, cancel).await;
        tracing::info!(?outcome, "session timer finished");
    });
}

When the peer is the refresher, its refresh re-INVITEs arrive as inbound in-dialog requests. The consumer answers them with an SDP answer (echoing Session-Expires) and pings refreshed so the watchdog deadline resets.

Structs§

SessionExpires
Parsed Session-Expires header value: interval in seconds plus the optional refresher parameter.
SessionTimer
Negotiated session-timer state for one dialog, from our side’s perspective.
UasSessionTimer
UAS-side negotiation result: the timer to run plus what to put in our 2xx so the peer agrees on it.

Enums§

Refresher
Which side of the original INVITE transaction performs refreshes — the value space of the Session-Expires refresher parameter.
SessionTimerOutcome
How session_timer_loop ended.

Constants§

DEFAULT_SESSION_EXPIRES_SECS
Session interval requested on outbound INVITEs, in seconds — the RFC 4028 recommended default of 30 minutes.
MIN_SESSION_EXPIRES_SECS
Smallest session interval we will run a timer at, in seconds. RFC 4028 §4 fixes 90 s as the absolute minimum (and the default Min-SE); anything shorter would churn re-INVITEs.

Traits§

SessionDialogOps
The dialog operations session_timer_loop needs, abstracted so the loop’s timing logic stays unit-testable without a live dialog.

Functions§

min_se_in
Extract the Min-SE interval (seconds) from a header list, ignoring any generic parameters. Malformed values are treated as absent.
negotiate_uac
UAC-side negotiation: decide the SessionTimer from the 2xx response to our INVITE.
negotiate_uas
UAS-side negotiation: decide the session timer from an inbound INVITE’s headers.
require_timer_header
Require: timer — placed in a 2xx by the answerer when the offerer advertised timer support (RFC 4028 §9).
session_expires_in
Extract and parse the Session-Expires header (long or compact x form) from a header list. Malformed values are logged and treated as absent — a broken peer header must not kill the call.
session_timer_loop
Drive RFC 4028 session keepalive for one confirmed dialog. Runs until cancelled or the session dies; same shape as crate::Registrar::keepalive_loop.
supported_timer_header
Supported: timer — advertise RFC 4028 support on a request or response.
supports_timer
true if any Supported header (typed, untyped, or compact k form) carries the timer option tag.