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 done: false,
460 }
461 }
462
463 /// The iterator returned by [`pairs`].
464 #[derive(Debug, Clone)]
465 pub struct Pairs<'a> {
466 raw: RawPairs<'a>,
467 done: bool,
468 }
469
470 fn component(raw: &[u8], needs_decoding: bool, offset: u64) -> Result<Cow<'_, str>, FormError> {
471 if !needs_decoding {
472 if let Ok(s) = core::str::from_utf8(raw) {
473 return Ok(Cow::Borrowed(s));
474 }
475 }
476 decode_to_vec(raw, PercentPolicy::Refuse)
477 .map(Cow::Owned)
478 .map_err(|e| e.shifted(offset))
479 }
480
481 impl<'a> Iterator for Pairs<'a> {
482 type Item = Result<(Cow<'a, str>, Cow<'a, str>), FormError>;
483
484 fn next(&mut self) -> Option<Self::Item> {
485 if self.done {
486 return None;
487 }
488 let item = match self.raw.next()? {
489 Ok(p) => {
490 let value_at = p
491 .offset()
492 .saturating_add(p.name_raw().len() as u64)
493 .saturating_add(1);
494 component(p.name_raw(), p.name_needs_decoding(), p.offset()).and_then(|name| {
495 component(p.value_raw(), p.value_needs_decoding(), value_at)
496 .map(|value| (name, value))
497 })
498 }
499 Err(e) => Err(e),
500 };
501 if item.is_err() {
502 self.done = true;
503 }
504 Some(item)
505 }
506 }
507
508 impl core::iter::FusedIterator for Pairs<'_> {}
509
510 #[cfg(test)]
511 mod tests {
512 use super::*;
513 use crate::error::{EncodingError, Limit, Location};
514 use alloc::format;
515
516 #[test]
517 fn test_parse_urlencoded_whatwg_cases() {
518 type Case<'a> = (&'a [u8], &'a [(&'a str, &'a str)]);
519 let cases: &[Case<'_>] = &[
520 (b"", &[]),
521 (b"a", &[("a", "")]),
522 (b"a=", &[("a", "")]),
523 (b"=a", &[("", "a")]),
524 (b"a==b", &[("a", "=b")]),
525 (b"a=b&c=d", &[("a", "b"), ("c", "d")]),
526 (b"&&a=1&&", &[("a", "1")]),
527 (b"+=+", &[(" ", " ")]),
528 (b"%2B=%20", &[("+", " ")]),
529 (b"a%26b=c%3Dd", &[("a&b", "c=d")]),
530 ];
531 for (input, expected) in cases {
532 let form = parse_urlencoded(input).unwrap();
533 assert_eq!(form.iter().collect::<Vec<_>>(), *expected, "{input:?}");
534 }
535 }
536
537 #[test]
538 fn test_parse_urlencoded_error_locations_are_absolute() {
539 let e = parse_urlencoded(b"ok=1&bad=%zz").unwrap_err();
540 assert_eq!(
541 e,
542 FormError::encoding(EncodingError::InvalidPercent, Location::at(9))
543 );
544 let e = parse_urlencoded(b"ok=1&%C0=x").unwrap_err();
545 assert_eq!(
546 e,
547 FormError::encoding(EncodingError::InvalidUtf8, Location::at(5))
548 );
549 }
550
551 #[test]
552 fn test_parse_urlencoded_with_validates_limits() {
553 let bad = Limits::DEFAULT.with_max_part_header_bytes(1);
554 assert!(matches!(
555 parse_urlencoded_with(b"a=1", &bad, &Policy::DEFAULT),
556 Err(FormError::InvalidLimits { .. })
557 ));
558 let small = Limits::DEFAULT.with_max_body_bytes(3);
559 assert!(matches!(
560 parse_urlencoded_with(b"a=12", &small, &Policy::DEFAULT),
561 Err(FormError::Limit {
562 limit: Limit::Body,
563 ..
564 })
565 ));
566 }
567
568 #[test]
569 fn test_form_debug_hides_values() {
570 let form = parse_urlencoded(b"password=hunter2&x=1").unwrap();
571 let s = format!("{form:?}");
572 assert_eq!(s, "Form { password: <7 bytes>, x: <1 byte> }");
573 }
574
575 #[test]
576 fn test_pairs_error_then_none() {
577 let mut it = pairs(b"a=%&b=1");
578 assert!(it.next().unwrap().is_err());
579 assert!(it.next().is_none());
580 }
581
582 #[test]
583 fn test_form_two_allocations_sizes() {
584 let form = parse_urlencoded(b"a=%41%42&b=c").unwrap();
585 assert_eq!(form.arena.len(), 5);
586 assert_eq!(form.get("a"), Some("AB"));
587 }
588 }
589}
590
591#[cfg(feature = "alloc")]
592pub use owned::{
593 Form, Pairs, pairs, parse_query, parse_urlencoded, parse_urlencoded_inspected,
594 parse_urlencoded_with,
595};