use crate::sip::{Method, SipRequest, SipResponse};
use crate::transaction::client::invite::TransactionId;
use crate::transaction::timer::{Timer, TimerValues};
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum State {
Trying,
Proceeding,
Completed,
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>),
TransportError,
}
#[derive(Debug)]
pub struct NonInviteServerTransaction {
id: TransactionId,
state: State,
request: SipRequest,
last_response: Option<bytes::Bytes>,
timers: TimerValues,
reliable: bool,
actions: Vec<Action>,
}
impl NonInviteServerTransaction {
pub fn new(request: SipRequest, reliable: bool) -> Option<Self> {
if request.method() == Method::Invite {
return None;
}
let id = TransactionId::from_request(&request)?;
let mut tx = Self {
id,
state: State::Trying,
request: request.clone(),
last_response: None,
timers: TimerValues::default(),
reliable,
actions: Vec::new(),
};
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 handle_timeout(&mut self, timer: Timer) {
match (self.state, timer) {
(State::Completed, Timer::J) => {
self.state = State::Terminated;
}
_ => {
}
}
}
pub fn handle_request(&mut self, _request: SipRequest) {
match self.state {
State::Trying => {
}
State::Proceeding | State::Completed => {
if let Some(ref resp) = self.last_response {
self.actions.push(Action::Send(resp.clone()));
}
}
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::Trying => {
if (100..200).contains(&code) {
self.state = State::Proceeding;
self.last_response = Some(resp_bytes.clone());
self.actions.push(Action::Send(resp_bytes));
} else if code >= 200 {
self.state = State::Completed;
self.last_response = Some(resp_bytes.clone());
self.actions.push(Action::Send(resp_bytes));
let timer_j = self.timers.timer_j(self.reliable);
if timer_j.is_zero() {
self.state = State::Terminated;
} else {
self.actions.push(Action::SetTimer(Timer::J, timer_j));
}
}
}
State::Proceeding => {
if (100..200).contains(&code) {
self.last_response = Some(resp_bytes.clone());
self.actions.push(Action::Send(resp_bytes));
} else if code >= 200 {
self.state = State::Completed;
self.last_response = Some(resp_bytes.clone());
self.actions.push(Action::Send(resp_bytes));
let timer_j = self.timers.timer_j(self.reliable);
if timer_j.is_zero() {
self.state = State::Terminated;
} else {
self.actions.push(Action::SetTimer(Timer::J, timer_j));
}
}
}
_ => {
}
}
}
pub fn poll_actions(&mut self) -> Vec<Action> {
std::mem::take(&mut self.actions)
}
#[cfg(test)]
pub(crate) fn inject_cancel_timer(&mut self, timer: Timer) {
self.actions.push(Action::CancelTimer(timer));
}
pub fn handle_transport_error(&mut self) {
match self.state {
State::Trying | State::Proceeding | State::Completed => {
self.state = State::Terminated;
self.actions.push(Action::Event(Event::TransportError));
}
State::Terminated => {}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_register() -> SipRequest {
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()
}
fn create_response(code: u16, req: &SipRequest) -> SipResponse {
SipResponse::builder()
.status(code, "Test")
.from_request(req)
.to_tag("totag")
.build()
.unwrap()
}
#[test]
fn test_new_transaction() {
let req = create_register();
let tx = NonInviteServerTransaction::new(req, false).unwrap();
assert_eq!(tx.state(), State::Trying);
assert!(!tx.is_terminated());
}
#[test]
fn test_reject_invite() {
let invite = 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();
let tx = NonInviteServerTransaction::new(invite, false);
assert!(tx.is_none());
}
#[test]
fn test_provisional_response() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(100, &req);
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_final_response_from_trying() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(200, &req);
tx.send_response(resp);
assert_eq!(tx.state(), State::Completed);
let actions = tx.poll_actions();
assert!(actions.iter().any(|a| matches!(a, Action::Send(_))));
assert!(actions
.iter()
.any(|a| matches!(a, Action::SetTimer(Timer::J, _))));
}
#[test]
fn test_final_response_from_proceeding() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
let resp1 = create_response(100, &req);
tx.send_response(resp1);
tx.poll_actions();
let resp2 = create_response(200, &req);
tx.send_response(resp2);
assert_eq!(tx.state(), State::Completed);
}
#[test]
fn test_response_below_100_in_proceeding_ignored() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
let resp1 = create_response(100, &req);
tx.send_response(resp1);
assert_eq!(tx.state(), State::Proceeding);
tx.poll_actions();
let resp2 = create_response(99, &req);
tx.send_response(resp2);
let actions = tx.poll_actions();
assert!(actions.is_empty());
assert_eq!(tx.state(), State::Proceeding);
}
#[test]
fn test_final_response_from_proceeding_reliable() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), true).unwrap();
tx.poll_actions();
let resp1 = create_response(100, &req);
tx.send_response(resp1);
tx.poll_actions();
let resp2 = create_response(200, &req);
tx.send_response(resp2);
assert_eq!(tx.state(), State::Terminated);
}
#[test]
fn test_retransmit_response() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(200, &req);
tx.send_response(resp);
tx.poll_actions();
tx.handle_request(req);
let actions = tx.poll_actions();
assert!(actions.iter().any(|a| matches!(a, Action::Send(_))));
}
#[test]
fn test_timer_j_terminates() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(200, &req);
tx.send_response(resp);
tx.poll_actions();
assert_eq!(tx.state(), State::Completed);
tx.handle_timeout(Timer::J);
assert_eq!(tx.state(), State::Terminated);
}
#[test]
fn test_reliable_transport_immediate_terminate() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), true).unwrap();
tx.poll_actions();
let resp = create_response(200, &req);
tx.send_response(resp);
assert_eq!(tx.state(), State::Terminated);
}
#[test]
fn test_handle_transport_error_in_trying() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req, 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_handle_transport_error_in_proceeding() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(100, &req);
tx.send_response(resp);
tx.poll_actions();
assert_eq!(tx.state(), State::Proceeding);
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_handle_transport_error_in_completed() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(200, &req);
tx.send_response(resp);
tx.poll_actions();
assert_eq!(tx.state(), State::Completed);
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_handle_transport_error_in_terminated_noop() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), true).unwrap();
tx.poll_actions();
let resp = create_response(200, &req);
tx.send_response(resp);
tx.poll_actions();
assert_eq!(tx.state(), State::Terminated);
tx.handle_transport_error();
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_handle_request_in_trying_no_retransmit() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
tx.handle_request(req);
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_handle_request_in_proceeding_retransmits() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(100, &req);
tx.send_response(resp);
tx.poll_actions();
assert_eq!(tx.state(), State::Proceeding);
tx.handle_request(req);
let actions = tx.poll_actions();
assert!(actions.iter().any(|a| matches!(a, Action::Send(_))));
}
#[test]
fn test_response_below_100_in_trying_ignored() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(99, &req);
tx.send_response(resp);
assert_eq!(tx.state(), State::Trying);
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_handle_request_in_proceeding_without_last_response() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(100, &req);
tx.send_response(resp);
tx.poll_actions();
tx.last_response = None;
tx.handle_request(req);
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_handle_request_in_terminated_ignored() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), true).unwrap();
tx.poll_actions();
let resp = create_response(200, &req);
tx.send_response(resp);
tx.poll_actions();
assert_eq!(tx.state(), State::Terminated);
tx.handle_request(req);
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_multiple_provisional_in_proceeding() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
let resp1 = create_response(100, &req);
tx.send_response(resp1);
tx.poll_actions();
assert_eq!(tx.state(), State::Proceeding);
let resp2 = create_response(183, &req);
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_send_response_in_completed_ignored() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
let resp = create_response(200, &req);
tx.send_response(resp);
tx.poll_actions();
assert_eq!(tx.state(), State::Completed);
let resp2 = create_response(404, &req);
tx.send_response(resp2);
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_send_response_in_terminated_ignored() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), true).unwrap();
tx.poll_actions();
let resp = create_response(200, &req);
tx.send_response(resp);
tx.poll_actions();
assert_eq!(tx.state(), State::Terminated);
let resp2 = create_response(404, &req);
tx.send_response(resp2);
let actions = tx.poll_actions();
assert!(actions.is_empty());
}
#[test]
fn test_unexpected_timer_ignored() {
let req = create_register();
let mut tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
tx.poll_actions();
tx.handle_timeout(Timer::J);
assert_eq!(tx.state(), State::Trying);
let resp = create_response(100, &req);
tx.send_response(resp);
tx.poll_actions();
tx.handle_timeout(Timer::J);
assert_eq!(tx.state(), State::Proceeding);
tx.handle_timeout(Timer::A);
assert_eq!(tx.state(), State::Proceeding);
}
#[test]
fn test_state_debug() {
let state = State::Trying;
let debug_str = format!("{:?}", state);
assert!(debug_str.contains("Trying"));
}
#[test]
fn test_action_debug() {
let action = Action::CancelTimer(Timer::J);
let debug_str = format!("{:?}", action);
assert!(debug_str.contains("CancelTimer"));
}
#[test]
fn test_event_debug() {
let event = Event::TransportError;
let debug_str = format!("{:?}", event);
assert!(debug_str.contains("TransportError"));
}
#[test]
fn test_request_accessor() {
let req = create_register();
let tx = NonInviteServerTransaction::new(req.clone(), false).unwrap();
assert_eq!(tx.request().method(), Method::Register);
}
#[test]
fn test_id_accessor() {
let req = create_register();
let tx = NonInviteServerTransaction::new(req, false).unwrap();
let id = tx.id();
assert!(id.branch.contains("z9hG4bK"));
}
#[test]
fn test_action_event_clone() {
let action = Action::Event(Event::TransportError);
let cloned = action.clone();
assert!(format!("{cloned:?}").contains("TransportError"));
}
#[test]
fn test_action_send_clone() {
let action = Action::Send(bytes::Bytes::from("test"));
let cloned = action.clone();
assert!(format!("{cloned:?}").starts_with("Send("));
}
}