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