use crate::sip::headers::Require;
use crate::sip::parser::header::{Header as PHeader, Headers as PHeaders};
use crate::sip::{Method, SipRequest, SipResponse};
use crate::transaction::client::invite::TransactionId;
use crate::transaction::timer::{Timer, TimerValues};
use std::time::Duration;
fn stamp_reliable_headers(response: &mut SipResponse, rseq: u32) -> bytes::Bytes {
let inner = response.inner_mut();
let existing = std::mem::take(&mut inner.headers);
let mut new_headers = PHeaders::new();
let mut handled_require = false;
fn push(h: &mut PHeaders, header: PHeader) {
h.push(header)
.expect("response header count under MAX_HEADERS");
}
for header in existing {
match &header {
PHeader::Require(value) => {
if handled_require {
if let Ok(extra) = Require::parse(value) {
merge_into_last_require(&mut new_headers, &extra.0);
}
continue;
}
let merged = merge_require_with_100rel(value);
push(&mut new_headers, PHeader::Require(merged));
handled_require = true;
}
PHeader::Other(key, value) if key.eq_ignore_ascii_case("Require") => {
if handled_require {
if let Ok(extra) = Require::parse(value) {
merge_into_last_require(&mut new_headers, &extra.0);
}
continue;
}
let merged = merge_require_with_100rel(value);
push(&mut new_headers, PHeader::Require(merged));
handled_require = true;
}
_ => {
push(&mut new_headers, header);
}
}
}
if !handled_require {
push(&mut new_headers, PHeader::Require("100rel".to_string()));
}
debug_assert!(
!new_headers
.iter()
.any(|h| matches!(h, PHeader::Other(k, _) if k.eq_ignore_ascii_case("RSeq"))),
"stamp_reliable_headers: RSeq already present on TU-built response"
);
push(
&mut new_headers,
PHeader::Other("RSeq".to_string(), rseq.to_string()),
);
inner.headers = new_headers;
response.to_bytes()
}
fn merge_require_with_100rel(value: &str) -> String {
let parsed = Require::parse(value);
debug_assert!(
parsed.is_ok(),
"stamp_reliable_headers: TU set malformed Require value {value:?}"
);
let mut tags = parsed.map(|p| p.0).unwrap_or_default();
if !tags.iter().any(|t| t.eq_ignore_ascii_case("100rel")) {
tags.push("100rel".to_string());
}
tags.join(", ")
}
fn merge_into_last_require(headers: &mut PHeaders, extra_tags: &[String]) {
let drained: Vec<PHeader> = std::mem::take(headers).into_iter().collect();
let last_require_idx = drained.iter().enumerate().rev().find_map(|(i, h)| {
if matches!(h, PHeader::Require(_)) {
Some(i)
} else {
None
}
});
let mut rebuilt = PHeaders::new();
let push = |h: &mut PHeaders, header: PHeader| {
h.push(header).expect("rebuilt header count <= original");
};
for (i, header) in drained.into_iter().enumerate() {
if Some(i) == last_require_idx {
if let PHeader::Require(value) = &header {
let mut tags = Require::parse(value).map(|p| p.0).unwrap_or_default();
for tag in extra_tags {
if !tags.iter().any(|t| t.eq_ignore_ascii_case(tag)) {
tags.push(tag.clone());
}
}
push(&mut rebuilt, PHeader::Require(tags.join(", ")));
continue;
}
}
push(&mut rebuilt, header);
}
*headers = rebuilt;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum State {
Proceeding,
Completed,
Confirmed,
Terminated,
}
#[derive(Debug, Clone)]
pub enum Action {
Send(bytes::Bytes),
Event(Event),
SetTimer(Timer, Duration),
CancelTimer(Timer),
}
#[derive(Debug, Clone)]
pub enum Event {
Request(Box<SipRequest>),
AckReceived,
PrackReceived(u32),
PrackTimeout {
rseq: u32,
final_sent: FinalSent,
},
Timeout,
TransportError,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FinalSent {
None,
NonTwoXx,
TwoXx,
}
#[derive(Debug, Clone)]
struct ReliableProvisional {
rseq: u32,
bytes: bytes::Bytes,
next_fire_after: Duration,
next_interval: Duration,
elapsed: Duration,
abandon_at: Duration,
}
#[derive(Debug)]
pub struct InviteServerTransaction {
id: TransactionId,
state: State,
request: SipRequest,
last_response: Option<bytes::Bytes>,
timers: TimerValues,
reliable: bool,
retransmit_interval: Duration,
actions: Vec<Action>,
next_rseq: u32,
reliable_provisionals: Vec<ReliableProvisional>,
final_sent: FinalSent,
}
impl InviteServerTransaction {
pub fn new(request: SipRequest, reliable: bool) -> Option<Self> {
if request.method() != Method::Invite {
return None;
}
let id = TransactionId::from_request(&request)?;
let timers = TimerValues::default();
let mut tx = Self {
id,
state: State::Proceeding,
request: request.clone(),
last_response: None,
timers,
reliable,
retransmit_interval: timers.timer_g(),
actions: Vec::new(),
next_rseq: 1,
reliable_provisionals: Vec::new(),
final_sent: FinalSent::None,
};
tx.actions
.push(Action::Event(Event::Request(Box::new(request))));
Some(tx)
}
pub fn id(&self) -> &TransactionId {
&self.id
}
pub fn state(&self) -> State {
self.state
}
pub fn is_terminated(&self) -> bool {
self.state == State::Terminated
}
pub fn request(&self) -> &SipRequest {
&self.request
}
pub fn matches_invite_dialog(&self, call_id: &str, from_tag: &str) -> bool {
let req_call_id = match self.request.call_id() {
Ok(c) => c,
Err(_) => return false,
};
let req_from_tag = match self.request.from_tag() {
Ok(t) => t,
Err(_) => return false,
};
req_call_id == call_id && req_from_tag == from_tag
}
pub fn handle_timeout(&mut self, timer: Timer) {
match (self.state, timer) {
(State::Completed, Timer::G) => {
if let Some(ref resp) = self.last_response {
self.actions.push(Action::Send(resp.clone()));
}
self.retransmit_interval = self.timers.next_retransmit(self.retransmit_interval);
self.actions
.push(Action::SetTimer(Timer::G, self.retransmit_interval));
}
(State::Completed, Timer::H) => {
self.state = State::Terminated;
self.actions.push(Action::Event(Event::Timeout));
}
(State::Confirmed, Timer::I) => {
self.state = State::Terminated;
}
(_, Timer::N) => {
self.fire_timer_n();
}
_ => {
}
}
}
fn fire_timer_n(&mut self) {
if self.reliable_provisionals.is_empty() {
return;
}
let due_in = self
.reliable_provisionals
.iter()
.map(|e| e.next_fire_after)
.min()
.unwrap_or(self.timers.t1);
let mut to_abandon: Vec<u32> = Vec::new();
for entry in self.reliable_provisionals.iter_mut() {
entry.next_fire_after = entry.next_fire_after.saturating_sub(due_in);
if !entry.next_fire_after.is_zero() {
continue;
}
entry.elapsed = entry.elapsed.saturating_add(entry.next_interval);
if entry.elapsed >= entry.abandon_at {
to_abandon.push(entry.rseq);
continue;
}
self.actions.push(Action::Send(entry.bytes.clone()));
entry.next_interval = std::cmp::min(entry.next_interval * 2, self.timers.t2);
entry.next_fire_after = entry.next_interval;
}
let final_sent = self.final_sent;
self.reliable_provisionals
.retain(|e| !to_abandon.contains(&e.rseq));
for rseq in &to_abandon {
self.actions.push(Action::Event(Event::PrackTimeout {
rseq: *rseq,
final_sent,
}));
}
if let Some(next) = self
.reliable_provisionals
.iter()
.map(|e| e.next_fire_after)
.min()
{
self.actions.push(Action::SetTimer(Timer::N, next));
}
}
fn drain_reliable_provisionals(&mut self) {
if self.reliable_provisionals.is_empty() {
return;
}
self.reliable_provisionals.clear();
self.actions.push(Action::CancelTimer(Timer::N));
}
pub fn handle_request(&mut self, request: SipRequest) {
match self.state {
State::Proceeding => {
if request.method() == Method::Invite {
if let Some(ref resp) = self.last_response {
self.actions.push(Action::Send(resp.clone()));
}
}
}
State::Completed => {
if request.method() == Method::Invite {
if let Some(ref resp) = self.last_response {
self.actions.push(Action::Send(resp.clone()));
}
} else if request.method() == Method::Ack {
self.state = State::Confirmed;
if !self.reliable {
self.actions.push(Action::CancelTimer(Timer::G));
}
self.actions.push(Action::CancelTimer(Timer::H));
self.actions.push(Action::Event(Event::AckReceived));
let timer_i = if self.reliable {
Duration::ZERO
} else {
self.timers.timer_i()
};
if timer_i.is_zero() {
self.state = State::Terminated;
} else {
self.actions.push(Action::SetTimer(Timer::I, timer_i));
}
}
}
State::Confirmed => {
}
State::Terminated => {
}
}
}
pub fn send_response(&mut self, response: SipResponse) {
let code = response.status_code();
let resp_bytes = response.to_bytes();
match self.state {
State::Proceeding => {
if (100..200).contains(&code) {
self.last_response = Some(resp_bytes.clone());
self.actions.push(Action::Send(resp_bytes));
} else if (200..300).contains(&code) {
self.final_sent = FinalSent::TwoXx;
self.drain_reliable_provisionals();
self.state = State::Terminated;
self.actions.push(Action::Send(resp_bytes));
} else if code >= 300 {
self.final_sent = FinalSent::NonTwoXx;
self.drain_reliable_provisionals();
self.state = State::Completed;
self.last_response = Some(resp_bytes.clone());
self.actions.push(Action::Send(resp_bytes));
if !self.reliable {
self.actions
.push(Action::SetTimer(Timer::G, self.retransmit_interval));
}
self.actions
.push(Action::SetTimer(Timer::H, self.timers.timer_h()));
}
}
_ => {
}
}
}
pub fn send_provisional_reliable(&mut self, mut response: SipResponse) {
let code = response.status_code();
debug_assert!(
code != 100,
"send_provisional_reliable: 100 Trying is undefined for reliable provisional (RFC 3262)"
);
debug_assert!(
(100..200).contains(&code),
"send_provisional_reliable: status must be 1xx, got {code}"
);
if self.state != State::Proceeding {
return;
}
let rseq = self.next_rseq;
self.next_rseq = self.next_rseq.checked_add(1).expect(
"RSeq exhausted; impossible in practice (2^32 reliable provisionals on a single transaction)",
);
let stamped = stamp_reliable_headers(&mut response, rseq);
let was_empty = self.reliable_provisionals.is_empty();
let entry = ReliableProvisional {
rseq,
bytes: stamped.clone(),
next_fire_after: self.timers.t1,
next_interval: self.timers.t1,
elapsed: Duration::ZERO,
abandon_at: self.timers.t1 * 64,
};
self.reliable_provisionals.push(entry);
self.last_response = Some(stamped.clone());
self.actions.push(Action::Send(stamped));
if was_empty {
self.actions
.push(Action::SetTimer(Timer::N, self.timers.t1));
}
}
pub fn handle_event(&mut self, event: Event) {
if let Event::PrackReceived(rseq) = event {
self.handle_prack_received(rseq);
}
}
fn handle_prack_received(&mut self, rseq: u32) {
let before = self.reliable_provisionals.len();
self.reliable_provisionals.retain(|e| e.rseq != rseq);
if self.reliable_provisionals.len() == before {
return;
}
if self.reliable_provisionals.is_empty() {
self.actions.push(Action::CancelTimer(Timer::N));
} else {
let next = self
.reliable_provisionals
.iter()
.map(|e| e.next_fire_after)
.min()
.unwrap_or(self.timers.t1);
self.actions.push(Action::CancelTimer(Timer::N));
self.actions.push(Action::SetTimer(Timer::N, next));
}
}
pub fn poll_actions(&mut self) -> Vec<Action> {
std::mem::take(&mut self.actions)
}
pub fn handle_transport_error(&mut self) {
match self.state {
State::Proceeding | State::Completed => {
self.state = State::Terminated;
self.actions.push(Action::Event(Event::TransportError));
}
_ => {}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_invite() -> SipRequest {
SipRequest::builder()
.method(Method::Invite)
.uri("sip:bob@example.com")
.via("192.168.1.1", 5060, "UDP", "z9hG4bKtest")
.from("sip:alice@example.com", "fromtag")
.to("sip:bob@example.com")
.call_id("test@example.com")
.cseq(1)
.build()
.unwrap()
}
fn create_response(code: u16, req: &SipRequest) -> SipResponse {
SipResponse::builder()
.status(code, "Test")
.from_request(req)
.to_tag("totag")
.build()
.unwrap()
}
fn create_ack() -> SipRequest {
SipRequest::builder()
.method(Method::Ack)
.uri("sip:bob@example.com")
.via("192.168.1.1", 5060, "UDP", "z9hG4bKtest")
.from("sip:alice@example.com", "fromtag")
.to("sip:bob@example.com")
.call_id("test@example.com")
.cseq(1)
.build()
.unwrap()
}
#[test]
fn test_new_transaction() {
let invite = create_invite();
let tx = InviteServerTransaction::new(invite, false).unwrap();
assert_eq!(tx.state(), State::Proceeding);
assert!(!tx.is_terminated());
}
#[test]
fn test_provisional_response() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(180, &invite);
tx.send_response(resp);
assert_eq!(tx.state(), State::Proceeding);
let actions = tx.poll_actions();
assert!(actions.iter().any(|a| matches!(a, Action::Send(_))));
}
#[test]
fn test_success_response_terminates() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(200, &invite);
tx.send_response(resp);
assert_eq!(tx.state(), State::Terminated);
}
#[test]
fn test_failure_response() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
assert_eq!(tx.state(), State::Completed);
let actions = tx.poll_actions();
assert!(actions
.iter()
.any(|a| matches!(a, Action::SetTimer(Timer::G, _))));
assert!(actions
.iter()
.any(|a| matches!(a, Action::SetTimer(Timer::H, _))));
}
#[test]
fn test_ack_received() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
let ack = create_ack();
tx.handle_request(ack);
assert_eq!(tx.state(), State::Confirmed);
let actions = tx.poll_actions();
assert!(actions
.iter()
.any(|a| matches!(a, Action::Event(Event::AckReceived))));
}
#[test]
fn test_timer_h_timeout() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
tx.handle_timeout(Timer::H);
assert_eq!(tx.state(), State::Terminated);
let actions = tx.poll_actions();
assert!(actions
.iter()
.any(|a| matches!(a, Action::Event(Event::Timeout))));
}
#[test]
fn test_timer_i_terminates() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
let ack = create_ack();
tx.handle_request(ack);
tx.poll_actions();
assert_eq!(tx.state(), State::Confirmed);
tx.handle_timeout(Timer::I);
assert_eq!(tx.state(), State::Terminated);
}
#[test]
fn test_reliable_transport() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), true).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
let actions = tx.poll_actions();
assert!(!actions
.iter()
.any(|a| matches!(a, Action::SetTimer(Timer::G, _))));
assert!(actions
.iter()
.any(|a| matches!(a, Action::SetTimer(Timer::H, _))));
}
#[test]
fn test_reject_non_invite() {
let register = SipRequest::builder()
.method(Method::Register)
.uri("sip:example.com")
.via("192.168.1.1", 5060, "UDP", "z9hG4bKtest")
.from("sip:alice@example.com", "fromtag")
.to("sip:alice@example.com")
.call_id("register@example.com")
.cseq(1)
.build()
.unwrap();
let tx = InviteServerTransaction::new(register, false);
assert!(tx.is_none());
}
#[test]
fn test_transaction_id() {
let invite = create_invite();
let tx = InviteServerTransaction::new(invite, false).unwrap();
let id = tx.id();
assert!(!id.branch.is_empty());
}
#[test]
fn test_request_accessor() {
let invite = create_invite();
let tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
assert_eq!(tx.request().method(), Method::Invite);
}
#[test]
fn test_timer_g_retransmit() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
assert_eq!(tx.state(), State::Completed);
tx.handle_timeout(Timer::G);
let actions = tx.poll_actions();
assert!(actions.iter().any(|a| matches!(a, Action::Send(_))));
assert!(actions
.iter()
.any(|a| matches!(a, Action::SetTimer(Timer::G, _))));
}
#[test]
fn test_timer_g_without_last_response() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
tx.last_response = None;
tx.handle_timeout(Timer::G);
let actions = tx.poll_actions();
assert!(actions
.iter()
.any(|a| matches!(a, Action::SetTimer(Timer::G, _))));
assert!(actions.iter().all(|a| !matches!(a, Action::Send(_))));
}
#[test]
fn test_invite_retransmit_in_proceeding() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(100, &invite);
tx.send_response(resp);
tx.poll_actions();
assert_eq!(tx.state(), State::Proceeding);
tx.handle_request(invite);
let actions = tx.poll_actions();
assert!(actions.iter().any(|a| matches!(a, Action::Send(_))));
}
#[test]
fn test_non_invite_request_in_proceeding_ignored() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite, false).unwrap();
tx.poll_actions();
let ack = create_ack();
tx.handle_request(ack);
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_invite_retransmit_in_proceeding_no_response() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
assert_eq!(tx.state(), State::Proceeding);
tx.handle_request(invite);
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_invite_retransmit_in_completed() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
assert_eq!(tx.state(), State::Completed);
tx.handle_request(invite);
let actions = tx.poll_actions();
assert!(actions.iter().any(|a| matches!(a, Action::Send(_))));
}
#[test]
fn test_invite_retransmit_in_completed_without_response() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
tx.last_response = None;
tx.handle_request(invite);
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_non_invite_request_in_completed_ignored() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
let bye = SipRequest::builder()
.method(Method::Bye)
.uri("sip:bob@example.com")
.via("192.168.1.1", 5060, "UDP", "z9hG4bKtest")
.from("sip:alice@example.com", "fromtag")
.to("sip:bob@example.com")
.call_id("test@example.com")
.cseq(2)
.build()
.unwrap();
tx.handle_request(bye);
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_ack_absorbed_in_confirmed() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
let ack = create_ack();
tx.handle_request(ack.clone());
tx.poll_actions();
assert_eq!(tx.state(), State::Confirmed);
tx.handle_request(ack);
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_request_in_terminated_ignored() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), true).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
let ack = create_ack();
tx.handle_request(ack);
assert_eq!(tx.state(), State::Terminated);
tx.poll_actions();
tx.handle_request(invite);
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_reliable_transport_ack_terminates() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), true).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
let ack = create_ack();
tx.handle_request(ack);
assert_eq!(tx.state(), State::Terminated);
}
#[test]
fn test_response_in_completed_ignored() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
assert_eq!(tx.state(), State::Completed);
let resp2 = create_response(500, &invite);
tx.send_response(resp2);
assert_eq!(tx.state(), State::Completed);
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_transport_error_in_proceeding() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite, false).unwrap();
tx.poll_actions();
tx.handle_transport_error();
assert_eq!(tx.state(), State::Terminated);
let actions = tx.poll_actions();
assert!(actions
.iter()
.any(|a| matches!(a, Action::Event(Event::TransportError))));
}
#[test]
fn test_transport_error_in_completed() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
tx.handle_transport_error();
assert_eq!(tx.state(), State::Terminated);
let actions = tx.poll_actions();
assert!(actions
.iter()
.any(|a| matches!(a, Action::Event(Event::TransportError))));
}
#[test]
fn test_transport_error_in_confirmed_ignored() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
let ack = create_ack();
tx.handle_request(ack);
tx.poll_actions();
assert_eq!(tx.state(), State::Confirmed);
tx.handle_transport_error();
assert_eq!(tx.state(), State::Confirmed);
}
#[test]
fn test_unexpected_timer_ignored() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite, false).unwrap();
tx.poll_actions();
tx.handle_timeout(Timer::G);
assert_eq!(tx.state(), State::Proceeding);
tx.handle_timeout(Timer::H);
assert_eq!(tx.state(), State::Proceeding);
tx.handle_timeout(Timer::I);
assert_eq!(tx.state(), State::Proceeding);
}
#[test]
fn test_3xx_response() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(302, &invite);
tx.send_response(resp);
assert_eq!(tx.state(), State::Completed);
}
#[test]
fn test_response_below_100_ignored() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(99, &invite);
tx.send_response(resp);
assert_eq!(tx.state(), State::Proceeding);
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_5xx_response() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(503, &invite);
tx.send_response(resp);
assert_eq!(tx.state(), State::Completed);
}
#[test]
fn test_6xx_response() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(603, &invite);
tx.send_response(resp);
assert_eq!(tx.state(), State::Completed);
}
#[test]
fn test_state_debug() {
assert_eq!(format!("{:?}", State::Proceeding), "Proceeding");
assert_eq!(format!("{:?}", State::Completed), "Completed");
assert_eq!(format!("{:?}", State::Confirmed), "Confirmed");
assert_eq!(format!("{:?}", State::Terminated), "Terminated");
}
#[test]
fn test_event_debug() {
let invite = create_invite();
let ev1 = Event::Request(Box::new(invite));
let ev2 = Event::AckReceived;
let ev3 = Event::Timeout;
let ev4 = Event::TransportError;
assert!(format!("{:?}", ev1).contains("Request"));
assert!(format!("{:?}", ev2).contains("AckReceived"));
assert!(format!("{:?}", ev3).contains("Timeout"));
assert!(format!("{:?}", ev4).contains("TransportError"));
}
#[test]
fn test_action_debug() {
let action1 = Action::Send(bytes::Bytes::from_static(b"test"));
let action2 = Action::SetTimer(Timer::G, Duration::from_secs(1));
let action3 = Action::CancelTimer(Timer::H);
assert!(format!("{:?}", action1).contains("Send"));
assert!(format!("{:?}", action2).contains("SetTimer"));
assert!(format!("{:?}", action3).contains("CancelTimer"));
}
#[test]
fn test_multiple_100_responses() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp1 = create_response(100, &invite);
tx.send_response(resp1);
assert_eq!(tx.state(), State::Proceeding);
tx.poll_actions();
let resp2 = create_response(180, &invite);
tx.send_response(resp2);
assert_eq!(tx.state(), State::Proceeding);
let actions = tx.poll_actions();
assert!(actions.iter().any(|a| matches!(a, Action::Send(_))));
}
#[test]
fn test_ack_cancels_timers() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(404, &invite);
tx.send_response(resp);
tx.poll_actions();
let ack = create_ack();
tx.handle_request(ack);
let actions = tx.poll_actions();
assert!(actions
.iter()
.any(|a| matches!(a, Action::CancelTimer(Timer::G))));
assert!(actions
.iter()
.any(|a| matches!(a, Action::CancelTimer(Timer::H))));
assert!(actions
.iter()
.any(|a| matches!(a, Action::SetTimer(Timer::I, _))));
}
#[test]
fn test_initial_request_event() {
let invite = create_invite();
let tx = InviteServerTransaction::new(invite, false).unwrap();
let actions = tx.actions.clone();
assert!(actions
.iter()
.any(|a| matches!(a, Action::Event(Event::Request(_)))));
}
fn first_send_text(actions: &[Action]) -> Option<String> {
actions.iter().find_map(|a| match a {
Action::Send(b) => Some(String::from_utf8_lossy(b).into_owned()),
_ => None,
})
}
fn count_sends(actions: &[Action]) -> usize {
actions
.iter()
.filter(|a| matches!(a, Action::Send(_)))
.count()
}
fn drive_timer_n_to_completion(tx: &mut InviteServerTransaction, max_ticks: usize) -> usize {
let mut ticks = 0;
while ticks < max_ticks && !tx.reliable_provisionals.is_empty() {
tx.handle_timeout(Timer::N);
tx.poll_actions(); ticks += 1;
}
ticks
}
#[test]
fn test_send_provisional_reliable_emits_send_and_timer() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(180, &invite);
tx.send_provisional_reliable(resp);
assert_eq!(tx.state(), State::Proceeding);
let actions = tx.poll_actions();
assert_eq!(count_sends(&actions), 1);
let wire = first_send_text(&actions).expect("Send action present");
assert!(
wire.contains("Require: 100rel"),
"wire missing Require: 100rel: {wire}"
);
assert!(wire.contains("RSeq: 1"), "wire missing RSeq: 1: {wire}");
let t1 = TimerValues::default().t1;
assert!(
actions.iter().any(|a| matches!(
a,
Action::SetTimer(Timer::N, d) if *d == t1
)),
"expected SetTimer(N, T1), got: {actions:?}"
);
}
#[test]
fn test_prack_received_cancels_retransmit() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(180, &invite);
tx.send_provisional_reliable(resp);
tx.poll_actions();
tx.handle_event(Event::PrackReceived(1));
let actions = tx.poll_actions();
assert!(actions
.iter()
.any(|a| matches!(a, Action::CancelTimer(Timer::N))));
tx.handle_timeout(Timer::N);
let actions = tx.poll_actions();
assert_eq!(count_sends(&actions), 0);
assert!(!actions
.iter()
.any(|a| matches!(a, Action::SetTimer(Timer::N, _))));
}
#[test]
fn test_prack_received_unknown_rseq_ignored() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(180, &invite);
tx.send_provisional_reliable(resp);
tx.poll_actions();
tx.handle_event(Event::PrackReceived(99));
let actions = tx.poll_actions();
assert!(actions.is_empty());
tx.handle_timeout(Timer::N);
let actions = tx.poll_actions();
assert_eq!(count_sends(&actions), 1);
}
#[test]
fn test_prack_timeout_no_final() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(180, &invite);
tx.send_provisional_reliable(resp);
tx.poll_actions();
let mut events: Vec<Event> = Vec::new();
for _ in 0..200 {
tx.handle_timeout(Timer::N);
for a in tx.poll_actions() {
if let Action::Event(ev) = a {
events.push(ev);
}
}
if tx.reliable_provisionals.is_empty() {
break;
}
}
let timeout = events.iter().find(|ev| {
matches!(
ev,
Event::PrackTimeout {
rseq: 1,
final_sent: FinalSent::None
}
)
});
assert!(
timeout.is_some(),
"expected PrackTimeout {{ rseq: 1, final_sent: None }}; got: {events:?}"
);
}
#[test]
fn test_prack_timeout_after_non_2xx_final_does_not_fire() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let provisional = create_response(180, &invite);
tx.send_provisional_reliable(provisional);
tx.poll_actions();
let final_resp = create_response(486, &invite);
tx.send_response(final_resp);
let drain_actions = tx.poll_actions();
assert!(
drain_actions
.iter()
.any(|a| matches!(a, Action::CancelTimer(Timer::N))),
"486 final must cancel Timer N"
);
assert!(tx.reliable_provisionals.is_empty());
let ticks = drive_timer_n_to_completion(&mut tx, 200);
assert_eq!(
ticks, 0,
"expected Timer N to no-op with empty buffer (got {ticks} ticks)"
);
}
#[test]
fn test_prack_timeout_after_2xx_final_does_not_fire() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let provisional = create_response(183, &invite);
tx.send_provisional_reliable(provisional);
tx.poll_actions();
let final_resp = create_response(200, &invite);
tx.send_response(final_resp);
let drain_actions = tx.poll_actions();
assert!(
drain_actions
.iter()
.any(|a| matches!(a, Action::CancelTimer(Timer::N))),
"200 OK final must cancel Timer N"
);
assert!(tx.reliable_provisionals.is_empty());
let ticks = drive_timer_n_to_completion(&mut tx, 200);
assert_eq!(ticks, 0);
}
#[test]
fn test_buffer_drained_on_final() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let provisional = create_response(180, &invite);
tx.send_provisional_reliable(provisional);
tx.poll_actions();
let final_resp = create_response(486, &invite);
tx.send_response(final_resp);
tx.poll_actions();
tx.handle_timeout(Timer::N);
let actions = tx.poll_actions();
assert_eq!(count_sends(&actions), 0);
assert!(!actions
.iter()
.any(|a| matches!(a, Action::SetTimer(Timer::N, _))));
}
#[test]
#[should_panic(expected = "100 Trying is undefined")]
fn test_reject_100() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(100, &invite);
tx.send_provisional_reliable(resp);
}
#[test]
fn test_rseq_monotonic() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp1 = create_response(180, &invite);
tx.send_provisional_reliable(resp1);
let actions = tx.poll_actions();
let wire1 = first_send_text(&actions).expect("first send");
assert!(wire1.contains("RSeq: 1"), "first wire: {wire1}");
let resp2 = create_response(183, &invite);
tx.send_provisional_reliable(resp2);
let actions = tx.poll_actions();
let wire2 = first_send_text(&actions).expect("second send");
assert!(wire2.contains("RSeq: 2"), "second wire: {wire2}");
}
#[test]
fn test_multiple_outstanding_prack() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
tx.send_provisional_reliable(create_response(180, &invite));
tx.poll_actions();
tx.send_provisional_reliable(create_response(183, &invite));
tx.poll_actions();
assert_eq!(tx.reliable_provisionals.len(), 2);
tx.handle_event(Event::PrackReceived(1));
tx.poll_actions();
assert_eq!(tx.reliable_provisionals.len(), 1);
assert_eq!(tx.reliable_provisionals[0].rseq, 2);
tx.handle_timeout(Timer::N);
let actions = tx.poll_actions();
assert_eq!(count_sends(&actions), 1);
let wire = first_send_text(&actions).expect("send for rseq 2");
assert!(
wire.contains("RSeq: 2"),
"expected retransmit of RSeq 2, got: {wire}"
);
}
#[test]
fn test_reliable_provisional_in_terminated_noop() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
tx.send_response(create_response(200, &invite));
tx.poll_actions();
assert_eq!(tx.state(), State::Terminated);
let resp = create_response(180, &invite);
tx.send_provisional_reliable(resp);
let actions = tx.poll_actions();
assert!(actions.is_empty());
assert!(tx.reliable_provisionals.is_empty());
}
#[test]
fn test_final_sent_field_after_2xx() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
tx.send_response(create_response(200, &invite));
assert_eq!(tx.final_sent, FinalSent::TwoXx);
}
#[test]
fn test_final_sent_field_after_non_2xx() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
tx.send_response(create_response(486, &invite));
assert_eq!(tx.final_sent, FinalSent::NonTwoXx);
}
#[test]
fn test_timer_n_retransmit_doubles_then_caps() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(180, &invite);
tx.send_provisional_reliable(resp);
tx.poll_actions();
let tv = TimerValues::default();
let mut expected = tv.t1 * 2;
for _ in 0..5 {
tx.handle_timeout(Timer::N);
let actions = tx.poll_actions();
let dur = actions.iter().find_map(|a| match a {
Action::SetTimer(Timer::N, d) => Some(*d),
_ => None,
});
if let Some(d) = dur {
assert_eq!(d, expected, "schedule mismatch (expected {expected:?})");
}
expected = std::cmp::min(expected * 2, tv.t2);
}
}
#[test]
fn test_final_sent_initially_none() {
let invite = create_invite();
let tx = InviteServerTransaction::new(invite, false).unwrap();
assert_eq!(tx.final_sent, FinalSent::None);
}
#[test]
fn test_send_provisional_reliable_merges_existing_require() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = SipResponse::builder()
.status(180, "Ringing")
.from_request(&invite)
.to_tag("totag")
.require(&["precondition"])
.build()
.unwrap();
tx.send_provisional_reliable(resp);
let actions = tx.poll_actions();
let wire = first_send_text(&actions).expect("Send action present");
let require_lines: Vec<&str> = wire
.lines()
.filter(|l| l.to_ascii_lowercase().starts_with("require:"))
.collect();
assert_eq!(
require_lines.len(),
1,
"expected exactly one Require: line, got {require_lines:?} (wire: {wire})"
);
let line = require_lines[0].to_ascii_lowercase();
assert!(
line.contains("precondition"),
"Require missing precondition: {line}"
);
assert!(line.contains("100rel"), "Require missing 100rel: {line}");
}
#[test]
fn test_send_provisional_reliable_round_trips_through_parse() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(180, &invite);
tx.send_provisional_reliable(resp);
let actions = tx.poll_actions();
let bytes = actions
.iter()
.find_map(|a| match a {
Action::Send(b) => Some(b.clone()),
_ => None,
})
.expect("Send action present");
let msg = crate::sip::SipMessage::parse(&bytes).expect("re-parse stamped bytes");
let response = match msg {
crate::sip::SipMessage::Response(r) => r,
_ => panic!("expected SipMessage::Response"),
};
assert!(
response
.require()
.is_some_and(|r| r.0.iter().any(|t| t.eq_ignore_ascii_case("100rel"))),
"round-tripped response missing Require: 100rel"
);
assert_eq!(response.rseq().map(|r| r.0), Some(1));
}
#[test]
fn test_multiple_outstanding_prack_staggered() {
let invite = create_invite();
let mut tx = InviteServerTransaction::new(invite.clone(), false).unwrap();
tx.poll_actions();
tx.send_provisional_reliable(create_response(180, &invite));
let actions = tx.poll_actions();
assert_eq!(count_sends(&actions), 1);
assert!(actions
.iter()
.any(|a| matches!(a, Action::SetTimer(Timer::N, _))));
tx.handle_timeout(Timer::N);
let actions = tx.poll_actions();
assert_eq!(count_sends(&actions), 1);
let wire = first_send_text(&actions).expect("entry1 first retransmit");
assert!(wire.contains("RSeq: 1"), "expected RSeq 1: {wire}");
tx.send_provisional_reliable(create_response(183, &invite));
let actions = tx.poll_actions();
assert_eq!(count_sends(&actions), 1, "entry2 initial send");
assert!(
!actions
.iter()
.any(|a| matches!(a, Action::SetTimer(Timer::N, _))),
"entry2 must NOT emit SetTimer(N) — it inherits entry1's deadline (got {actions:?})"
);
let mut sends_rseq1: usize = 1;
let mut sends_rseq2: usize = 0;
let mut timeouts: Vec<u32> = Vec::new();
for _ in 0..200 {
tx.handle_timeout(Timer::N);
let actions = tx.poll_actions();
for a in &actions {
match a {
Action::Send(b) => {
let s = String::from_utf8_lossy(b);
if s.contains("RSeq: 1") {
sends_rseq1 += 1;
} else if s.contains("RSeq: 2") {
sends_rseq2 += 1;
}
}
Action::Event(Event::PrackTimeout { rseq, .. }) => {
timeouts.push(*rseq);
}
_ => {}
}
}
if tx.reliable_provisionals.is_empty() {
break;
}
}
assert!(
sends_rseq1 >= 3,
"entry1 retransmitted too few times: {sends_rseq1}"
);
assert!(
sends_rseq2 >= 2,
"entry2 retransmitted too few times: {sends_rseq2}"
);
assert!(
sends_rseq1 >= sends_rseq2,
"entry1 should retransmit at least as many times as entry2 (head start of one tick); got rseq1={sends_rseq1}, rseq2={sends_rseq2}"
);
assert!(
sends_rseq1.abs_diff(sends_rseq2) <= 2,
"schedules diverged unreasonably: rseq1={sends_rseq1}, rseq2={sends_rseq2}"
);
assert!(
timeouts.contains(&1),
"expected PrackTimeout for RSeq 1; got: {timeouts:?}"
);
assert!(
timeouts.contains(&2),
"expected PrackTimeout for RSeq 2; got: {timeouts:?}"
);
assert!(tx.reliable_provisionals.is_empty(), "buffer must drain");
}
}