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};