Skip to main content

sipx_sip/
params.rs

1//! Ordered parameter lists: the `;name=value` tails on URIs and header values.
2//!
3//! Order and duplicates are preserved. Forwarding must not reorder a parameter list, and a
4//! duplicate is a fact about the message a proxy has no business erasing — the layer that
5//! cares can decide what a repeat means.
6
7use bytes::Bytes;
8
9use crate::escape;
10
11/// One `name` or `name=value` parameter.
12#[derive(Debug, Clone)]
13pub struct Param {
14    name: Bytes,
15    value: Option<Bytes>,
16}
17
18impl Param {
19    /// A parameter with no value, as in `;lr`.
20    #[must_use]
21    pub fn flag(name: impl Into<Bytes>) -> Self {
22        Self {
23            name: name.into(),
24            value: None,
25        }
26    }
27
28    /// A parameter with a value, as in `;transport=tcp`.
29    #[must_use]
30    pub fn new(name: impl Into<Bytes>, value: impl Into<Bytes>) -> Self {
31        Self {
32            name: name.into(),
33            value: Some(value.into()),
34        }
35    }
36
37    /// The parameter name, as it appeared.
38    #[must_use]
39    pub fn name(&self) -> &[u8] {
40        &self.name
41    }
42
43    /// The parameter value, as it appeared, still percent-encoded.
44    #[must_use]
45    pub fn value(&self) -> Option<&[u8]> {
46        self.value.as_deref()
47    }
48
49    /// Whether this parameter's name matches, case-insensitively.
50    ///
51    /// Escapes of unreserved characters fold before the comparison: `pname` is built from
52    /// `paramchar`, which includes `escaped`, so `%74ransport` is a legal spelling of
53    /// `transport` (RFC 3261 §19.1.4).
54    #[must_use]
55    pub fn has_name(&self, name: &str) -> bool {
56        names_equivalent(&self.name, name.as_bytes())
57    }
58}
59
60/// Whether two parameter names are the same name under RFC 3261 §19.1.4: case-insensitive,
61/// with escapes of unreserved characters folded into the characters themselves.
62#[must_use]
63pub(crate) fn names_equivalent(a: &[u8], b: &[u8]) -> bool {
64    escape::eq_ignore_ascii_case(
65        &escape::normalize_for_comparison(a),
66        &escape::normalize_for_comparison(b),
67    )
68}
69
70/// An ordered list of parameters.
71#[derive(Debug, Clone, Default)]
72pub struct Params {
73    entries: Vec<Param>,
74}
75
76impl Params {
77    /// An empty list.
78    #[must_use]
79    pub fn new() -> Self {
80        Self::default()
81    }
82
83    /// How many parameters are present, counting duplicates.
84    #[must_use]
85    pub fn len(&self) -> usize {
86        self.entries.len()
87    }
88
89    /// Whether the list is empty.
90    #[must_use]
91    pub fn is_empty(&self) -> bool {
92        self.entries.is_empty()
93    }
94
95    /// Append a parameter, keeping any existing one of the same name.
96    pub fn push(&mut self, param: Param) {
97        self.entries.push(param);
98    }
99
100    /// Remove every parameter with this name, and say whether any was there.
101    ///
102    /// Every one, not the first: the list preserves duplicates because a message may contain them,
103    /// and leaving a second copy behind after removing the first would be a silent change of
104    /// meaning rather than a removal.
105    pub fn remove(&mut self, name: &str) -> bool {
106        let before = self.entries.len();
107        self.entries.retain(|p| !p.has_name(name));
108        self.entries.len() != before
109    }
110
111    /// Every parameter, in wire order.
112    pub fn iter(&self) -> impl Iterator<Item = &Param> {
113        self.entries.iter()
114    }
115
116    /// The first parameter with this name, if any. Names compare case-insensitively.
117    #[must_use]
118    pub fn get(&self, name: &str) -> Option<&Param> {
119        self.entries.iter().find(|p| p.has_name(name))
120    }
121
122    /// The value of the first parameter with this name.
123    ///
124    /// Returns `None` both when the parameter is absent and when it is present without a
125    /// value; use [`Params::contains`] to tell those apart, because `;lr` and no `lr` at all
126    /// mean different things.
127    #[must_use]
128    pub fn value(&self, name: &str) -> Option<&[u8]> {
129        self.get(name).and_then(Param::value)
130    }
131
132    /// Whether a parameter with this name is present, with or without a value.
133    #[must_use]
134    pub fn contains(&self, name: &str) -> bool {
135        self.get(name).is_some()
136    }
137
138    /// Whether two parameter values are equivalent under RFC 3261 §19.1.4: order is
139    /// insignificant, comparison is case-insensitive, and escapes of unreserved characters
140    /// fold into the characters themselves.
141    #[must_use]
142    fn values_equivalent(a: Option<&[u8]>, b: Option<&[u8]>) -> bool {
143        match (a, b) {
144            (None, None) => true,
145            (Some(x), Some(y)) => escape::eq_ignore_ascii_case(
146                &escape::normalize_for_comparison(x),
147                &escape::normalize_for_comparison(y),
148            ),
149            _ => false,
150        }
151    }
152
153    /// Whether a named parameter is equivalent in both lists, treating absence as a value.
154    #[must_use]
155    pub(crate) fn param_equivalent(&self, other: &Self, name: &str) -> bool {
156        match (self.get(name), other.get(name)) {
157            (None, None) => true,
158            (Some(x), Some(y)) => Self::values_equivalent(x.value(), y.value()),
159            _ => false,
160        }
161    }
162
163    /// Whether every parameter present in *both* lists agrees.
164    ///
165    /// Parameters present in only one are ignored — the caller applies the special cases
166    /// (RFC 3261 §19.1.4 names `user`, `ttl`, `method`, `maddr` and `transport`) with
167    /// [`Params::param_equivalent`].
168    #[must_use]
169    pub(crate) fn common_params_agree(&self, other: &Self) -> bool {
170        self.entries.iter().all(|p| {
171            other
172                .entries
173                .iter()
174                .filter(|q| names_equivalent(q.name(), p.name()))
175                .all(|q| Self::values_equivalent(p.value(), q.value()))
176        })
177    }
178
179    /// Serialize as `;name=value` pairs, in order.
180    pub fn write_to(&self, out: &mut Vec<u8>, separator: u8) {
181        for (i, p) in self.entries.iter().enumerate() {
182            out.push(if i == 0 && separator == b'?' {
183                b'?'
184            } else if separator == b'?' {
185                b'&'
186            } else {
187                separator
188            });
189            out.extend_from_slice(&p.name);
190            if let Some(v) = &p.value {
191                out.push(b'=');
192                out.extend_from_slice(v);
193            }
194        }
195    }
196}
197
198impl<'a> IntoIterator for &'a Params {
199    type Item = &'a Param;
200    type IntoIter = std::slice::Iter<'a, Param>;
201
202    fn into_iter(self) -> Self::IntoIter {
203        self.entries.iter()
204    }
205}
206
207impl FromIterator<Param> for Params {
208    fn from_iter<T: IntoIterator<Item = Param>>(iter: T) -> Self {
209        Self {
210            entries: iter.into_iter().collect(),
211        }
212    }
213}
214
215#[cfg(test)]
216#[allow(
217    clippy::unwrap_used,
218    clippy::expect_used,
219    clippy::panic,
220    clippy::indexing_slicing
221)]
222mod tests {
223    use super::*;
224
225    fn params(pairs: &[(&str, Option<&str>)]) -> Params {
226        pairs
227            .iter()
228            .map(|(n, v)| match v {
229                Some(v) => Param::new(Bytes::from((*n).to_owned()), Bytes::from((*v).to_owned())),
230                None => Param::flag(Bytes::from((*n).to_owned())),
231            })
232            .collect()
233    }
234
235    #[test]
236    fn preserves_order_and_duplicates() {
237        let p = params(&[("b", Some("2")), ("a", Some("1")), ("b", Some("3"))]);
238        assert_eq!(p.len(), 3);
239        let names: Vec<_> = p.iter().map(|x| x.name().to_vec()).collect();
240        assert_eq!(names, vec![b"b".to_vec(), b"a".to_vec(), b"b".to_vec()]);
241        // get() returns the first occurrence, not the last.
242        assert_eq!(p.value("b"), Some(&b"2"[..]));
243    }
244
245    /// Every copy, not the first. A list that may hold duplicates and a removal that took one of
246    /// them would leave the parameter still there, having reported that it was gone — a silent
247    /// change of meaning rather than a removal.
248    #[test]
249    fn remove_takes_every_copy_and_says_whether_there_was_one() {
250        let mut p = params(&[("b", Some("2")), ("a", Some("1")), ("b", Some("3"))]);
251        assert!(p.remove("b"));
252        assert_eq!(p.len(), 1);
253        assert_eq!(p.value("b"), None);
254        assert_eq!(p.value("a"), Some(&b"1"[..]));
255        // Nothing to remove is `false`, and leaves the list alone.
256        assert!(!p.remove("b"));
257        assert_eq!(p.len(), 1);
258        // A valueless parameter is a parameter, and matching is the case-insensitive comparison
259        // `has_name` makes it (RFC 3261 §19.1.4).
260        let mut flags = params(&[("lr", None), ("Transport", Some("tcp"))]);
261        assert!(flags.remove("LR"));
262        assert!(flags.remove("transport"));
263        assert!(flags.is_empty());
264    }
265
266    #[test]
267    fn flag_is_not_the_same_as_absent() {
268        let p = params(&[("lr", None)]);
269        assert!(p.contains("lr"));
270        assert_eq!(p.value("lr"), None);
271        assert!(!p.contains("nope"));
272    }
273
274    #[test]
275    fn names_and_values_compare_case_insensitively() {
276        let a = params(&[("Transport", Some("TCP"))]);
277        let b = params(&[("transport", Some("tcp"))]);
278        assert!(a.param_equivalent(&b, "transport"));
279    }
280
281    /// RFC 3261 §19.1.4: `pname` includes `escaped`, so an escaped spelling of a name is
282    /// the same name.
283    #[test]
284    fn escaped_names_fold_when_looked_up() {
285        let p = params(&[("%74ransport", Some("udp"))]);
286        assert!(p.get("transport").is_some());
287        assert_eq!(p.value("transport"), Some(&b"udp"[..]));
288
289        let plain = params(&[("transport", Some("udp"))]);
290        assert!(p.param_equivalent(&plain, "transport"));
291        assert!(p.common_params_agree(&plain) && plain.common_params_agree(&p));
292
293        let other = params(&[("transport", Some("tcp"))]);
294        assert!(!p.common_params_agree(&other));
295    }
296
297    #[test]
298    fn writes_uri_and_header_separators() {
299        let p = params(&[("a", Some("1")), ("b", None)]);
300        let mut out = Vec::new();
301        p.write_to(&mut out, b';');
302        assert_eq!(out, b";a=1;b");
303
304        let mut out = Vec::new();
305        p.write_to(&mut out, b'?');
306        assert_eq!(out, b"?a=1&b");
307    }
308}