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