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