ftth_rsipstack/transaction/
key.rs

1use crate::{Error, Result};
2use rsip::headers::UntypedHeader;
3use rsip::typed::Via;
4use rsip::{
5    param::Tag,
6    prelude::{HeadersExt, ToTypedHeader},
7    Method,
8};
9use rsip::{Request, Response};
10use std::fmt::Write;
11use std::hash::Hash;
12
13#[derive(Clone, PartialEq, Eq, Hash, Debug)]
14pub enum TransactionRole {
15    Client,
16    Server,
17}
18
19impl std::fmt::Display for TransactionRole {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        match self {
22            TransactionRole::Client => write!(f, "c"),
23            TransactionRole::Server => write!(f, "s"),
24        }
25    }
26}
27
28#[derive(Clone, PartialEq, Eq, Hash, Debug)]
29pub struct TransactionKey(String);
30
31impl std::fmt::Display for TransactionKey {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        write!(f, "{}", self.0)
34    }
35}
36
37impl TransactionKey {
38    pub fn from_request(req: &Request, role: TransactionRole) -> Result<Self> {
39        let via = req.via_header()?.typed()?;
40        let mut method = req.method().clone();
41
42        if matches!(method, Method::Ack | Method::Cancel) && role == TransactionRole::Server {
43            method = Method::Invite;
44        }
45
46        let from_tag = req
47            .from_header()?
48            .tag()?
49            .ok_or(Error::Error("from tags missing".to_string()))?;
50        let call_id = req.call_id_header()?.value();
51        let cseq = req.cseq_header()?.seq()?;
52        Self::build_key(role, via, method, cseq, from_tag, call_id)
53    }
54
55    pub fn from_response(resp: &Response, role: TransactionRole) -> Result<Self> {
56        let via = resp.via_header()?.typed()?;
57        let cseq = resp.cseq_header()?;
58        let method = cseq.method()?;
59        let from_tag = resp
60            .from_header()?
61            .tag()?
62            .ok_or(Error::Error("from tags missing".to_string()))?;
63        let call_id = resp.call_id_header()?.value();
64
65        Self::build_key(role, via, method, cseq.seq()?, from_tag, call_id)
66    }
67
68    pub(super) fn build_key(
69        role: TransactionRole,
70        via: Via,
71        method: Method,
72        cseq: u32,
73        from_tag: Tag,
74        call_id: &str,
75    ) -> Result<Self> {
76        let mut key = String::new();
77        match via.branch() {
78            Some(branch) => {
79                write!(
80                    &mut key,
81                    "{}.{}_{}_{}_{}_{}",
82                    role, method, cseq, call_id, from_tag, branch
83                )
84            }
85            None => {
86                write!(
87                    &mut key,
88                    "{}.{}_{}_{}_{}_{}.2543",
89                    role, method, cseq, call_id, from_tag, via.uri.host_with_port
90                )
91            }
92        }
93        .map_err(|e| Error::Error(e.to_string()))?;
94        Ok(TransactionKey(key))
95    }
96}
97
98#[test]
99fn test_transaction_key() -> Result<()> {
100    use rsip::headers::*;
101    let register_req = rsip::message::Request {
102        method: rsip::method::Method::Register,
103        uri: rsip::Uri {
104            scheme: Some(rsip::Scheme::Sips),
105            host_with_port: rsip::Domain::from("restsend.com").into(),
106            ..Default::default()
107        },
108        headers: vec![
109            Via::new("SIP/2.0/TLS sip.restsend.com:5061;branch=z9hG4bKnashd92").into(),
110            CSeq::new("2 REGISTER").into(),
111            From::new("Bob <sips:bob@sip.restsend.com>;tag=ja743ks76zlflH").into(),
112            CallId::new("1j9FpLxk3uxtm8tn@sip.restsend.com").into(),
113        ]
114        .into(),
115        version: rsip::Version::V2,
116        body: Default::default(),
117    };
118    let key = TransactionKey::from_request(&register_req, TransactionRole::Client)?;
119    assert_eq!(
120        key,
121        TransactionKey(
122            "c.REGISTER_2_1j9FpLxk3uxtm8tn@sip.restsend.com_ja743ks76zlflH_z9hG4bKnashd92"
123                .to_string()
124        )
125    );
126    let register_resp = rsip::message::Response {
127        status_code: rsip::StatusCode::OK,
128        version: rsip::Version::V2,
129        headers: vec![
130            Via::new("SIP/2.0/TLS client.sip.restsend.com:5061;branch=z9hG4bKnashd92").into(),
131            CSeq::new("2 REGISTER").into(),
132            From::new("Bob <sips:bob@sip.restsend.com>;tag=ja743ks76zlflH").into(),
133            CallId::new("1j9FpLxk3uxtm8tn@sip.restsend.com").into(),
134        ]
135        .into(),
136        body: Default::default(),
137    };
138    let key = TransactionKey::from_response(&register_resp, TransactionRole::Server)?;
139    assert_eq!(
140        key,
141        TransactionKey(
142            "s.REGISTER_2_1j9FpLxk3uxtm8tn@sip.restsend.com_ja743ks76zlflH_z9hG4bKnashd92"
143                .to_string()
144        )
145    );
146
147    let mut ack_req = register_req.clone();
148    ack_req.method = Method::Ack;
149    ack_req.headers.unique_push(CSeq::new("2 ACK").into());
150
151    let key = TransactionKey::from_request(&ack_req, TransactionRole::Server)?;
152    assert_eq!(
153        key,
154        TransactionKey(
155            "s.INVITE_2_1j9FpLxk3uxtm8tn@sip.restsend.com_ja743ks76zlflH_z9hG4bKnashd92"
156                .to_string()
157        )
158    );
159    Ok(())
160}