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