ftth_rsipstack/transaction/
key.rs1use 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(®ister_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(®ister_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}