Skip to main content

form_net/urlencoded/
mod.rs

1//! `application/x-www-form-urlencoded`: the WHATWG URL standard's parser
2//! (section 5.1) with strict refusal where WHATWG would repair, and its
3//! serializer.
4
5pub(crate) mod decode;
6pub(crate) mod encode;
7pub(crate) mod raw;
8
9#[cfg(feature = "alloc")]
10mod owned {
11    use alloc::borrow::Cow;
12    use alloc::vec::Vec;
13    use core::fmt;
14
15    use super::decode::{decode_append, decode_to_vec};
16    use super::raw::{RawPairs, raw_pairs};
17    use crate::error::{ExtractError, FormError};
18    use crate::limits::{Limits, PercentPolicy, Policy};
19
20    #[derive(Clone, Copy)]
21    struct Span {
22        name_start: usize,
23        name_len: usize,
24        value_start: usize,
25        value_len: usize,
26    }
27
28    /// The owned result of a urlencoded parse: every field, in input order,
29    /// duplicates kept.
30    ///
31    /// Every decoded name and value lives in one buffer allocated once, and
32    /// one table of positions; neither reallocates. Lookups are linear scans
33    /// (there is no hash map, so hash flooding cannot apply; `max_fields`
34    /// bounds every scan). On drop the buffer is wiped. `Debug` prints names
35    /// and value lengths, never values. There is no `Clone`: a clone would
36    /// be a second copy of every secret.
37    ///
38    /// # Examples
39    ///
40    /// ```
41    /// let form = form_net::parse_urlencoded(b"role=user&role=admin&tag=red&tag=blue")?;
42    /// assert_eq!(form.get("role"), Some("user"));                 // first wins
43    /// assert!(form.get_one("role").is_err());                     // strict
44    /// assert_eq!(form.get_one("missing")?, None);
45    /// assert_eq!(form.get_all("tag").collect::<Vec<_>>(), ["red", "blue"]);
46    /// assert_eq!(form.iter().count(), 4);
47    /// assert_eq!(
48    ///     format!("{form:?}"),
49    ///     "Form { role: <4 bytes>, role: <5 bytes>, tag: <3 bytes>, tag: <4 bytes> }"
50    /// );
51    /// # Ok::<(), form_net::FormError>(())
52    /// ```
53    #[must_use]
54    pub struct Form {
55        arena: Vec<u8>,
56        spans: Vec<Span>,
57    }
58
59    impl Form {
60        fn text(&self, start: usize, len: usize) -> &str {
61            let bytes = self
62                .arena
63                .get(start..start.saturating_add(len))
64                .unwrap_or(&[]);
65            // Every span was checked as UTF-8 when it was decoded.
66            core::str::from_utf8(bytes).unwrap_or("")
67        }
68
69        fn name_of(&self, s: &Span) -> &str {
70            self.text(s.name_start, s.name_len)
71        }
72
73        fn value_of(&self, s: &Span) -> &str {
74            self.text(s.value_start, s.value_len)
75        }
76
77        /// The first value given for `name`, as browsers'
78        /// `URLSearchParams.get` returns. Use [`get_one`](Self::get_one) where a
79        /// repeated name must be refused.
80        ///
81        /// # Examples
82        ///
83        /// ```
84        /// let form = form_net::parse_urlencoded(b"email=a%40b.example")?;
85        /// assert_eq!(form.get("email"), Some("a@b.example"));
86        /// assert_eq!(form.get("other"), None);
87        /// # Ok::<(), form_net::FormError>(())
88        /// ```
89        #[must_use]
90        pub fn get(&self, name: &str) -> Option<&str> {
91            self.spans
92                .iter()
93                .find(|s| self.name_of(s) == name)
94                .map(|s| self.value_of(s))
95        }
96
97        /// The value given for `name`, refusing a name given more than once
98        /// (the parameter pollution defence).
99        ///
100        /// # Errors
101        ///
102        /// `Extract { reason: Duplicate, field: None }`.
103        ///
104        /// # Examples
105        ///
106        /// ```
107        /// let form = form_net::parse_urlencoded(b"a=1&b=2&b=3")?;
108        /// assert_eq!(form.get_one("a")?, Some("1"));
109        /// assert!(form.get_one("b").is_err());
110        /// # Ok::<(), form_net::FormError>(())
111        /// ```
112        pub fn get_one(&self, name: &str) -> Result<Option<&str>, FormError> {
113            let mut found = None;
114            for s in &self.spans {
115                if self.name_of(s) == name {
116                    if found.is_some() {
117                        return Err(FormError::Extract {
118                            reason: ExtractError::Duplicate,
119                            field: None,
120                        });
121                    }
122                    found = Some(self.value_of(s));
123                }
124            }
125            Ok(found)
126        }
127
128        /// Every value given for `name`, in input order.
129        ///
130        /// # Examples
131        ///
132        /// ```
133        /// let form = form_net::parse_urlencoded(b"t=a&x=1&t=b")?;
134        /// assert_eq!(form.get_all("t").collect::<Vec<_>>(), ["a", "b"]);
135        /// # Ok::<(), form_net::FormError>(())
136        /// ```
137        pub fn get_all<'f>(&'f self, name: &'f str) -> impl Iterator<Item = &'f str> + 'f {
138            self.spans
139                .iter()
140                .filter(move |s| self.name_of(s) == name)
141                .map(move |s| self.value_of(s))
142        }
143
144        /// Every `(name, value)`, in input order.
145        ///
146        /// # Examples
147        ///
148        /// ```
149        /// let form = form_net::parse_urlencoded(b"a=1&b=2")?;
150        /// assert_eq!(form.iter().collect::<Vec<_>>(), [("a", "1"), ("b", "2")]);
151        /// # Ok::<(), form_net::FormError>(())
152        /// ```
153        pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
154            self.spans
155                .iter()
156                .map(|s| (self.name_of(s), self.value_of(s)))
157        }
158
159        /// Every name, in input order, repeats included.
160        ///
161        /// # Examples
162        ///
163        /// ```
164        /// let form = form_net::parse_urlencoded(b"a=1&b=2&a=3")?;
165        /// assert_eq!(form.names().collect::<Vec<_>>(), ["a", "b", "a"]);
166        /// # Ok::<(), form_net::FormError>(())
167        /// ```
168        pub fn names(&self) -> impl Iterator<Item = &str> {
169            self.spans.iter().map(|s| self.name_of(s))
170        }
171
172        /// The number of fields, repeats included.
173        ///
174        /// # Examples
175        ///
176        /// ```
177        /// assert_eq!(form_net::parse_urlencoded(b"a&&b")?.len(), 2);
178        /// # Ok::<(), form_net::FormError>(())
179        /// ```
180        #[must_use]
181        pub fn len(&self) -> usize {
182            self.spans.len()
183        }
184
185        /// Whether there are no fields.
186        ///
187        /// # Examples
188        ///
189        /// ```
190        /// assert!(form_net::parse_urlencoded(b"&&")?.is_empty());
191        /// # Ok::<(), form_net::FormError>(())
192        /// ```
193        #[must_use]
194        pub fn is_empty(&self) -> bool {
195            self.spans.is_empty()
196        }
197
198        /// Whether `name` was given at least once.
199        ///
200        /// # Examples
201        ///
202        /// ```
203        /// let form = form_net::parse_urlencoded(b"remember")?;
204        /// assert!(form.contains("remember"));
205        /// # Ok::<(), form_net::FormError>(())
206        /// ```
207        #[must_use]
208        pub fn contains(&self, name: &str) -> bool {
209            self.spans.iter().any(|s| self.name_of(s) == name)
210        }
211    }
212
213    impl Drop for Form {
214        fn drop(&mut self) {
215            crate::wipe::wipe(&mut self.arena);
216        }
217    }
218
219    impl fmt::Debug for Form {
220        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221            struct Len(usize);
222            impl fmt::Debug for Len {
223                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224                    if self.0 == 1 {
225                        f.write_str("<1 byte>")
226                    } else {
227                        write!(f, "<{} bytes>", self.0)
228                    }
229                }
230            }
231            let mut m = f.debug_struct("Form");
232            for s in &self.spans {
233                m.field(self.name_of(s), &Len(s.value_len));
234            }
235            m.finish()
236        }
237    }
238
239    fn build(input: &[u8], limits: &Limits, percent: PercentPolicy) -> Result<Form, FormError> {
240        let mut pairs = raw_pairs(input, limits);
241        // The span table is sized from the separator count, capped by the
242        // field limit; the arena from the input length (decoding never makes
243        // text longer). Neither grows afterwards.
244        let separators = crate::scan::count_byte(input, b'&');
245        let cap = separators
246            .saturating_add(1)
247            .min(usize::try_from(limits.max_fields).unwrap_or(usize::MAX))
248            .min(input.len());
249        let mut spans = Vec::with_capacity(cap);
250        let mut arena = alloc::vec![0u8; input.len().min(limits.body_usize())];
251        let mut used = 0usize;
252        let result = collect(&mut pairs, &mut arena, &mut used, &mut spans, percent);
253        if let Err(e) = result {
254            crate::wipe::wipe(&mut arena);
255            return Err(e);
256        }
257        arena.truncate(used);
258        Ok(Form { arena, spans })
259    }
260
261    fn collect(
262        pairs: &mut RawPairs<'_>,
263        arena: &mut [u8],
264        used: &mut usize,
265        spans: &mut Vec<Span>,
266        percent: PercentPolicy,
267    ) -> Result<(), FormError> {
268        for pair in pairs {
269            let pair = pair?;
270            let name_start = *used;
271            let name_len = decode_append(pair.name_raw(), arena, name_start, percent)
272                .map_err(|e| e.shifted(pair.offset()))?;
273            let value_start = name_start.saturating_add(name_len);
274            let value_at = pair
275                .offset()
276                .saturating_add(pair.name_raw().len() as u64)
277                .saturating_add(1);
278            let value_len = decode_append(pair.value_raw(), arena, value_start, percent)
279                .map_err(|e| e.shifted(value_at))?;
280            *used = value_start.saturating_add(value_len);
281            spans.push(Span {
282                name_start,
283                name_len,
284                value_start,
285                value_len,
286            });
287        }
288        Ok(())
289    }
290
291    /// Parses a whole `application/x-www-form-urlencoded` body with the
292    /// default limits and policy, and returns the fields, owned, in input
293    /// order.
294    ///
295    /// `&` is the only separator (`;` is an ordinary byte), `+` is a space,
296    /// and a malformed `%` or invalid UTF-8 is refused, never repaired.
297    ///
298    /// # Errors
299    ///
300    /// `Limit { Body | Fields | NameLength | ValueLength | TextTotal }`;
301    /// `Encoding { InvalidPercent | InvalidUtf8 }`.
302    ///
303    /// # Examples
304    ///
305    /// ```
306    /// let form = form_net::parse_urlencoded(b"email=a%40b.example&remember=on")?;
307    /// assert_eq!(form.get("email"), Some("a@b.example"));
308    /// assert_eq!(form.get("remember"), Some("on"));
309    ///
310    /// let form = form_net::parse_urlencoded(b"a=1;b=2")?;
311    /// assert_eq!(form.get("a"), Some("1;b=2"));
312    ///
313    /// assert!(form_net::parse_urlencoded(b"name=%zz").is_err());
314    /// assert!(form_net::parse_urlencoded(b"name=%C0%AF").is_err());
315    /// # Ok::<(), form_net::FormError>(())
316    /// ```
317    pub fn parse_urlencoded(input: &[u8]) -> Result<Form, FormError> {
318        build(input, &Limits::DEFAULT, PercentPolicy::Refuse)
319    }
320
321    /// As [`parse_urlencoded`], after stripping exactly one leading `?` if
322    /// there is one.
323    ///
324    /// # Errors
325    ///
326    /// As [`parse_urlencoded`].
327    ///
328    /// # Examples
329    ///
330    /// ```
331    /// let q = form_net::parse_query(b"?q=rust+forms&page=2")?;
332    /// assert_eq!(q.get("q"), Some("rust forms"));
333    /// assert_eq!(form_net::parse_query(b"??q=x")?.get("?q"), Some("x"));
334    /// # Ok::<(), form_net::FormError>(())
335    /// ```
336    pub fn parse_query(query: &[u8]) -> Result<Form, FormError> {
337        parse_urlencoded(query.strip_prefix(b"?").unwrap_or(query))
338    }
339
340    /// As [`parse_urlencoded`] with explicit limits and policy.
341    /// `limits.validate()` runs first. Of the policy, only `percent` applies
342    /// to urlencoded bodies.
343    ///
344    /// # Errors
345    ///
346    /// As [`parse_urlencoded`]; `InvalidLimits` from `validate`.
347    ///
348    /// # Examples
349    ///
350    /// ```
351    /// use form_net::{Limits, Policy, PercentPolicy};
352    ///
353    /// const LOGIN: Limits = Limits::DEFAULT.with_max_body_bytes(4 * 1024).with_max_fields(8);
354    /// let form = form_net::parse_urlencoded_with(b"email=a%40b.example&password=x", &LOGIN, &Policy::DEFAULT)?;
355    /// assert_eq!(form.len(), 2);
356    ///
357    /// const LIKE_BROWSERS: Policy = Policy::DEFAULT.with_percent(PercentPolicy::Literal);
358    /// let form = form_net::parse_urlencoded_with(b"discount=100%", &Limits::DEFAULT, &LIKE_BROWSERS)?;
359    /// assert_eq!(form.get("discount"), Some("100%"));
360    /// # Ok::<(), form_net::FormError>(())
361    /// ```
362    pub fn parse_urlencoded_with(
363        input: &[u8],
364        limits: &Limits,
365        policy: &Policy,
366    ) -> Result<Form, FormError> {
367        limits.validate()?;
368        build(input, limits, policy.percent)
369    }
370
371    /// Iterates the fields of a urlencoded body lazily, with the default
372    /// limits. A name or value that needs no decoding is borrowed from the
373    /// input; others are decoded into a new `String` (which belongs to the
374    /// caller and is not wiped: use [`Form`] for secret fields). After the
375    /// first error the iterator returns `None`.
376    ///
377    /// # Examples
378    ///
379    /// ```
380    /// use std::borrow::Cow;
381    ///
382    /// let mut it = form_net::pairs(b"a=1&b=x+y");
383    /// let (name, value) = it.next().unwrap()?;
384    /// assert!(matches!((name, value), (Cow::Borrowed("a"), Cow::Borrowed("1"))));
385    /// let (_, value) = it.next().unwrap()?;
386    /// assert!(matches!(value, Cow::Owned(ref s) if s == "x y"));
387    /// assert!(it.next().is_none());
388    /// # Ok::<(), form_net::FormError>(())
389    /// ```
390    pub fn pairs(input: &[u8]) -> Pairs<'_> {
391        Pairs {
392            raw: raw_pairs(input, &Limits::DEFAULT),
393            done: false,
394        }
395    }
396
397    /// The iterator returned by [`pairs`].
398    #[derive(Debug, Clone)]
399    pub struct Pairs<'a> {
400        raw: RawPairs<'a>,
401        done: bool,
402    }
403
404    fn component(raw: &[u8], needs_decoding: bool, offset: u64) -> Result<Cow<'_, str>, FormError> {
405        if !needs_decoding {
406            if let Ok(s) = core::str::from_utf8(raw) {
407                return Ok(Cow::Borrowed(s));
408            }
409        }
410        decode_to_vec(raw, PercentPolicy::Refuse)
411            .map(Cow::Owned)
412            .map_err(|e| e.shifted(offset))
413    }
414
415    impl<'a> Iterator for Pairs<'a> {
416        type Item = Result<(Cow<'a, str>, Cow<'a, str>), FormError>;
417
418        fn next(&mut self) -> Option<Self::Item> {
419            if self.done {
420                return None;
421            }
422            let item = match self.raw.next()? {
423                Ok(p) => {
424                    let value_at = p
425                        .offset()
426                        .saturating_add(p.name_raw().len() as u64)
427                        .saturating_add(1);
428                    component(p.name_raw(), p.name_needs_decoding(), p.offset()).and_then(|name| {
429                        component(p.value_raw(), p.value_needs_decoding(), value_at)
430                            .map(|value| (name, value))
431                    })
432                }
433                Err(e) => Err(e),
434            };
435            if item.is_err() {
436                self.done = true;
437            }
438            Some(item)
439        }
440    }
441
442    impl core::iter::FusedIterator for Pairs<'_> {}
443
444    #[cfg(test)]
445    mod tests {
446        use super::*;
447        use crate::error::{EncodingError, Limit, Location};
448        use alloc::format;
449
450        #[test]
451        fn test_parse_urlencoded_whatwg_cases() {
452            type Case<'a> = (&'a [u8], &'a [(&'a str, &'a str)]);
453            let cases: &[Case<'_>] = &[
454                (b"", &[]),
455                (b"a", &[("a", "")]),
456                (b"a=", &[("a", "")]),
457                (b"=a", &[("", "a")]),
458                (b"a==b", &[("a", "=b")]),
459                (b"a=b&c=d", &[("a", "b"), ("c", "d")]),
460                (b"&&a=1&&", &[("a", "1")]),
461                (b"+=+", &[(" ", " ")]),
462                (b"%2B=%20", &[("+", " ")]),
463                (b"a%26b=c%3Dd", &[("a&b", "c=d")]),
464            ];
465            for (input, expected) in cases {
466                let form = parse_urlencoded(input).unwrap();
467                assert_eq!(form.iter().collect::<Vec<_>>(), *expected, "{input:?}");
468            }
469        }
470
471        #[test]
472        fn test_parse_urlencoded_error_locations_are_absolute() {
473            let e = parse_urlencoded(b"ok=1&bad=%zz").unwrap_err();
474            assert_eq!(
475                e,
476                FormError::encoding(EncodingError::InvalidPercent, Location::at(9))
477            );
478            let e = parse_urlencoded(b"ok=1&%C0=x").unwrap_err();
479            assert_eq!(
480                e,
481                FormError::encoding(EncodingError::InvalidUtf8, Location::at(5))
482            );
483        }
484
485        #[test]
486        fn test_parse_urlencoded_with_validates_limits() {
487            let bad = Limits::DEFAULT.with_max_part_header_bytes(1);
488            assert!(matches!(
489                parse_urlencoded_with(b"a=1", &bad, &Policy::DEFAULT),
490                Err(FormError::InvalidLimits { .. })
491            ));
492            let small = Limits::DEFAULT.with_max_body_bytes(3);
493            assert!(matches!(
494                parse_urlencoded_with(b"a=12", &small, &Policy::DEFAULT),
495                Err(FormError::Limit {
496                    limit: Limit::Body,
497                    ..
498                })
499            ));
500        }
501
502        #[test]
503        fn test_form_debug_hides_values() {
504            let form = parse_urlencoded(b"password=hunter2&x=1").unwrap();
505            let s = format!("{form:?}");
506            assert_eq!(s, "Form { password: <7 bytes>, x: <1 byte> }");
507        }
508
509        #[test]
510        fn test_pairs_error_then_none() {
511            let mut it = pairs(b"a=%&b=1");
512            assert!(it.next().unwrap().is_err());
513            assert!(it.next().is_none());
514        }
515
516        #[test]
517        fn test_form_two_allocations_sizes() {
518            let form = parse_urlencoded(b"a=%41%42&b=c").unwrap();
519            assert_eq!(form.arena.len(), 5);
520            assert_eq!(form.get("a"), Some("AB"));
521        }
522    }
523}
524
525#[cfg(feature = "alloc")]
526pub use owned::{Form, Pairs, pairs, parse_query, parse_urlencoded, parse_urlencoded_with};