Skip to main content

parse_net/
param.rs

1//! Parameters: `*( OWS ";" OWS [ parameter ] )` (RFC 9110 section 5.6.6).
2//!
3//! # Examples
4//!
5//! ```
6//! use parse_net::{param, Limits};
7//!
8//! let (p, used) = param::parse(b"; charset=utf-8;format=\"flowed\" , next", &Limits::DEFAULT)?;
9//! assert_eq!(used, 31);
10//! assert!(p.get("Charset").is_some_and(|v| v.eq_bytes(b"utf-8")));
11//! assert_eq!(format!("{p:?}"), "Params([\"charset\", \"format\"])");
12//! # Ok::<(), parse_net::ParseError>(())
13//! ```
14
15use core::fmt;
16
17use crate::{
18    class,
19    error::{ErrorKind, Result, err},
20    limits::{DuplicatePolicy, Limits},
21    scan::control_kind,
22    text::{Word, is_ows, parse_word, skip_ows},
23};
24
25/// One parameter: a name and a value.
26#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
27pub struct Param<'a> {
28    /// The name as sent (compare it ASCII case-insensitively).
29    pub name: &'a [u8],
30    /// The value.
31    pub value: Word<'a>,
32}
33
34/// A small filter over parameter names, so a repeated name is searched for
35/// only when one could exist. Bounded: at most `max_params` names are seen.
36#[inline]
37fn name_bit(name: &[u8]) -> u64 {
38    let first = name.first().map_or(0, u8::to_ascii_lowercase);
39    let last = name.last().map_or(0, u8::to_ascii_lowercase);
40    let h = (name.len() as u64)
41        .wrapping_mul(31)
42        .wrapping_add(u64::from(first))
43        .wrapping_mul(31)
44        .wrapping_add(u64::from(last));
45    1u64 << (h & 63)
46}
47
48/// Parses parameters at the start of `s`.
49///
50/// `s` starts at the first `;` (optionally preceded by OWS) or is empty.
51/// Parsing continues as long as the grammar does; the view and the number of
52/// bytes used are returned (OWS after the last parameter is not used), and
53/// whatever follows is the caller's to check. Empty parameters (`;;`, a
54/// trailing `;`) are allowed, as the grammar says. Whitespace around `=` is
55/// not (BWS is not part of this grammar).
56///
57/// # Errors
58///
59/// [`ErrorKind::NotToken`] for a bad name; [`ErrorKind::MissingValue`] for a
60/// name with no `=` or no value; [`ErrorKind::InvalidByte`] for whitespace
61/// around `=`; the quoted-string errors; [`ErrorKind::DuplicateParameter`]
62/// under [`DuplicatePolicy::Refuse`]; [`ErrorKind::TooManyMembers`] beyond
63/// [`Limits::max_params`].
64///
65/// # Examples
66///
67/// ```
68/// use parse_net::{param, ErrorKind, Limits};
69///
70/// let dup = param::parse(b";a=1;A=2", &Limits::DEFAULT).unwrap_err();
71/// assert_eq!((dup.kind(), dup.offset()), (ErrorKind::DuplicateParameter, 5));
72/// assert_eq!(param::parse(b";a =1", &Limits::DEFAULT).unwrap_err().kind(), ErrorKind::InvalidByte);
73/// ```
74pub fn parse<'a>(s: &'a [u8], limits: &Limits) -> Result<(Params<'a>, usize)> {
75    let policy = limits.duplicate_params;
76    let mut count: usize = 0;
77    let mut used = 0usize;
78    let mut seen: u64 = 0;
79    loop {
80        let ows = skip_ows(s.get(used..).unwrap_or(&[]));
81        let semi = used + ows;
82        if s.get(semi) != Some(&b';') {
83            break;
84        }
85        let mut i = semi + 1;
86        i += skip_ows(s.get(i..).unwrap_or(&[]));
87        match s.get(i) {
88            None | Some(b';') => {
89                used = i;
90                continue;
91            }
92            Some(&b) if !class::is_tchar(b) => {
93                // OWS after an empty parameter followed by a list separator
94                // or the end belongs to the caller.
95                if b == b',' {
96                    used = semi + 1;
97                    break;
98                }
99                return err(
100                    if crate::scan::is_control(b) {
101                        control_kind(b)
102                    } else {
103                        ErrorKind::NotToken
104                    },
105                    i,
106                );
107            }
108            Some(_) => {}
109        }
110        let name_start = i;
111        let name_len = s
112            .get(i..)
113            .unwrap_or(&[])
114            .iter()
115            .take_while(|&&b| class::is_tchar(b))
116            .count();
117        let name = s.get(name_start..name_start + name_len).unwrap_or(&[]);
118        i += name_len;
119        match s.get(i) {
120            Some(b'=') => i += 1,
121            Some(&b) if is_ows(b) => {
122                let after = i + skip_ows(s.get(i..).unwrap_or(&[]));
123                if s.get(after) == Some(&b'=') {
124                    return err(ErrorKind::InvalidByte, i);
125                }
126                return err(ErrorKind::MissingValue, i);
127            }
128            Some(b'"') => return err(ErrorKind::MissingValue, i),
129            Some(&b) if b != b';' && b != b',' && class::is_field_text(b) => {
130                return err(ErrorKind::NotToken, i);
131            }
132            Some(&b) if !class::is_field_text(b) => return err(control_kind(b), i),
133            _ => return err(ErrorKind::MissingValue, i),
134        }
135        let value_start = i;
136        match s.get(i) {
137            Some(&b) if is_ows(b) => return err(ErrorKind::InvalidByte, i),
138            None | Some(b';' | b',') => return err(ErrorKind::MissingValue, i),
139            _ => {}
140        }
141        let rest = s.get(i..).unwrap_or(&[]);
142        let (_, n) = parse_word(rest).map_err(|e| match e.kind() {
143            ErrorKind::Empty => crate::error::ParseError::new(
144                control_or_invalid(rest.first().copied().unwrap_or(0)),
145                value_start,
146            ),
147            _ => e.shifted(value_start),
148        })?;
149        i += n;
150        count += 1;
151        if count > usize::from(limits.params) {
152            return err(ErrorKind::TooManyMembers, name_start);
153        }
154        if policy == DuplicatePolicy::Refuse {
155            let bit = name_bit(name);
156            if seen & bit != 0 {
157                let earlier = Params {
158                    text: s.get(..used).unwrap_or(&[]),
159                    count: 0,
160                    policy,
161                };
162                if earlier.iter().any(|p| p.name.eq_ignore_ascii_case(name)) {
163                    return err(ErrorKind::DuplicateParameter, name_start);
164                }
165            }
166            seen |= bit;
167        }
168        used = i;
169    }
170    Ok((
171        Params {
172            text: s.get(..used).unwrap_or(&[]),
173            count: u32::try_from(count).unwrap_or(u32::MAX),
174            policy,
175        },
176        used,
177    ))
178}
179
180#[inline]
181const fn control_or_invalid(b: u8) -> ErrorKind {
182    if crate::scan::is_control(b) {
183        control_kind(b)
184    } else {
185        ErrorKind::InvalidByte
186    }
187}
188
189/// Validated parameters. Iterating them cannot fail.
190///
191/// `Debug` prints the parameter names only: values can be secrets.
192#[derive(Clone, Copy, PartialEq, Eq, Hash)]
193pub struct Params<'a> {
194    text: &'a [u8],
195    count: u32,
196    policy: DuplicatePolicy,
197}
198
199impl<'a> Params<'a> {
200    /// Every parameter, in order (with `First` or `Last`, duplicates
201    /// included).
202    #[must_use]
203    pub fn iter(&self) -> ParamIter<'a> {
204        ParamIter { rest: self.text }
205    }
206
207    /// The value of the parameter named `name` (ASCII case-insensitive): the
208    /// first match, or the last under [`DuplicatePolicy::Last`].
209    #[must_use]
210    pub fn get(&self, name: &str) -> Option<Word<'a>> {
211        let name = name.as_bytes();
212        let mut matches = self.iter().filter(|p| p.name.eq_ignore_ascii_case(name));
213        let found = if self.policy == DuplicatePolicy::Last {
214            matches.last()
215        } else {
216            matches.next()
217        };
218        found.map(|p| p.value)
219    }
220
221    /// The parameters before the first one named `name` (ASCII
222    /// case-insensitive), and that parameter; all of them and `None` when no
223    /// parameter has the name. Used to split media-type parameters from
224    /// `q` and the `accept-ext` after it.
225    pub(crate) fn split_at_name(&self, name: &[u8]) -> (Params<'a>, Option<Param<'a>>) {
226        for (index, p) in self.iter().enumerate() {
227            if p.name.eq_ignore_ascii_case(name) {
228                // `p.name` points into `self.text`; the distance is its offset.
229                let off = (p.name.as_ptr() as usize).saturating_sub(self.text.as_ptr() as usize);
230                let head = self.text.get(..off).unwrap_or(&[]);
231                let semi = head.iter().rposition(|&b| b == b';').unwrap_or(0);
232                let before = Params {
233                    text: self.text.get(..semi).unwrap_or(&[]),
234                    count: u32::try_from(index).unwrap_or(u32::MAX),
235                    policy: self.policy,
236                };
237                return (before, Some(p));
238            }
239        }
240        (*self, None)
241    }
242
243    /// The number of parameters.
244    #[must_use]
245    pub const fn len(&self) -> usize {
246        self.count as usize
247    }
248
249    /// Whether there are no parameters.
250    #[must_use]
251    pub const fn is_empty(&self) -> bool {
252        self.count == 0
253    }
254
255    /// The validated text, from the first `;` to the end of the last
256    /// parameter.
257    #[must_use]
258    pub const fn raw(&self) -> &'a [u8] {
259        self.text
260    }
261}
262
263impl fmt::Debug for Params<'_> {
264    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265        f.write_str("Params(")?;
266        f.debug_list()
267            .entries(
268                self.iter()
269                    .map(|p| core::str::from_utf8(p.name).unwrap_or("?")),
270            )
271            .finish()?;
272        f.write_str(")")
273    }
274}
275
276/// The parameters of a validated [`Params`].
277#[derive(Clone, Debug)]
278pub struct ParamIter<'a> {
279    rest: &'a [u8],
280}
281
282impl<'a> Iterator for ParamIter<'a> {
283    type Item = Param<'a>;
284
285    fn next(&mut self) -> Option<Param<'a>> {
286        loop {
287            // Validated text: OWS ";" OWS [ name "=" word ] ...
288            let s = self.rest;
289            let semi = s.iter().position(|&b| b == b';')?;
290            let after = semi + 1;
291            let start = after + skip_ows(s.get(after..).unwrap_or(&[]));
292            let tail = s.get(start..).unwrap_or(&[]);
293            let name_len = tail.iter().take_while(|&&b| class::is_tchar(b)).count();
294            if name_len == 0 {
295                self.rest = tail;
296                continue;
297            }
298            let name = tail.get(..name_len).unwrap_or(&[]);
299            let value_text = tail.get(name_len + 1..).unwrap_or(&[]);
300            let Ok((value, n)) = parse_word(value_text) else {
301                self.rest = &[];
302                return None;
303            };
304            self.rest = value_text.get(n..).unwrap_or(&[]);
305            return Some(Param { name, value });
306        }
307    }
308}
309
310#[cfg(test)]
311mod tests {
312    use std::{format, vec::Vec};
313
314    use super::*;
315
316    fn p(s: &[u8]) -> Result<(Params<'_>, usize)> {
317        parse(s, &Limits::DEFAULT)
318    }
319
320    fn names<'a>(ps: &Params<'a>) -> Vec<&'a [u8]> {
321        ps.iter().map(|x| x.name).collect()
322    }
323
324    #[test]
325    fn test_params_edge_cases() {
326        assert_eq!(p(b"").unwrap().1, 0);
327        assert!(p(b";").unwrap().0.is_empty());
328        assert_eq!(p(b";").unwrap().1, 1);
329        assert_eq!(p(b";;").unwrap().1, 2);
330        let (ps, used) = p(b"; a=b").unwrap();
331        assert_eq!((used, names(&ps)), (5, [&b"a"[..]].to_vec()));
332        assert_eq!(p(b";a=").unwrap_err().kind(), ErrorKind::MissingValue);
333        assert_eq!(p(b";a").unwrap_err().kind(), ErrorKind::MissingValue);
334        assert_eq!(p(b";a;b=c").unwrap_err().kind(), ErrorKind::MissingValue);
335        assert_eq!(p(b";a =b").unwrap_err().kind(), ErrorKind::InvalidByte);
336        assert_eq!(p(b";a= b").unwrap_err().kind(), ErrorKind::InvalidByte);
337        assert_eq!(p(b";@=b").unwrap_err().kind(), ErrorKind::NotToken);
338        assert_eq!(p(b";a@=b").unwrap_err().kind(), ErrorKind::NotToken);
339        assert_eq!(p(b";a=\"\"").unwrap().0.get("a").map(|w| w.len()), Some(0));
340        assert!(
341            p(b";a=\"x\\\"y\"")
342                .unwrap()
343                .0
344                .get("a")
345                .unwrap()
346                .eq_bytes(b"x\"y")
347        );
348        assert_eq!(
349            p(b";a=\"x").unwrap_err().kind(),
350            ErrorKind::UnterminatedQuote
351        );
352        assert_eq!(p(b";a=b\rc").unwrap().1, 4);
353        assert_eq!(p(b";a=\x01").unwrap_err().kind(), ErrorKind::Control);
354    }
355
356    #[test]
357    fn test_params_stop_at_list_separator_and_trailing_ows() {
358        let (ps, used) = p(b";a=1 , b").unwrap();
359        assert_eq!((used, ps.len()), (4, 1));
360        let (_, used) = p(b";a=1; ,b").unwrap();
361        assert_eq!(used, 5);
362        let (_, used) = p(b" ;a=1 x").unwrap();
363        assert_eq!(used, 5);
364    }
365
366    #[test]
367    fn test_params_duplicate_refused_case_insensitive() {
368        let e = p(b";A=b;a=c").unwrap_err();
369        assert_eq!((e.kind(), e.offset()), (ErrorKind::DuplicateParameter, 5));
370        assert!(p(b";ab=1;ba=2;aa=3;bb=4").is_ok());
371    }
372
373    #[test]
374    fn test_params_first_and_last_policies() {
375        let first = Limits::DEFAULT.duplicate_params(DuplicatePolicy::First);
376        let last = Limits::DEFAULT.duplicate_params(DuplicatePolicy::Last);
377        let (a, _) = parse(b";a=1;A=2", &first).unwrap();
378        let (b, _) = parse(b";a=1;A=2", &last).unwrap();
379        assert!(a.get("a").unwrap().eq_bytes(b"1"));
380        assert!(b.get("a").unwrap().eq_bytes(b"2"));
381        assert_eq!(a.len(), 2);
382    }
383
384    #[test]
385    fn test_params_count_limit() {
386        let mut v = Vec::new();
387        for i in 0..257 {
388            v.extend_from_slice(format!(";p{i}=v").as_bytes());
389        }
390        let cut = v.len() - format!(";p{}=v", 256).len();
391        assert_eq!(p(&v[..cut]).unwrap().0.len(), 256);
392        assert_eq!(p(&v).unwrap_err().kind(), ErrorKind::TooManyMembers);
393    }
394
395    #[test]
396    fn test_params_debug_prints_names_only() {
397        let (ps, _) = p(b";user=alice;token=\"s3cr3t\"").unwrap();
398        let d = format!("{ps:?}");
399        assert_eq!(d, "Params([\"user\", \"token\"])");
400        assert!(!d.contains("s3cr3t") && !d.contains("alice"));
401    }
402
403    #[test]
404    fn test_params_iter_skips_empty_parameters() {
405        let (ps, _) = p(b";; a=1 ;;b=\"x;y\";").unwrap();
406        assert_eq!(names(&ps), [&b"a"[..], b"b"]);
407        assert!(ps.get("b").unwrap().eq_bytes(b"x;y"));
408        assert_eq!(ps.raw(), b";; a=1 ;;b=\"x;y\";");
409    }
410}