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