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:
- Pure header logic —
SessionExpiresparse/build for theSession-Expiresheader (with its;refresher=uac|uasparam),min_se_inforMin-SE, andsupports_timerfor theSupported: timeroption tag. rsip 0.4 has no typed variants for these, so they are parsed manually fromrsip::Header::Other— same style as the crate’s SDP and RTP parsing. - Negotiation —
negotiate_uac(from a 2xx response, caller side) andnegotiate_uas(from an INVITE, callee side) decide theSessionTimer: the negotiated interval and whether we are the refresher.SessionTimer::refresh_after/SessionTimer::expiry_aftergive the RFC 4028 §10 schedule. - Runtime —
session_timer_loop, shaped likecrate::Registrar::keepalive_loop: aselect!over sleeps and aCancellationTokenthat 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§
- Session
Expires - Parsed
Session-Expiresheader value: interval in seconds plus the optionalrefresherparameter. - Session
Timer - Negotiated session-timer state for one dialog, from our side’s perspective.
- UasSession
Timer - 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-Expiresrefresherparameter. - Session
Timer Outcome - How
session_timer_loopended.
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§
- Session
Dialog Ops - The dialog operations
session_timer_loopneeds, abstracted so the loop’s timing logic stays unit-testable without a live dialog.
Functions§
- min_
se_ in - Extract the
Min-SEinterval (seconds) from a header list, ignoring any generic parameters. Malformed values are treated as absent. - negotiate_
uac - UAC-side negotiation: decide the
SessionTimerfrom 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-Expiresheader (long or compactxform) 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 trueif anySupportedheader (typed, untyped, or compactkform) carries thetimeroption tag.