1use std::fmt;
2
3#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4pub enum HeaderName {
5 Via,
6 From,
7 To,
8 CallId,
9 CSeq,
10 Contact,
11 MaxForwards,
12 ContentType,
13 ContentLength,
14 Authorization,
15 WwwAuthenticate,
16 ProxyAuthenticate,
17 ProxyAuthorization,
18 Expires,
19 UserAgent,
20 Allow,
21 Supported,
22 Require,
23 RAck,
24 RSeq,
25 ReferTo,
26 ReferredBy,
27 Event,
28 SubscriptionState,
29 Other(String),
30}
31
32impl HeaderName {
33 pub fn from_str(s: &str) -> Self {
34 match s.to_lowercase().as_str() {
35 "via" | "v" => HeaderName::Via,
36 "from" | "f" => HeaderName::From,
37 "to" | "t" => HeaderName::To,
38 "call-id" | "i" => HeaderName::CallId,
39 "cseq" => HeaderName::CSeq,
40 "contact" | "m" => HeaderName::Contact,
41 "max-forwards" => HeaderName::MaxForwards,
42 "content-type" | "c" => HeaderName::ContentType,
43 "content-length" | "l" => HeaderName::ContentLength,
44 "authorization" => HeaderName::Authorization,
45 "www-authenticate" => HeaderName::WwwAuthenticate,
46 "proxy-authenticate" => HeaderName::ProxyAuthenticate,
47 "proxy-authorization" => HeaderName::ProxyAuthorization,
48 "expires" => HeaderName::Expires,
49 "user-agent" => HeaderName::UserAgent,
50 "allow" => HeaderName::Allow,
51 "supported" | "k" => HeaderName::Supported,
52 "require" => HeaderName::Require,
53 "rack" => HeaderName::RAck,
54 "rseq" => HeaderName::RSeq,
55 "refer-to" | "r" => HeaderName::ReferTo,
56 "referred-by" | "b" => HeaderName::ReferredBy,
57 "event" | "o" => HeaderName::Event,
58 "subscription-state" => HeaderName::SubscriptionState,
59 other => HeaderName::Other(other.to_string()),
60 }
61 }
62
63 pub fn as_str(&self) -> &str {
64 match self {
65 HeaderName::Via => "Via",
66 HeaderName::From => "From",
67 HeaderName::To => "To",
68 HeaderName::CallId => "Call-ID",
69 HeaderName::CSeq => "CSeq",
70 HeaderName::Contact => "Contact",
71 HeaderName::MaxForwards => "Max-Forwards",
72 HeaderName::ContentType => "Content-Type",
73 HeaderName::ContentLength => "Content-Length",
74 HeaderName::Authorization => "Authorization",
75 HeaderName::WwwAuthenticate => "WWW-Authenticate",
76 HeaderName::ProxyAuthenticate => "Proxy-Authenticate",
77 HeaderName::ProxyAuthorization => "Proxy-Authorization",
78 HeaderName::Expires => "Expires",
79 HeaderName::UserAgent => "User-Agent",
80 HeaderName::Allow => "Allow",
81 HeaderName::Supported => "Supported",
82 HeaderName::Require => "Require",
83 HeaderName::RAck => "RAck",
84 HeaderName::RSeq => "RSeq",
85 HeaderName::ReferTo => "Refer-To",
86 HeaderName::ReferredBy => "Referred-By",
87 HeaderName::Event => "Event",
88 HeaderName::SubscriptionState => "Subscription-State",
89 HeaderName::Other(s) => s.as_str(),
90 }
91 }
92}
93
94impl fmt::Display for HeaderName {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 write!(f, "{}", self.as_str())
97 }
98}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
101pub struct HeaderValue(pub String);
102
103impl HeaderValue {
104 pub fn new(s: impl Into<String>) -> Self {
105 Self(s.into())
106 }
107
108 pub fn as_str(&self) -> &str {
109 &self.0
110 }
111}
112
113impl fmt::Display for HeaderValue {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 write!(f, "{}", self.0)
116 }
117}
118
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct Header {
121 pub name: HeaderName,
122 pub value: HeaderValue,
123}
124
125impl Header {
126 pub fn new(name: HeaderName, value: impl Into<String>) -> Self {
127 Self {
128 name,
129 value: HeaderValue::new(value),
130 }
131 }
132}
133
134impl fmt::Display for Header {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 write!(f, "{}: {}", self.name, self.value)
137 }
138}
139
140#[derive(Debug, Clone, Default)]
141pub struct Headers {
142 headers: Vec<Header>,
143}
144
145impl Headers {
146 pub fn new() -> Self {
147 Self {
148 headers: Vec::new(),
149 }
150 }
151
152 pub fn add(&mut self, name: HeaderName, value: impl Into<String>) {
153 self.headers.push(Header::new(name, value));
154 }
155
156 pub fn get(&self, name: &HeaderName) -> Option<&HeaderValue> {
157 self.headers
158 .iter()
159 .find(|h| &h.name == name)
160 .map(|h| &h.value)
161 }
162
163 pub fn get_all(&self, name: &HeaderName) -> Vec<&HeaderValue> {
164 self.headers
165 .iter()
166 .filter(|h| &h.name == name)
167 .map(|h| &h.value)
168 .collect()
169 }
170
171 pub fn set(&mut self, name: HeaderName, value: impl Into<String>) {
172 if let Some(header) = self.headers.iter_mut().find(|h| h.name == name) {
173 header.value = HeaderValue::new(value);
174 } else {
175 self.add(name, value);
176 }
177 }
178
179 pub fn remove(&mut self, name: &HeaderName) {
180 self.headers.retain(|h| &h.name != name);
181 }
182
183 pub fn iter(&self) -> impl Iterator<Item = &Header> {
184 self.headers.iter()
185 }
186
187 pub fn is_empty(&self) -> bool {
188 self.headers.is_empty()
189 }
190
191 pub fn len(&self) -> usize {
192 self.headers.len()
193 }
194}
195
196impl fmt::Display for Headers {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 for header in &self.headers {
199 writeln!(f, "{}", header)?;
200 }
201 Ok(())
202 }
203}
204
205pub fn extract_tag(header_value: &str) -> Option<String> {
207 header_value
208 .split(';')
209 .find_map(|param| {
210 let param = param.trim();
211 if let Some(tag) = param.strip_prefix("tag=") {
212 Some(tag.to_string())
213 } else {
214 None
215 }
216 })
217}
218
219pub fn extract_uri(header_value: &str) -> Option<String> {
221 if let Some(start) = header_value.find('<') {
222 if let Some(end) = header_value.find('>') {
223 return Some(header_value[start + 1..end].to_string());
224 }
225 }
226 let uri = header_value.split(';').next()?.trim();
228 if uri.starts_with("sip:") || uri.starts_with("sips:") {
229 Some(uri.to_string())
230 } else {
231 None
232 }
233}
234
235pub fn generate_tag() -> String {
237 use rand::Rng;
238 let mut rng = rand::thread_rng();
239 let tag: u64 = rng.gen();
240 format!("{:x}", tag)
241}
242
243pub fn generate_branch() -> String {
245 use rand::Rng;
246 let mut rng = rand::thread_rng();
247 let branch: u64 = rng.gen();
248 format!("z9hG4bK{:x}", branch)
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 #[test]
256 fn test_header_name_parsing() {
257 assert_eq!(HeaderName::from_str("Via"), HeaderName::Via);
258 assert_eq!(HeaderName::from_str("v"), HeaderName::Via);
259 assert_eq!(HeaderName::from_str("VIA"), HeaderName::Via);
260 assert_eq!(HeaderName::from_str("from"), HeaderName::From);
261 assert_eq!(HeaderName::from_str("f"), HeaderName::From);
262 assert_eq!(HeaderName::from_str("Call-ID"), HeaderName::CallId);
263 assert_eq!(HeaderName::from_str("i"), HeaderName::CallId);
264 assert_eq!(HeaderName::from_str("CSeq"), HeaderName::CSeq);
265 assert_eq!(
266 HeaderName::from_str("X-Custom"),
267 HeaderName::Other("x-custom".to_string())
268 );
269 }
270
271 #[test]
272 fn test_header_name_display() {
273 assert_eq!(HeaderName::Via.as_str(), "Via");
274 assert_eq!(HeaderName::CallId.as_str(), "Call-ID");
275 assert_eq!(HeaderName::ContentType.as_str(), "Content-Type");
276 }
277
278 #[test]
279 fn test_headers_collection() {
280 let mut headers = Headers::new();
281 headers.add(HeaderName::Via, "SIP/2.0/UDP 10.0.0.1:5060;branch=z9hG4bK776asdhds");
282 headers.add(HeaderName::From, "<sip:alice@atlanta.com>;tag=1928301774");
283 headers.add(HeaderName::To, "<sip:bob@biloxi.com>");
284 headers.add(HeaderName::CallId, "a84b4c76e66710@pc33.atlanta.com");
285
286 assert_eq!(headers.len(), 4);
287 assert!(!headers.is_empty());
288
289 assert_eq!(
290 headers.get(&HeaderName::From).unwrap().as_str(),
291 "<sip:alice@atlanta.com>;tag=1928301774"
292 );
293
294 headers.add(HeaderName::Via, "SIP/2.0/UDP 10.0.0.2:5060;branch=z9hG4bKnashds8");
296 assert_eq!(headers.get_all(&HeaderName::Via).len(), 2);
297 }
298
299 #[test]
300 fn test_headers_set_replaces() {
301 let mut headers = Headers::new();
302 headers.add(HeaderName::ContentLength, "0");
303 headers.set(HeaderName::ContentLength, "150");
304 assert_eq!(headers.get(&HeaderName::ContentLength).unwrap().as_str(), "150");
305 assert_eq!(headers.len(), 1);
306 }
307
308 #[test]
309 fn test_headers_remove() {
310 let mut headers = Headers::new();
311 headers.add(HeaderName::Via, "SIP/2.0/UDP 10.0.0.1:5060");
312 headers.add(HeaderName::From, "<sip:alice@atlanta.com>");
313 headers.remove(&HeaderName::Via);
314 assert!(headers.get(&HeaderName::Via).is_none());
315 assert_eq!(headers.len(), 1);
316 }
317
318 #[test]
319 fn test_extract_tag() {
320 assert_eq!(
321 extract_tag("<sip:alice@atlanta.com>;tag=1928301774"),
322 Some("1928301774".to_string())
323 );
324 assert_eq!(extract_tag("<sip:bob@biloxi.com>"), None);
325 assert_eq!(
326 extract_tag("\"Alice\" <sip:alice@atlanta.com>;tag=abc123"),
327 Some("abc123".to_string())
328 );
329 }
330
331 #[test]
332 fn test_extract_uri() {
333 assert_eq!(
334 extract_uri("<sip:alice@atlanta.com>;tag=1928301774"),
335 Some("sip:alice@atlanta.com".to_string())
336 );
337 assert_eq!(
338 extract_uri("\"Alice\" <sip:alice@atlanta.com>"),
339 Some("sip:alice@atlanta.com".to_string())
340 );
341 assert_eq!(
342 extract_uri("sip:bob@biloxi.com"),
343 Some("sip:bob@biloxi.com".to_string())
344 );
345 }
346
347 #[test]
348 fn test_generate_tag() {
349 let tag = generate_tag();
350 assert!(!tag.is_empty());
351 let tag2 = generate_tag();
353 assert_ne!(tag, tag2);
354 }
355
356 #[test]
357 fn test_generate_branch() {
358 let branch = generate_branch();
359 assert!(branch.starts_with("z9hG4bK"));
360 }
361
362 #[test]
363 fn test_header_display() {
364 let header = Header::new(HeaderName::From, "<sip:alice@atlanta.com>;tag=123");
365 assert_eq!(
366 header.to_string(),
367 "From: <sip:alice@atlanta.com>;tag=123"
368 );
369 }
370}