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 b < 0x20 || b == 0x7f {
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 b < 0x20 || b == 0x7f {
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 number of parameters.
222    #[must_use]
223    pub const fn len(&self) -> usize {
224        self.count as usize
225    }
226
227    /// Whether there are no parameters.
228    #[must_use]
229    pub const fn is_empty(&self) -> bool {
230        self.count == 0
231    }
232
233    /// The validated text, from the first `;` to the end of the last
234    /// parameter.
235    #[must_use]
236    pub const fn raw(&self) -> &'a [u8] {
237        self.text
238    }
239}
240
241impl fmt::Debug for Params<'_> {
242    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        f.write_str("Params(")?;
244        f.debug_list()
245            .entries(
246                self.iter()
247                    .map(|p| core::str::from_utf8(p.name).unwrap_or("?")),
248            )
249            .finish()?;
250        f.write_str(")")
251    }
252}
253
254/// The parameters of a validated [`Params`].
255#[derive(Clone, Debug)]
256pub struct ParamIter<'a> {
257    rest: &'a [u8],
258}
259
260impl<'a> Iterator for ParamIter<'a> {
261    type Item = Param<'a>;
262
263    fn next(&mut self) -> Option<Param<'a>> {
264        loop {
265            // Validated text: OWS ";" OWS [ name "=" word ] ...
266            let s = self.rest;
267            let semi = s.iter().position(|&b| b == b';')?;
268            let after = semi + 1;
269            let start = after + skip_ows(s.get(after..).unwrap_or(&[]));
270            let tail = s.get(start..).unwrap_or(&[]);
271            let name_len = tail.iter().take_while(|&&b| class::is_tchar(b)).count();
272            if name_len == 0 {
273                self.rest = tail;
274                continue;
275            }
276            let name = tail.get(..name_len).unwrap_or(&[]);
277            let value_text = tail.get(name_len + 1..).unwrap_or(&[]);
278            let Ok((value, n)) = parse_word(value_text) else {
279                self.rest = &[];
280                return None;
281            };
282            self.rest = value_text.get(n..).unwrap_or(&[]);
283            return Some(Param { name, value });
284        }
285    }
286}
287
288#[cfg(test)]
289mod tests {
290    use std::{format, vec::Vec};
291
292    use super::*;
293
294    fn p(s: &[u8]) -> Result<(Params<'_>, usize)> {
295        parse(s, &Limits::DEFAULT)
296    }
297
298    fn names<'a>(ps: &Params<'a>) -> Vec<&'a [u8]> {
299        ps.iter().map(|x| x.name).collect()
300    }
301
302    #[test]
303    fn test_params_edge_cases() {
304        assert_eq!(p(b"").unwrap().1, 0);
305        assert!(p(b";").unwrap().0.is_empty());
306        assert_eq!(p(b";").unwrap().1, 1);
307        assert_eq!(p(b";;").unwrap().1, 2);
308        let (ps, used) = p(b"; a=b").unwrap();
309        assert_eq!((used, names(&ps)), (5, [&b"a"[..]].to_vec()));
310        assert_eq!(p(b";a=").unwrap_err().kind(), ErrorKind::MissingValue);
311        assert_eq!(p(b";a").unwrap_err().kind(), ErrorKind::MissingValue);
312        assert_eq!(p(b";a;b=c").unwrap_err().kind(), ErrorKind::MissingValue);
313        assert_eq!(p(b";a =b").unwrap_err().kind(), ErrorKind::InvalidByte);
314        assert_eq!(p(b";a= b").unwrap_err().kind(), ErrorKind::InvalidByte);
315        assert_eq!(p(b";@=b").unwrap_err().kind(), ErrorKind::NotToken);
316        assert_eq!(p(b";a@=b").unwrap_err().kind(), ErrorKind::NotToken);
317        assert_eq!(p(b";a=\"\"").unwrap().0.get("a").map(|w| w.len()), Some(0));
318        assert!(
319            p(b";a=\"x\\\"y\"")
320                .unwrap()
321                .0
322                .get("a")
323                .unwrap()
324                .eq_bytes(b"x\"y")
325        );
326        assert_eq!(
327            p(b";a=\"x").unwrap_err().kind(),
328            ErrorKind::UnterminatedQuote
329        );
330        assert_eq!(p(b";a=b\rc").unwrap().1, 4);
331        assert_eq!(p(b";a=\x01").unwrap_err().kind(), ErrorKind::Control);
332    }
333
334    #[test]
335    fn test_params_stop_at_list_separator_and_trailing_ows() {
336        let (ps, used) = p(b";a=1 , b").unwrap();
337        assert_eq!((used, ps.len()), (4, 1));
338        let (_, used) = p(b";a=1; ,b").unwrap();
339        assert_eq!(used, 5);
340        let (_, used) = p(b" ;a=1 x").unwrap();
341        assert_eq!(used, 5);
342    }
343
344    #[test]
345    fn test_params_duplicate_refused_case_insensitive() {
346        let e = p(b";A=b;a=c").unwrap_err();
347        assert_eq!((e.kind(), e.offset()), (ErrorKind::DuplicateParameter, 5));
348        assert!(p(b";ab=1;ba=2;aa=3;bb=4").is_ok());
349    }
350
351    #[test]
352    fn test_params_first_and_last_policies() {
353        let first = Limits::DEFAULT.duplicate_params(DuplicatePolicy::First);
354        let last = Limits::DEFAULT.duplicate_params(DuplicatePolicy::Last);
355        let (a, _) = parse(b";a=1;A=2", &first).unwrap();
356        let (b, _) = parse(b";a=1;A=2", &last).unwrap();
357        assert!(a.get("a").unwrap().eq_bytes(b"1"));
358        assert!(b.get("a").unwrap().eq_bytes(b"2"));
359        assert_eq!(a.len(), 2);
360    }
361
362    #[test]
363    fn test_params_count_limit() {
364        let mut v = Vec::new();
365        for i in 0..257 {
366            v.extend_from_slice(format!(";p{i}=v").as_bytes());
367        }
368        let cut = v.len() - format!(";p{}=v", 256).len();
369        assert_eq!(p(&v[..cut]).unwrap().0.len(), 256);
370        assert_eq!(p(&v).unwrap_err().kind(), ErrorKind::TooManyMembers);
371    }
372
373    #[test]
374    fn test_params_debug_prints_names_only() {
375        let (ps, _) = p(b";user=alice;token=\"s3cr3t\"").unwrap();
376        let d = format!("{ps:?}");
377        assert_eq!(d, "Params([\"user\", \"token\"])");
378        assert!(!d.contains("s3cr3t") && !d.contains("alice"));
379    }
380
381    #[test]
382    fn test_params_iter_skips_empty_parameters() {
383        let (ps, _) = p(b";; a=1 ;;b=\"x;y\";").unwrap();
384        assert_eq!(names(&ps), [&b"a"[..], b"b"]);
385        assert!(ps.get("b").unwrap().eq_bytes(b"x;y"));
386        assert_eq!(ps.raw(), b";; a=1 ;;b=\"x;y\";");
387    }
388}