ftth_rsipstack/transaction/
key.rs

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