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