use std::fmt;
use crate::error::HeaderError;
use crate::headers::grammar::{parse_u64, trim};
use crate::message::TypedHeader;
use crate::name::HeaderName;
pub const OPTION_TAG: &str = "100rel";
pub const MAX_FIRST_RSEQ: u32 = i32::MAX as u32;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct RSeq(pub u32);
impl TypedHeader for RSeq {
const NAME: HeaderName = HeaderName::RSeq;
fn decode(value: &[u8]) -> Result<Self, HeaderError> {
let n = parse_u64(trim(value), "RSeq")?;
u32::try_from(n)
.map(Self)
.map_err(|_| HeaderError::OutOfRange { header: "RSeq" })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RAck {
pub rseq: u32,
pub cseq: u32,
pub method: Vec<u8>,
}
impl TypedHeader for RAck {
const NAME: HeaderName = HeaderName::RAck;
fn decode(value: &[u8]) -> Result<Self, HeaderError> {
let mut parts = trim(value)
.split(u8::is_ascii_whitespace)
.filter(|p| !p.is_empty());
let bad = || HeaderError::Syntax { header: "RAck" };
let rseq = parse_u64(parts.next().ok_or_else(bad)?, "RAck")?;
let cseq = parse_u64(parts.next().ok_or_else(bad)?, "RAck")?;
let method = parts.next().ok_or_else(bad)?.to_vec();
if parts.next().is_some() || method.is_empty() {
return Err(bad());
}
Ok(Self {
rseq: u32::try_from(rseq).map_err(|_| HeaderError::OutOfRange { header: "RAck" })?,
cseq: u32::try_from(cseq).map_err(|_| HeaderError::OutOfRange { header: "RAck" })?,
method,
})
}
}
impl fmt::Display for RAck {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {} {}",
self.rseq,
self.cseq,
String::from_utf8_lossy(&self.method)
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Reliability {
Forbidden,
Permitted,
Required,
Refuse,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Offered {
pub supported: bool,
pub required: bool,
}
impl Offered {
#[must_use]
pub fn in_request(request: &crate::message::Request) -> Self {
let has = |name: &HeaderName| {
request
.headers
.get_all(name)
.any(|header| contains_tag(&header.value()))
};
Self {
supported: has(&HeaderName::Supported),
required: has(&HeaderName::Require),
}
}
}
fn contains_tag(value: &[u8]) -> bool {
value
.split(|&b| b == b',')
.any(|tag| trim(tag).eq_ignore_ascii_case(OPTION_TAG.as_bytes()))
}
#[must_use]
pub fn reliability(peer: Offered, enabled: bool) -> Reliability {
match (peer.required, peer.supported, enabled) {
(true, _, true) => Reliability::Required,
(true, _, false) => Reliability::Refuse,
(false, true, true) => Reliability::Permitted,
(false, _, _) => Reliability::Forbidden,
}
}
#[derive(Debug, Clone, Copy)]
pub struct Numbering {
next: u32,
outstanding: Option<u32>,
}
impl Numbering {
#[must_use]
pub fn starting_at(first: u32) -> Self {
Self {
next: first.clamp(1, MAX_FIRST_RSEQ),
outstanding: None,
}
}
pub fn allocate(&mut self) -> Option<u32> {
if self.outstanding.is_some() {
return None;
}
let rseq = self.next;
self.next = self.next.saturating_add(1);
self.outstanding = Some(rseq);
Some(rseq)
}
#[must_use]
pub fn outstanding(&self) -> Option<u32> {
self.outstanding
}
pub fn acknowledge(&mut self, ack: &RAck, cseq: u32, method: &[u8]) -> bool {
let matches = self.outstanding == Some(ack.rseq)
&& ack.cseq == cseq
&& ack.method.eq_ignore_ascii_case(method);
if matches {
self.outstanding = None;
}
matches
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Sequence {
last: Option<u32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Received {
Acknowledge,
Duplicate,
OutOfOrder,
}
impl Sequence {
pub fn accept(&mut self, rseq: u32) -> Received {
match self.last {
None => {
self.last = Some(rseq);
Received::Acknowledge
}
Some(last) if rseq == last => Received::Duplicate,
Some(last) if rseq == last.saturating_add(1) => {
self.last = Some(rseq);
Received::Acknowledge
}
Some(_) => Received::OutOfOrder,
}
}
#[must_use]
pub fn last(&self) -> Option<u32> {
self.last
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod tests {
use super::*;
#[test]
fn the_option_tag_is_found_however_the_peer_spells_the_list() {
use crate::{Limits, Message, parse_datagram};
let request = |extra: &str| {
let text = format!(
"INVITE sip:b@example.com SIP/2.0\r\n\
Via: SIP/2.0/UDP 192.0.2.1;branch=z9hG4bKx\r\n\
To: <sip:b@example.com>\r\n\
From: <sip:a@example.net>;tag=1\r\n\
Call-ID: c\r\n\
CSeq: 1 INVITE\r\n\
{extra}\
Content-Length: 0\r\n\r\n"
);
match parse_datagram(bytes::Bytes::from(text), &Limits::datagram()).expect("parses") {
Message::Request(r) => r,
Message::Response(_) => panic!("a request"),
}
};
assert!(Offered::in_request(&request("Supported: timer, 100rel, path\r\n")).supported);
assert!(Offered::in_request(&request("Supported: 100REL\r\n")).supported);
assert!(Offered::in_request(&request("Require: 100rel\r\n")).required);
assert!(!Offered::in_request(&request("Supported: 100relx\r\n")).supported);
assert!(!Offered::in_request(&request("Supported: timer\r\n")).supported);
}
#[test]
fn an_rack_carries_all_three_fields() {
let ack = RAck::decode(b"9021 314159 INVITE").expect("parses");
assert_eq!(ack.rseq, 9021);
assert_eq!(ack.cseq, 314_159);
assert_eq!(ack.method, b"INVITE");
assert_eq!(ack.to_string(), "9021 314159 INVITE");
}
#[test]
fn a_malformed_rack_is_rejected_rather_than_guessed_at() {
for value in [
&b"9021 314159"[..],
&b"9021 314159 INVITE extra"[..],
&b"9021"[..],
&b""[..],
] {
assert!(
RAck::decode(value).is_err(),
"{:?} should not parse",
String::from_utf8_lossy(value)
);
}
}
#[test]
fn the_uas_will_not_number_a_second_response_before_the_first_is_acknowledged() {
let mut numbering = Numbering::starting_at(500);
assert_eq!(numbering.allocate(), Some(500));
assert_eq!(numbering.allocate(), None);
let ack = RAck {
rseq: 500,
cseq: 1,
method: b"INVITE".to_vec(),
};
assert!(numbering.acknowledge(&ack, 1, b"INVITE"));
assert_eq!(numbering.allocate(), Some(501));
}
#[test]
fn a_prack_for_another_request_does_not_acknowledge_this_one() {
let mut numbering = Numbering::starting_at(500);
numbering.allocate();
let wrong_cseq = RAck {
rseq: 500,
cseq: 2,
method: b"INVITE".to_vec(),
};
assert!(!numbering.acknowledge(&wrong_cseq, 1, b"INVITE"));
let wrong_method = RAck {
rseq: 500,
cseq: 1,
method: b"UPDATE".to_vec(),
};
assert!(!numbering.acknowledge(&wrong_method, 1, b"INVITE"));
assert_eq!(numbering.outstanding(), Some(500));
}
#[test]
fn a_first_rseq_outside_the_window_is_brought_into_it() {
assert_eq!(Numbering::starting_at(0).allocate(), Some(1));
assert_eq!(
Numbering::starting_at(u32::MAX).allocate(),
Some(MAX_FIRST_RSEQ)
);
}
#[test]
fn the_uac_acknowledges_in_order_and_discards_the_rest() {
let mut seen = Sequence::default();
assert_eq!(seen.accept(9021), Received::Acknowledge);
assert_eq!(seen.accept(9021), Received::Duplicate);
assert_eq!(seen.accept(9022), Received::Acknowledge);
assert_eq!(seen.accept(9024), Received::OutOfOrder);
assert_eq!(seen.last(), Some(9022));
assert_eq!(seen.accept(9023), Received::Acknowledge);
}
#[test]
fn a_peer_that_never_mentioned_100rel_gets_unreliable_provisionals() {
assert_eq!(
reliability(
Offered {
supported: false,
required: false
},
true
),
Reliability::Forbidden
);
}
#[test]
fn a_requirement_this_side_will_not_meet_is_refused_rather_than_ignored() {
let asked = Offered {
supported: true,
required: true,
};
let offered = Offered {
supported: true,
required: false,
};
assert_eq!(reliability(asked, false), Reliability::Refuse);
assert_eq!(reliability(asked, true), Reliability::Required);
assert_eq!(reliability(offered, true), Reliability::Permitted);
assert_eq!(reliability(offered, false), Reliability::Forbidden);
}
}