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