1use crate::sip::{
2 headers::untyped::*,
3 headers::{Header, Headers},
4 uri::Branch,
5 Error, Method, StatusCode, Uri, Version,
6};
7
8pub trait HasHeaders {
9 fn headers(&self) -> &Headers;
10 fn headers_mut(&mut self) -> &mut Headers;
11}
12
13macro_rules! header_get {
14 ($iter:expr, $variant:path, $err:expr) => {
15 $iter
16 .find_map(|h| {
17 if let $variant(inner) = h {
18 Some(inner)
19 } else {
20 None
21 }
22 })
23 .ok_or_else(|| $err)
24 };
25}
26
27macro_rules! header_get_mut {
28 ($iter:expr, $variant:path, $err:expr) => {
29 $iter
30 .find_map(|h| {
31 if let $variant(inner) = h {
32 Some(inner)
33 } else {
34 None
35 }
36 })
37 .ok_or_else(|| $err)
38 };
39}
40
41macro_rules! header_opt {
42 ($iter:expr, $variant:path) => {
43 $iter.find_map(|h| {
44 if let $variant(inner) = h {
45 Some(inner)
46 } else {
47 None
48 }
49 })
50 };
51}
52
53macro_rules! all_headers {
54 ($iter:expr, $variant:path) => {
55 $iter
56 .filter_map(|h| {
57 if let $variant(inner) = h {
58 Some(inner)
59 } else {
60 None
61 }
62 })
63 .collect()
64 };
65}
66
67pub trait HeadersExt: HasHeaders {
68 fn to_header(&self) -> Result<&To, Error> {
69 header_get!(
70 self.headers().iter(),
71 Header::To,
72 Error::MissingHeader("To".into())
73 )
74 }
75 fn to_header_mut(&mut self) -> Result<&mut To, Error> {
76 header_get_mut!(
77 self.headers_mut().iter_mut(),
78 Header::To,
79 Error::MissingHeader("To".into())
80 )
81 }
82 fn from_header(&self) -> Result<&From, Error> {
83 header_get!(
84 self.headers().iter(),
85 Header::From,
86 Error::MissingHeader("From".into())
87 )
88 }
89 fn from_header_mut(&mut self) -> Result<&mut From, Error> {
90 header_get_mut!(
91 self.headers_mut().iter_mut(),
92 Header::From,
93 Error::MissingHeader("From".into())
94 )
95 }
96 fn via_header(&self) -> Result<&Via, Error> {
97 header_get!(
98 self.headers().iter(),
99 Header::Via,
100 Error::MissingHeader("Via".into())
101 )
102 }
103 fn top_via_header(&self) -> Result<Via, Error> {
104 self.via_header()?.first_value()
105 }
106 fn via_header_mut(&mut self) -> Result<&mut Via, Error> {
107 header_get_mut!(
108 self.headers_mut().iter_mut(),
109 Header::Via,
110 Error::MissingHeader("Via".into())
111 )
112 }
113 fn call_id_header(&self) -> Result<&CallId, Error> {
114 header_get!(
115 self.headers().iter(),
116 Header::CallId,
117 Error::MissingHeader("Call-ID".into())
118 )
119 }
120 fn call_id_header_mut(&mut self) -> Result<&mut CallId, Error> {
121 header_get_mut!(
122 self.headers_mut().iter_mut(),
123 Header::CallId,
124 Error::MissingHeader("Call-ID".into())
125 )
126 }
127 fn cseq_header(&self) -> Result<&CSeq, Error> {
128 header_get!(
129 self.headers().iter(),
130 Header::CSeq,
131 Error::MissingHeader("CSeq".into())
132 )
133 }
134 fn cseq_header_mut(&mut self) -> Result<&mut CSeq, Error> {
135 header_get_mut!(
136 self.headers_mut().iter_mut(),
137 Header::CSeq,
138 Error::MissingHeader("CSeq".into())
139 )
140 }
141 fn max_forwards_header(&self) -> Result<&MaxForwards, Error> {
142 header_get!(
143 self.headers().iter(),
144 Header::MaxForwards,
145 Error::MissingHeader("Max-Forwards".into())
146 )
147 }
148 fn max_forwards_header_mut(&mut self) -> Result<&mut MaxForwards, Error> {
149 header_get_mut!(
150 self.headers_mut().iter_mut(),
151 Header::MaxForwards,
152 Error::MissingHeader("Max-Forwards".into())
153 )
154 }
155 fn contact_header(&self) -> Result<&Contact, Error> {
156 header_get!(
157 self.headers().iter(),
158 Header::Contact,
159 Error::MissingHeader("Contact".into())
160 )
161 }
162 fn contact_header_mut(&mut self) -> Result<&mut Contact, Error> {
163 header_get_mut!(
164 self.headers_mut().iter_mut(),
165 Header::Contact,
166 Error::MissingHeader("Contact".into())
167 )
168 }
169 fn contact_headers(&self) -> Vec<&Contact> {
170 all_headers!(self.headers().iter(), Header::Contact)
171 }
172 fn typed_contact_headers(&self) -> Result<Vec<crate::sip::typed::Contact>, Error> {
173 let mut contacts = Vec::new();
174 for contact in self.contact_headers() {
175 contacts.extend(crate::sip::typed::Contact::parse_header_list(
176 contact.value(),
177 )?);
178 }
179 Ok(contacts)
180 }
181 fn record_route_headers(&self) -> Vec<&RecordRoute> {
182 all_headers!(self.headers().iter(), Header::RecordRoute)
183 }
184 fn record_route_header(&self) -> Option<&RecordRoute> {
185 header_opt!(self.headers().iter(), Header::RecordRoute)
186 }
187 fn typed_record_route_headers(&self) -> Result<Vec<crate::sip::typed::RecordRoute>, Error> {
188 let mut rrs = Vec::new();
189 for rr in self.record_route_headers() {
190 rrs.extend(crate::sip::typed::RecordRoute::parse_header_list(
191 rr.value(),
192 )?);
193 }
194 Ok(rrs)
195 }
196 fn route_headers(&self) -> Vec<&Route> {
197 all_headers!(self.headers().iter(), Header::Route)
198 }
199 fn route_header(&self) -> Option<&Route> {
200 header_opt!(self.headers().iter(), Header::Route)
201 }
202 fn typed_route_headers(&self) -> Result<Vec<crate::sip::typed::Route>, Error> {
203 let mut routes = Vec::new();
204 for r in self.route_headers() {
205 routes.extend(crate::sip::typed::Route::parse_header_list(r.value())?);
206 }
207 Ok(routes)
208 }
209 fn user_agent_header(&self) -> Option<&UserAgent> {
210 header_opt!(self.headers().iter(), Header::UserAgent)
211 }
212 fn authorization_header(&self) -> Option<&Authorization> {
213 header_opt!(self.headers().iter(), Header::Authorization)
214 }
215 fn www_authenticate_header(&self) -> Option<&WwwAuthenticate> {
216 header_opt!(self.headers().iter(), Header::WwwAuthenticate)
217 }
218 fn expires_header(&self) -> Option<&Expires> {
219 header_opt!(self.headers().iter(), Header::Expires)
220 }
221 fn min_expires_header(&self) -> Option<&MinExpires> {
222 header_opt!(self.headers().iter(), Header::MinExpires)
223 }
224 fn reason_header(&self) -> Option<&crate::sip::headers::untyped::Reason> {
225 header_opt!(self.headers().iter(), Header::Reason)
226 }
227 fn refer_to_header(&self) -> Option<&crate::sip::headers::untyped::ReferTo> {
228 header_opt!(self.headers().iter(), Header::ReferTo)
229 }
230 fn referred_by_header(&self) -> Option<&crate::sip::headers::untyped::ReferredBy> {
231 header_opt!(self.headers().iter(), Header::ReferredBy)
232 }
233 fn session_expires_header(&self) -> Option<&crate::sip::headers::untyped::SessionExpires> {
234 header_opt!(self.headers().iter(), Header::SessionExpires)
235 }
236 fn p_asserted_identity_header(
237 &self,
238 ) -> Option<&crate::sip::headers::untyped::PAssertedIdentity> {
239 header_opt!(self.headers().iter(), Header::PAssertedIdentity)
240 }
241 fn call_info_header(&self) -> Option<&CallInfo> {
242 header_opt!(self.headers().iter(), Header::CallInfo)
243 }
244 fn call_info_headers(&self) -> Vec<&CallInfo> {
245 all_headers!(self.headers().iter(), Header::CallInfo)
246 }
247 fn user_to_user_header(&self) -> Option<&UserToUser> {
248 header_opt!(self.headers().iter(), Header::UserToUser)
249 }
250 fn user_to_user_headers(&self) -> Vec<&UserToUser> {
251 all_headers!(self.headers().iter(), Header::UserToUser)
252 }
253 fn replaces_header(&self) -> Option<&crate::sip::headers::untyped::Replaces> {
254 header_opt!(self.headers().iter(), Header::Replaces)
255 }
256 fn privacy_header(&self) -> Option<&crate::sip::headers::untyped::Privacy> {
257 header_opt!(self.headers().iter(), Header::Privacy)
258 }
259 fn path_headers(&self) -> Vec<&crate::sip::headers::untyped::Path> {
260 all_headers!(self.headers().iter(), Header::Path)
261 }
262 fn rseq_value(&self) -> Option<u32> {
263 self.headers().iter().find_map(|h| {
264 if let Header::RSeq(r) = h {
265 r.value().trim().parse().ok()
266 } else {
267 None
268 }
269 })
270 }
271 fn rack_value(&self) -> Option<(u32, u32, Method)> {
272 self.headers().iter().find_map(|h| {
273 if let Header::RAck(r) = h {
274 let v = r.value();
275 let mut parts = v.split_whitespace();
276 let rseq = parts.next()?.parse::<u32>().ok()?;
277 let cseq = parts.next()?.parse::<u32>().ok()?;
278 let method = parts.next()?.parse::<Method>().ok()?;
279 Some((rseq, cseq, method))
280 } else {
281 None
282 }
283 })
284 }
285 fn header_value(&self, name: &str) -> Option<String> {
286 self.headers().iter().find_map(|h| {
287 if h.name().eq_ignore_ascii_case(name) {
288 Some(h.value().trim().to_string())
289 } else {
290 None
291 }
292 })
293 }
294 fn header_contains_token(&self, name: &str, token: &str) -> bool {
295 self.headers().iter().any(|header| match header {
296 Header::Supported(value) if name.eq_ignore_ascii_case("Supported") => value
297 .value()
298 .split(',')
299 .any(|item| item.trim().eq_ignore_ascii_case(token)),
300 Header::Require(value) if name.eq_ignore_ascii_case("Require") => value
301 .value()
302 .split(',')
303 .any(|item| item.trim().eq_ignore_ascii_case(token)),
304 _ => false,
305 })
306 }
307 fn transaction_id(&self) -> Result<Option<Branch>, Error> {
308 use crate::sip::headers::untyped::ToTypedHeader;
309 Ok(self.top_via_header()?.typed()?.branch().cloned())
310 }
311}
312
313#[derive(Debug, PartialEq, Eq, Clone)]
314pub struct Request {
315 pub method: Method,
316 pub uri: Uri,
317 pub version: Version,
318 pub headers: Headers,
319 pub body: Vec<u8>,
320}
321
322impl Request {
323 pub fn method(&self) -> &Method {
324 &self.method
325 }
326 pub fn uri(&self) -> &Uri {
327 &self.uri
328 }
329 pub fn destination(&self) -> Uri {
330 for route in self.route_headers() {
331 if let Ok(mut routes) = crate::sip::typed::Route::parse_header_list(route.value()) {
332 if let Some(route) = routes.drain(..).next() {
333 return route.uri;
334 }
335 }
336 }
337 self.uri.clone()
338 }
339 pub fn version(&self) -> &Version {
340 &self.version
341 }
342 pub fn body(&self) -> &Vec<u8> {
343 &self.body
344 }
345 pub fn body_mut(&mut self) -> &mut Vec<u8> {
346 &mut self.body
347 }
348}
349
350impl HasHeaders for Request {
351 fn headers(&self) -> &Headers {
352 &self.headers
353 }
354 fn headers_mut(&mut self) -> &mut Headers {
355 &mut self.headers
356 }
357}
358
359impl HeadersExt for Request {}
360
361impl std::fmt::Display for Request {
362 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
363 write!(
364 f,
365 "{} {} {}\r\n{}\r\n{}",
366 self.method,
367 self.uri,
368 self.version,
369 self.headers,
370 String::from_utf8_lossy(&self.body)
371 )
372 }
373}
374
375impl Request {
376 pub fn to_bytes(&self) -> Vec<u8> {
388 let head = format!(
389 "{} {} {}\r\n{}\r\n",
390 self.method, self.uri, self.version, self.headers
391 );
392 let mut buf = head.into_bytes();
393 buf.extend_from_slice(&self.body);
394 buf
395 }
396}
397
398impl std::convert::TryFrom<Vec<u8>> for Request {
399 type Error = Error;
400 fn try_from(bytes: Vec<u8>) -> Result<Self, Error> {
401 match crate::sip::parser::parse_message(&bytes)? {
402 SipMessage::Request(r) => Ok(r),
403 SipMessage::Response(_) => {
404 Err(Error::Unexpected("expected Request, got Response".into()))
405 }
406 }
407 }
408}
409
410impl std::convert::TryFrom<&[u8]> for Request {
411 type Error = Error;
412 fn try_from(bytes: &[u8]) -> Result<Self, Error> {
413 match crate::sip::parser::parse_message(bytes)? {
414 SipMessage::Request(r) => Ok(r),
415 SipMessage::Response(_) => {
416 Err(Error::Unexpected("expected Request, got Response".into()))
417 }
418 }
419 }
420}
421
422impl std::convert::TryFrom<&str> for Request {
423 type Error = Error;
424 fn try_from(s: &str) -> Result<Self, Error> {
425 Self::try_from(s.as_bytes())
426 }
427}
428
429impl std::convert::TryFrom<String> for Request {
430 type Error = Error;
431 fn try_from(s: String) -> Result<Self, Error> {
432 Self::try_from(s.as_bytes())
433 }
434}
435
436impl std::convert::From<Request> for String {
437 fn from(r: Request) -> String {
438 r.to_string()
439 }
440}
441
442impl std::convert::From<Request> for Vec<u8> {
443 fn from(r: Request) -> Vec<u8> {
444 r.to_bytes()
445 }
446}
447
448#[derive(Debug, PartialEq, Eq, Clone)]
449pub struct Response {
450 pub status_code: StatusCode,
451 pub version: Version,
452 pub headers: Headers,
453 pub body: Vec<u8>,
454}
455
456impl Response {
457 pub fn status_code(&self) -> &StatusCode {
458 &self.status_code
459 }
460 pub fn version(&self) -> &Version {
461 &self.version
462 }
463 pub fn body(&self) -> &Vec<u8> {
464 &self.body
465 }
466 pub fn body_mut(&mut self) -> &mut Vec<u8> {
467 &mut self.body
468 }
469 pub fn reason_phrase(&self) -> Option<&str> {
470 self.headers.iter().find_map(|h| {
471 if let Header::Reason(r) = h {
472 Some(r.value())
473 } else {
474 None
475 }
476 })
477 }
478
479 pub fn via_received(&self) -> Option<crate::sip::uri::HostWithPort> {
481 use crate::sip::HeadersExt;
482 self.top_via_header().ok().and_then(|via| {
483 via.typed()
484 .ok()
485 .and_then(|typed_via: crate::sip::typed::Via| {
486 let received = typed_via.params.iter().find_map(|p| {
487 if let crate::sip::uri::Param::Received(r) = p {
488 r.parse().ok().map(crate::sip::uri::Host::IpAddr)
489 } else {
490 None
491 }
492 });
493 let rport = typed_via.params.iter().find_map(|p| {
494 if let crate::sip::uri::Param::Rport(rport) = p {
495 *rport
496 } else {
497 None
498 }
499 });
500 received.map(|host| crate::sip::uri::HostWithPort {
501 host,
502 port: rport.map(crate::sip::transport::Port),
503 })
504 })
505 })
506 }
507}
508
509impl HasHeaders for Response {
510 fn headers(&self) -> &Headers {
511 &self.headers
512 }
513 fn headers_mut(&mut self) -> &mut Headers {
514 &mut self.headers
515 }
516}
517
518impl HeadersExt for Response {}
519
520impl Default for Response {
521 fn default() -> Self {
522 Response {
523 status_code: StatusCode::OK,
524 version: Version::V2,
525 headers: Headers::default(),
526 body: Vec::new(),
527 }
528 }
529}
530
531impl std::fmt::Display for Response {
532 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
533 write!(
534 f,
535 "{} {} {}\r\n{}\r\n{}",
536 self.version,
537 self.status_code.code(),
538 self.status_code.text(),
539 self.headers,
540 String::from_utf8_lossy(&self.body)
541 )
542 }
543}
544
545impl Response {
546 pub fn to_bytes(&self) -> Vec<u8> {
551 let head = format!(
552 "{} {} {}\r\n{}\r\n",
553 self.version,
554 self.status_code.code(),
555 self.status_code.text(),
556 self.headers
557 );
558 let mut buf = head.into_bytes();
559 buf.extend_from_slice(&self.body);
560 buf
561 }
562}
563
564impl std::convert::TryFrom<Vec<u8>> for Response {
565 type Error = Error;
566 fn try_from(bytes: Vec<u8>) -> Result<Self, Error> {
567 match crate::sip::parser::parse_message(&bytes)? {
568 SipMessage::Response(r) => Ok(r),
569 SipMessage::Request(_) => {
570 Err(Error::Unexpected("expected Response, got Request".into()))
571 }
572 }
573 }
574}
575
576impl std::convert::TryFrom<&[u8]> for Response {
577 type Error = Error;
578 fn try_from(bytes: &[u8]) -> Result<Self, Error> {
579 match crate::sip::parser::parse_message(bytes)? {
580 SipMessage::Response(r) => Ok(r),
581 SipMessage::Request(_) => {
582 Err(Error::Unexpected("expected Response, got Request".into()))
583 }
584 }
585 }
586}
587
588impl std::convert::TryFrom<&str> for Response {
589 type Error = Error;
590 fn try_from(s: &str) -> Result<Self, Error> {
591 Self::try_from(s.as_bytes())
592 }
593}
594
595impl std::convert::TryFrom<String> for Response {
596 type Error = Error;
597 fn try_from(s: String) -> Result<Self, Error> {
598 Self::try_from(s.as_bytes())
599 }
600}
601
602impl std::convert::From<Response> for String {
603 fn from(r: Response) -> String {
604 r.to_string()
605 }
606}
607
608impl std::convert::From<Response> for Vec<u8> {
609 fn from(r: Response) -> Vec<u8> {
610 r.to_bytes()
611 }
612}
613
614impl std::convert::TryFrom<bytes::Bytes> for SipMessage {
615 type Error = Error;
616 fn try_from(bytes: bytes::Bytes) -> Result<Self, Error> {
617 crate::sip::parser::parse_message(&bytes)
618 }
619}
620
621#[derive(Debug, PartialEq, Eq, Clone)]
622pub enum SipMessage {
623 Request(Request),
624 Response(Response),
625}
626
627impl SipMessage {
628 pub fn is_request(&self) -> bool {
629 matches!(self, SipMessage::Request(_))
630 }
631 pub fn is_response(&self) -> bool {
632 matches!(self, SipMessage::Response(_))
633 }
634
635 pub fn start_line(&self) -> String {
640 match self {
641 SipMessage::Request(r) => format!("{} {} {}", r.method, r.uri, r.version),
642 SipMessage::Response(r) => format!(
643 "{} {} {}",
644 r.version,
645 r.status_code.code(),
646 r.status_code.text()
647 ),
648 }
649 }
650}
651
652impl HasHeaders for SipMessage {
653 fn headers(&self) -> &Headers {
654 match self {
655 SipMessage::Request(r) => r.headers(),
656 SipMessage::Response(r) => r.headers(),
657 }
658 }
659 fn headers_mut(&mut self) -> &mut Headers {
660 match self {
661 SipMessage::Request(r) => r.headers_mut(),
662 SipMessage::Response(r) => r.headers_mut(),
663 }
664 }
665}
666
667impl HeadersExt for SipMessage {}
668
669impl std::fmt::Display for SipMessage {
670 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
671 match self {
672 SipMessage::Request(r) => write!(f, "{}", r),
673 SipMessage::Response(r) => write!(f, "{}", r),
674 }
675 }
676}
677
678impl SipMessage {
679 pub fn to_bytes(&self) -> Vec<u8> {
684 match self {
685 SipMessage::Request(r) => r.to_bytes(),
686 SipMessage::Response(r) => r.to_bytes(),
687 }
688 }
689}
690
691impl std::convert::TryFrom<Vec<u8>> for SipMessage {
692 type Error = Error;
693 fn try_from(bytes: Vec<u8>) -> Result<Self, Error> {
694 crate::sip::parser::parse_message(&bytes)
695 }
696}
697
698impl std::convert::TryFrom<&[u8]> for SipMessage {
699 type Error = Error;
700 fn try_from(bytes: &[u8]) -> Result<Self, Error> {
701 crate::sip::parser::parse_message(bytes)
702 }
703}
704
705impl std::convert::TryFrom<&str> for SipMessage {
706 type Error = Error;
707 fn try_from(s: &str) -> Result<Self, Error> {
708 Self::try_from(s.as_bytes())
709 }
710}
711
712impl std::convert::TryFrom<String> for SipMessage {
713 type Error = Error;
714 fn try_from(s: String) -> Result<Self, Error> {
715 Self::try_from(s.as_bytes())
716 }
717}
718
719impl std::convert::From<Request> for SipMessage {
720 fn from(r: Request) -> SipMessage {
721 SipMessage::Request(r)
722 }
723}
724
725impl std::convert::From<Response> for SipMessage {
726 fn from(r: Response) -> SipMessage {
727 SipMessage::Response(r)
728 }
729}
730
731impl std::convert::TryFrom<SipMessage> for Request {
732 type Error = Error;
733 fn try_from(m: SipMessage) -> Result<Self, Error> {
734 match m {
735 SipMessage::Request(r) => Ok(r),
736 SipMessage::Response(_) => Err(Error::Unexpected("expected Request".into())),
737 }
738 }
739}
740
741impl std::convert::TryFrom<SipMessage> for Response {
742 type Error = Error;
743 fn try_from(m: SipMessage) -> Result<Self, Error> {
744 match m {
745 SipMessage::Response(r) => Ok(r),
746 SipMessage::Request(_) => Err(Error::Unexpected("expected Response".into())),
747 }
748 }
749}
750
751impl std::convert::From<SipMessage> for String {
752 fn from(m: SipMessage) -> String {
753 m.to_string()
754 }
755}
756
757impl std::convert::From<SipMessage> for Vec<u8> {
758 fn from(m: SipMessage) -> Vec<u8> {
759 m.to_bytes()
760 }
761}
762
763#[cfg(test)]
764mod tests {
765 use super::{HasHeaders, HeadersExt, Request, Response, SipMessage};
766 use crate::sip::{Header, Method};
767
768 fn invite_request() -> &'static str {
769 concat!(
770 "INVITE sip:bob@restsend.com SIP/2.0\r\n",
771 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bK776asdhds\r\n",
772 "Max-Forwards: 70\r\n",
773 "To: Bob <sip:bob@restsend.com>\r\n",
774 "From: Alice <sip:alice@restsend.com>;tag=1928301774\r\n",
775 "Call-ID: a84b4c76e66710@ua.restsend.com\r\n",
776 "CSeq: 314159 INVITE\r\n",
777 "Contact: <sip:alice@ua.restsend.com>\r\n",
778 "Content-Type: application/sdp\r\n",
779 "Content-Length: 0\r\n",
780 "\r\n"
781 )
782 }
783
784 fn register_request() -> &'static str {
785 concat!(
786 "REGISTER sip:registrar.restsend.com SIP/2.0\r\n",
787 "Via: SIP/2.0/UDP ua.restsend.com:5060;branch=z9hG4bKnashds8\r\n",
788 "Max-Forwards: 70\r\n",
789 "To: Bob <sip:bob@restsend.com>\r\n",
790 "From: Bob <sip:bob@restsend.com>;tag=456248\r\n",
791 "Call-ID: 843817637684230@998sdasdh09\r\n",
792 "CSeq: 1826 REGISTER\r\n",
793 "Contact: <sip:bob@192.0.2.4>\r\n",
794 "Expires: 7200\r\n",
795 "Content-Length: 0\r\n",
796 "\r\n"
797 )
798 }
799
800 fn ok_response() -> &'static str {
801 concat!(
802 "SIP/2.0 200 OK\r\n",
803 "Via: SIP/2.0/UDP proxy1.restsend.com;branch=z9hG4bK4b43c2ff8.1;received=192.0.2.3\r\n",
804 "Via: SIP/2.0/UDP proxy2.restsend.com;branch=z9hG4bK77ef4c2312983.1;received=192.0.2.2\r\n",
805 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bK776asdhds;received=192.0.2.1\r\n",
806 "To: Bob <sip:bob@restsend.com>;tag=a6c85cf\r\n",
807 "From: Alice <sip:alice@restsend.com>;tag=1928301774\r\n",
808 "Call-ID: a84b4c76e66710@ua.restsend.com\r\n",
809 "CSeq: 314159 INVITE\r\n",
810 "Contact: <sip:bob@192.0.2.4>\r\n",
811 "Content-Type: application/sdp\r\n",
812 "Content-Length: 131\r\n",
813 "\r\n",
814 "v=0\r\no=bob 2890844527 2890844527 IN IP4 192.0.2.4\r\ns=-\r\nc=IN IP4 192.0.2.4\r\nt=0 0\r\nm=audio 3456 RTP/AVP 0\r\na=rtpmap:0 PCMU/8000"
815 )
816 }
817
818 #[test]
819 fn parse_invite_request() {
820 let req: Request = invite_request().try_into().unwrap();
821 assert_eq!(req.method, Method::Invite);
822 assert_eq!(req.uri.to_string(), "sip:bob@restsend.com");
823 assert_eq!(
824 req.from_header().unwrap().value(),
825 "Alice <sip:alice@restsend.com>;tag=1928301774"
826 );
827 assert_eq!(
828 req.to_header().unwrap().value(),
829 "Bob <sip:bob@restsend.com>"
830 );
831 assert_eq!(
832 req.call_id_header().unwrap().value(),
833 "a84b4c76e66710@ua.restsend.com"
834 );
835 }
836
837 #[test]
838 fn parse_register_request() {
839 let req: Request = register_request().try_into().unwrap();
840 assert_eq!(req.method, Method::Register);
841 let contacts = req.typed_contact_headers().unwrap();
842 assert_eq!(contacts.len(), 1);
843 assert_eq!(contacts[0].uri.to_string(), "sip:bob@192.0.2.4");
844 }
845
846 #[test]
847 fn parse_ok_response() {
848 let resp: Response = ok_response().try_into().unwrap();
849 assert_eq!(resp.status_code.code(), 200);
850 assert_eq!(resp.body, b"v=0\r\no=bob 2890844527 2890844527 IN IP4 192.0.2.4\r\ns=-\r\nc=IN IP4 192.0.2.4\r\nt=0 0\r\nm=audio 3456 RTP/AVP 0\r\na=rtpmap:0 PCMU/8000");
851 }
852
853 #[test]
854 fn request_binary_body_survives_serialization() {
855 let mut req: Request = invite_request().try_into().unwrap();
860 let binary_body: Vec<u8> = vec![0x00, 0x01, 0x80, 0xFF, 0xC0, 0xC1, 0xFE, 0x02, 0x7F, 0x90];
861 req.body = binary_body.clone();
862
863 let wire: Vec<u8> = req.into();
864
865 assert!(
866 wire.ends_with(&binary_body),
867 "binary body corrupted during serialization: got tail {:?}",
868 &wire[wire.len().saturating_sub(binary_body.len() + 4)..]
869 );
870 }
871
872 #[test]
873 fn response_binary_body_survives_serialization() {
874 let mut resp: Response = ok_response().try_into().unwrap();
875 let binary_body: Vec<u8> = vec![0x00, 0x80, 0xFF, 0xC0, 0xC1, 0xFE, 0x02];
876 resp.body = binary_body.clone();
877
878 let wire: Vec<u8> = resp.into();
879
880 assert!(
881 wire.ends_with(&binary_body),
882 "binary body corrupted during serialization"
883 );
884 }
885
886 #[test]
887 fn response_has_multiple_via_headers() {
888 let resp: Response = ok_response().try_into().unwrap();
889 let vias: Vec<_> = resp
890 .headers()
891 .iter()
892 .filter(|h| matches!(h, Header::Via(_)))
893 .collect();
894 assert_eq!(vias.len(), 3);
895 }
896
897 #[test]
898 fn top_via_header_returns_first_value_from_combined_header() {
899 let resp: Response = concat!(
900 "SIP/2.0 401 Unauthorized\r\n",
901 "Via: SIP/2.0/UDP 172.22.22.80:5062;received=172.22.22.80;rport=5062;branch=z9hG4bKfirst,SIP/2.0/TCP 10.0.13.70:5060;branch=z9hG4bKsecond\r\n",
902 "From: <sip:001010000000001@ims.example.com>;tag=e80c1d8c\r\n",
903 "To: <sip:001010000000001@ims.example.com>;tag=2e518\r\n",
904 "Call-ID: 9e353fc94f78064f@10.0.13.70\r\n",
905 "CSeq: 1 REGISTER\r\n",
906 "Content-Length: 0\r\n",
907 "\r\n",
908 )
909 .try_into()
910 .unwrap();
911
912 assert_eq!(
913 resp.via_header().unwrap().value(),
914 "SIP/2.0/UDP 172.22.22.80:5062;received=172.22.22.80;rport=5062;branch=z9hG4bKfirst,SIP/2.0/TCP 10.0.13.70:5060;branch=z9hG4bKsecond"
915 );
916 assert_eq!(
917 resp.top_via_header().unwrap().value(),
918 "SIP/2.0/UDP 172.22.22.80:5062;received=172.22.22.80;rport=5062;branch=z9hG4bKfirst"
919 );
920 }
921
922 #[test]
923 fn headers_push_front() {
924 let mut req: Request = invite_request().try_into().unwrap();
925 let orig_first = req.headers.iter().next().unwrap().name().to_string();
926 req.headers
927 .push_front(Header::MaxForwards(crate::sip::MaxForwards::new("10")));
928 assert_eq!(req.headers.iter().next().unwrap().name(), "Max-Forwards");
929 assert_eq!(req.headers.iter().nth(1).unwrap().name(), orig_first);
930 }
931
932 #[test]
933 fn new_headers_reason_roundtrip() {
934 let msg: SipMessage = concat!(
935 "BYE sip:alice@restsend.com SIP/2.0\r\n",
936 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bK776asdhds\r\n",
937 "From: <sip:bob@restsend.com>;tag=a6c85cf\r\n",
938 "To: <sip:alice@restsend.com>;tag=1928301774\r\n",
939 "Call-ID: a84b4c76e66710@ua.restsend.com\r\n",
940 "CSeq: 231 BYE\r\n",
941 "Reason: SIP ;cause=200 ;text=\"Call completed elsewhere\"\r\n",
942 "Content-Length: 0\r\n",
943 "\r\n"
944 )
945 .try_into()
946 .unwrap();
947 let reason = msg.reason_header().unwrap();
948 assert!(reason.value().contains("200"));
949 assert!(reason.value().contains("Call completed elsewhere"));
950 }
951
952 #[test]
953 fn new_headers_refer_to_roundtrip() {
954 let msg: SipMessage = concat!(
955 "REFER sip:bob@restsend.com SIP/2.0\r\n",
956 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bKkjshdyff\r\n",
957 "To: <sip:bob@restsend.com>\r\n",
958 "From: <sip:alice@restsend.com>;tag=xyz\r\n",
959 "Call-ID: 12345600@ua.restsend.com\r\n",
960 "CSeq: 1 REFER\r\n",
961 "Refer-To: <sip:carol@restsend.com>\r\n",
962 "Content-Length: 0\r\n",
963 "\r\n"
964 )
965 .try_into()
966 .unwrap();
967 assert!(msg.refer_to_header().is_some());
968 assert_eq!(
969 msg.refer_to_header().unwrap().value(),
970 "<sip:carol@restsend.com>"
971 );
972 }
973
974 #[test]
975 fn new_headers_session_expires_roundtrip() {
976 let msg: SipMessage = concat!(
977 "INVITE sip:bob@restsend.com SIP/2.0\r\n",
978 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bKtest\r\n",
979 "From: <sip:alice@restsend.com>;tag=abc\r\n",
980 "To: <sip:bob@restsend.com>\r\n",
981 "Call-ID: test@ua.restsend.com\r\n",
982 "CSeq: 1 INVITE\r\n",
983 "Session-Expires: 1800;refresher=uac\r\n",
984 "Min-SE: 90\r\n",
985 "Content-Length: 0\r\n",
986 "\r\n"
987 )
988 .try_into()
989 .unwrap();
990 let se = msg.session_expires_header().unwrap();
991 assert!(se.value().contains("1800"));
992 let min_se = msg.headers().iter().find_map(|h| {
993 if let Header::MinSE(m) = h {
994 Some(m)
995 } else {
996 None
997 }
998 });
999 assert!(min_se.is_some());
1000 assert_eq!(min_se.unwrap().value(), "90");
1001 }
1002
1003 #[test]
1004 fn new_headers_call_info() {
1005 let msg: SipMessage = concat!(
1006 "INVITE sip:bob@restsend.com SIP/2.0\r\n",
1007 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bKtest\r\n",
1008 "From: <sip:alice@restsend.com>;tag=abc\r\n",
1009 "To: <sip:bob@restsend.com>\r\n",
1010 "Call-ID: call-info-test@ua.restsend.com\r\n",
1011 "CSeq: 1 INVITE\r\n",
1012 "Call-Info: <http://www.example.com/alice/photo.jpg>;purpose=icon\r\n",
1013 "Call-Info: <http://www.example.com/alice/>;purpose=info\r\n",
1014 "Content-Length: 0\r\n",
1015 "\r\n"
1016 )
1017 .try_into()
1018 .unwrap();
1019 let first = msg.call_info_header().unwrap();
1020 assert!(first.value().contains("photo.jpg"));
1021 assert!(first.value().contains("purpose=icon"));
1022 let all = msg.call_info_headers();
1023 assert_eq!(all.len(), 2);
1024 assert!(all[1].value().contains("purpose=info"));
1025 }
1026
1027 #[test]
1028 fn new_headers_user_to_user() {
1029 let msg: SipMessage = concat!(
1030 "INVITE sip:bob@restsend.com SIP/2.0\r\n",
1031 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bKtest\r\n",
1032 "From: <sip:alice@restsend.com>;tag=abc\r\n",
1033 "To: <sip:bob@restsend.com>\r\n",
1034 "Call-ID: uui-test@ua.restsend.com\r\n",
1035 "CSeq: 1 INVITE\r\n",
1036 "User-to-User: 56a390f3d2b7310023a;encoding=hex;purpose=isdn-uui;content=isdn-uui\r\n",
1037 "Content-Length: 0\r\n",
1038 "\r\n"
1039 )
1040 .try_into()
1041 .unwrap();
1042 let uui = msg.user_to_user_header().unwrap();
1043 assert!(uui.value().starts_with("56a390f3d2b7310023a"));
1044 assert!(uui.value().contains("encoding=hex"));
1045 assert!(uui.value().contains("purpose=isdn-uui"));
1046 assert_eq!(msg.user_to_user_headers().len(), 1);
1047 }
1048
1049 #[test]
1050 fn new_headers_p_asserted_identity() {
1051 let msg: SipMessage = concat!(
1052 "INVITE sip:bob@restsend.com SIP/2.0\r\n",
1053 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bKtest\r\n",
1054 "From: <sip:anonymous@anonymous.invalid>;tag=abc\r\n",
1055 "To: <sip:bob@restsend.com>\r\n",
1056 "Call-ID: pai-test@ua.restsend.com\r\n",
1057 "CSeq: 1 INVITE\r\n",
1058 "P-Asserted-Identity: <sip:alice@restsend.com>\r\n",
1059 "Privacy: id\r\n",
1060 "Content-Length: 0\r\n",
1061 "\r\n"
1062 )
1063 .try_into()
1064 .unwrap();
1065 assert!(msg.p_asserted_identity_header().is_some());
1066 assert_eq!(
1067 msg.p_asserted_identity_header().unwrap().value(),
1068 "<sip:alice@restsend.com>"
1069 );
1070 assert!(msg.privacy_header().is_some());
1071 }
1072
1073 #[test]
1074 fn new_headers_replaces() {
1075 let msg: SipMessage = concat!(
1076 "INVITE sip:bob@restsend.com SIP/2.0\r\n",
1077 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bKtest\r\n",
1078 "From: <sip:alice@restsend.com>;tag=abc\r\n",
1079 "To: <sip:bob@restsend.com>\r\n",
1080 "Call-ID: transfer-test@ua.restsend.com\r\n",
1081 "CSeq: 1 INVITE\r\n",
1082 "Replaces: original-call-id@ua.restsend.com;to-tag=orig-to;from-tag=orig-from\r\n",
1083 "Content-Length: 0\r\n",
1084 "\r\n"
1085 )
1086 .try_into()
1087 .unwrap();
1088 let replaces = msg.replaces_header().unwrap();
1089 assert!(replaces.value().contains("original-call-id"));
1090 }
1091
1092 #[test]
1093 fn new_headers_rseq_rack() {
1094 let prack: SipMessage = concat!(
1095 "PRACK sip:bob@restsend.com SIP/2.0\r\n",
1096 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bKtest\r\n",
1097 "From: <sip:alice@restsend.com>;tag=abc\r\n",
1098 "To: <sip:bob@restsend.com>;tag=xyz\r\n",
1099 "Call-ID: prack-test@ua.restsend.com\r\n",
1100 "CSeq: 2 PRACK\r\n",
1101 "RAck: 776656 1 INVITE\r\n",
1102 "Content-Length: 0\r\n",
1103 "\r\n"
1104 )
1105 .try_into()
1106 .unwrap();
1107
1108 let rack = prack.rack_value().unwrap();
1109 assert_eq!(rack.0, 776656);
1110 assert_eq!(rack.1, 1);
1111 assert_eq!(rack.2, Method::Invite);
1112
1113 let provisional: SipMessage = concat!(
1114 "SIP/2.0 183 Session Progress\r\n",
1115 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bKtest\r\n",
1116 "From: <sip:alice@restsend.com>;tag=abc\r\n",
1117 "To: <sip:bob@restsend.com>;tag=xyz\r\n",
1118 "Call-ID: prack-test@ua.restsend.com\r\n",
1119 "CSeq: 1 INVITE\r\n",
1120 "RSeq: 776656\r\n",
1121 "Content-Length: 0\r\n",
1122 "\r\n"
1123 )
1124 .try_into()
1125 .unwrap();
1126
1127 assert_eq!(provisional.rseq_value(), Some(776656));
1128 }
1129
1130 #[test]
1131 fn new_headers_path_header() {
1132 let msg: SipMessage = concat!(
1133 "REGISTER sip:registrar.restsend.com SIP/2.0\r\n",
1134 "Via: SIP/2.0/TCP edge.restsend.com;branch=z9hG4bKtest\r\n",
1135 "From: <sip:alice@restsend.com>;tag=abc\r\n",
1136 "To: <sip:alice@restsend.com>\r\n",
1137 "Call-ID: path-test@edge.restsend.com\r\n",
1138 "CSeq: 1 REGISTER\r\n",
1139 "Path: <sip:edge.restsend.com;lr>\r\n",
1140 "Contact: <sip:alice@192.0.2.5:5060>\r\n",
1141 "Content-Length: 0\r\n",
1142 "\r\n"
1143 )
1144 .try_into()
1145 .unwrap();
1146 let paths = msg.path_headers();
1147 assert_eq!(paths.len(), 1);
1148 assert!(paths[0].value().contains("edge.restsend.com"));
1149 }
1150
1151 #[test]
1152 fn header_value_helper_case_insensitive() {
1153 let req: Request = invite_request().try_into().unwrap();
1154 let val = req.header_value("content-type");
1155 assert!(val.is_some());
1156 assert!(val.unwrap().contains("application/sdp"));
1157 }
1158
1159 #[test]
1160 fn header_contains_token_helper() {
1161 let msg: SipMessage = concat!(
1162 "INVITE sip:bob@restsend.com SIP/2.0\r\n",
1163 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bKtest\r\n",
1164 "From: <sip:alice@restsend.com>;tag=abc\r\n",
1165 "To: <sip:bob@restsend.com>\r\n",
1166 "Call-ID: test@pc33\r\n",
1167 "CSeq: 1 INVITE\r\n",
1168 "Supported: timer, 100rel, replaces\r\n",
1169 "Content-Length: 0\r\n",
1170 "\r\n"
1171 )
1172 .try_into()
1173 .unwrap();
1174 assert!(msg.header_contains_token("supported", "timer"));
1175 assert!(msg.header_contains_token("supported", "100rel"));
1176 assert!(!msg.header_contains_token("supported", "gruu"));
1177 assert!(!msg.header_contains_token("content-type", "application/sdp"));
1178 }
1179
1180 #[test]
1181 fn destination_from_request_without_route() {
1182 let req: Request = invite_request().try_into().unwrap();
1183 let dest = req.destination();
1184 assert_eq!(dest.to_string(), "sip:bob@restsend.com");
1185 }
1186
1187 #[test]
1188 fn destination_from_request_with_route() {
1189 let req: Request = concat!(
1190 "INVITE sip:bob@restsend.com SIP/2.0\r\n",
1191 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bKtest\r\n",
1192 "Route: <sip:proxy.restsend.com;lr>\r\n",
1193 "From: <sip:alice@restsend.com>;tag=abc\r\n",
1194 "To: <sip:bob@restsend.com>\r\n",
1195 "Call-ID: routed@pc33\r\n",
1196 "CSeq: 1 INVITE\r\n",
1197 "Content-Length: 0\r\n",
1198 "\r\n"
1199 )
1200 .try_into()
1201 .unwrap();
1202 let dest = req.destination();
1203 assert_eq!(dest.to_string(), "sip:proxy.restsend.com;lr");
1204 }
1205
1206 #[test]
1207 fn compact_form_headers_are_parsed() {
1208 let message: SipMessage = concat!(
1209 "INVITE sip:bob@restsend.com SIP/2.0\r\n",
1210 "v: SIP/2.0/UDP restsend.com:5060;branch=z9hG4bK-1\r\n",
1211 "f: <sip:alice@restsend.com>;tag=123\r\n",
1212 "t: <sip:bob@restsend.com>\r\n",
1213 "i: call-id-1\r\n",
1214 "m: <sip:alice@restsend.com>\r\n",
1215 "e: gzip\r\n",
1216 "l: 0\r\n",
1217 "c: application/sdp\r\n",
1218 "s: hello\r\n",
1219 "k: timer\r\n",
1220 "\r\n"
1221 )
1222 .try_into()
1223 .unwrap();
1224
1225 assert!(message.via_header().is_ok());
1226 assert!(message.from_header().is_ok());
1227 assert!(message.to_header().is_ok());
1228 assert!(message.call_id_header().is_ok());
1229 assert!(message.contact_header().is_ok());
1230 assert!(message
1231 .headers()
1232 .iter()
1233 .any(|header| matches!(header, Header::ContentEncoding(_))));
1234 assert!(message
1235 .headers()
1236 .iter()
1237 .any(|header| matches!(header, Header::ContentLength(_))));
1238 assert!(message
1239 .headers()
1240 .iter()
1241 .any(|header| matches!(header, Header::ContentType(_))));
1242 assert!(message
1243 .headers()
1244 .iter()
1245 .any(|header| matches!(header, Header::Subject(_))));
1246 assert!(message
1247 .headers()
1248 .iter()
1249 .any(|header| matches!(header, Header::Supported(_))));
1250 }
1251
1252 #[test]
1253 fn compact_form_refer_to_alias_r() {
1254 let msg: SipMessage = concat!(
1255 "REFER sip:bob@restsend.com SIP/2.0\r\n",
1256 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bKtest\r\n",
1257 "From: <sip:alice@restsend.com>;tag=abc\r\n",
1258 "To: <sip:bob@restsend.com>\r\n",
1259 "Call-ID: refer-compact@host\r\n",
1260 "CSeq: 1 REFER\r\n",
1261 "r: <sip:carol@restsend.com>\r\n",
1262 "Content-Length: 0\r\n",
1263 "\r\n"
1264 )
1265 .try_into()
1266 .unwrap();
1267 assert!(msg.refer_to_header().is_some());
1268 }
1269
1270 #[test]
1271 fn compact_form_session_expires_alias_x() {
1272 let msg: SipMessage = concat!(
1273 "INVITE sip:bob@restsend.com SIP/2.0\r\n",
1274 "Via: SIP/2.0/UDP ua.restsend.com;branch=z9hG4bKtest\r\n",
1275 "From: <sip:alice@restsend.com>;tag=abc\r\n",
1276 "To: <sip:bob@restsend.com>\r\n",
1277 "Call-ID: se-compact@host\r\n",
1278 "CSeq: 1 INVITE\r\n",
1279 "x: 1800\r\n",
1280 "Content-Length: 0\r\n",
1281 "\r\n"
1282 )
1283 .try_into()
1284 .unwrap();
1285 assert!(msg.session_expires_header().is_some());
1286 assert_eq!(msg.session_expires_header().unwrap().value(), "1800");
1287 }
1288}