Skip to main content

form_net/urlencoded/
raw.rs

1//! The allocation-free urlencoded scanner.
2
3use core::fmt;
4
5use crate::error::{FormError, Limit, Location};
6use crate::limits::{Limits, PercentPolicy};
7use crate::urlencoded::decode::decode_component;
8
9const HAS_EQUALS: u8 = 1;
10const NAME_DECODE: u8 = 2;
11const VALUE_DECODE: u8 = 4;
12
13/// Iterates the pairs of a urlencoded body without allocating or copying.
14///
15/// Budgets are checked per sequence before the sequence is handed out: body
16/// size (every byte, empty sequences included), field count, raw name and
17/// value lengths, and the running total of raw value lengths. After the
18/// first error the iterator returns `None`.
19///
20/// # Examples
21///
22/// ```
23/// use form_net::Limits;
24///
25/// let pairs: Vec<_> = form_net::raw_pairs(b"a=1&&b", &Limits::DEFAULT)
26///     .map(|p| p.map(|p| (p.name_raw(), p.value_raw(), p.has_equals())))
27///     .collect::<Result<_, _>>()?;
28/// assert_eq!(pairs, [(&b"a"[..], &b"1"[..], true), (&b"b"[..], &b""[..], false)]);
29/// # Ok::<(), form_net::FormError>(())
30/// ```
31#[must_use]
32pub fn raw_pairs<'a>(input: &'a [u8], limits: &Limits) -> RawPairs<'a> {
33    RawPairs {
34        input,
35        pos: 0,
36        budget: Budget::new(limits),
37        done: false,
38        checked_body: false,
39    }
40}
41
42/// The per-sequence budgets, shared by [`raw_pairs`] and the streaming
43/// parser so both refuse the same sequence with the same error.
44#[derive(Clone, Copy, Debug)]
45pub(crate) struct Budget {
46    pub(crate) limits: Limits,
47    fields: u32,
48    text: u64,
49}
50
51impl Budget {
52    pub(crate) const fn new(limits: &Limits) -> Budget {
53        Budget {
54            limits: *limits,
55            fields: 0,
56            text: 0,
57        }
58    }
59
60    /// Counts and checks one complete, non-empty sequence: field count,
61    /// name length, value length, then the text total.
62    pub(crate) fn admit(&mut self, pair: &RawPair<'_>) -> Result<(), FormError> {
63        let at = Location::at(pair.offset);
64        self.fields = self.fields.saturating_add(1);
65        if self.fields > self.limits.max_fields {
66            return Err(FormError::limit(Limit::Fields, at));
67        }
68        if pair.name.len() as u64 > u64::from(self.limits.name()) {
69            return Err(FormError::limit(Limit::NameLength, at));
70        }
71        let value_at = pair.value_offset();
72        if pair.value.len() as u64 > u64::from(self.limits.value()) {
73            return Err(FormError::limit(Limit::ValueLength, value_at));
74        }
75        self.text = self.text.saturating_add(pair.value.len() as u64);
76        if self.text > self.limits.text() {
77            return Err(FormError::limit(Limit::TextTotal, value_at));
78        }
79        Ok(())
80    }
81
82    /// Checks the start of a sequence that is not complete yet: when its
83    /// complete form is certain to be refused, refuses it now with the error
84    /// [`admit`](Self::admit) would give, so a held sequence never outgrows
85    /// `urlencoded_buffer_len`.
86    pub(crate) fn admit_partial(
87        &self,
88        offset: u64,
89        name_len: usize,
90        has_equals: bool,
91        value_len: usize,
92    ) -> Result<(), FormError> {
93        let name_over = name_len as u64 > u64::from(self.limits.name());
94        let value_over = has_equals && value_len as u64 > u64::from(self.limits.value());
95        if !name_over && !value_over {
96            return Ok(());
97        }
98        if self.fields.saturating_add(1) > self.limits.max_fields {
99            return Err(FormError::limit(Limit::Fields, Location::at(offset)));
100        }
101        if name_over {
102            return Err(FormError::limit(Limit::NameLength, Location::at(offset)));
103        }
104        let value_at = offset.saturating_add(name_len as u64).saturating_add(1);
105        Err(FormError::limit(Limit::ValueLength, Location::at(value_at)))
106    }
107
108    pub(crate) fn reset(&mut self) {
109        self.fields = 0;
110        self.text = 0;
111    }
112}
113
114/// The iterator returned by [`raw_pairs`].
115#[derive(Clone)]
116pub struct RawPairs<'a> {
117    input: &'a [u8],
118    pos: usize,
119    budget: Budget,
120    done: bool,
121    checked_body: bool,
122}
123
124impl fmt::Debug for RawPairs<'_> {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        f.debug_struct("RawPairs")
127            .field("position", &self.pos)
128            .field("fields", &self.budget.fields)
129            .finish_non_exhaustive()
130    }
131}
132
133impl<'a> RawPairs<'a> {
134    fn fail(&mut self, err: FormError) -> Option<Result<RawPair<'a>, FormError>> {
135        self.done = true;
136        Some(Err(err))
137    }
138}
139
140impl<'a> Iterator for RawPairs<'a> {
141    type Item = Result<RawPair<'a>, FormError>;
142
143    fn next(&mut self) -> Option<Self::Item> {
144        if self.done {
145            return None;
146        }
147        if !self.checked_body {
148            self.checked_body = true;
149            if self.input.len() > self.budget.limits.body_usize() {
150                return self.fail(FormError::limit(
151                    Limit::Body,
152                    Location::at(self.budget.limits.body()),
153                ));
154            }
155        }
156        loop {
157            if self.pos >= self.input.len() {
158                self.done = true;
159                return None;
160            }
161            let start = self.pos;
162            let mut flags = 0u8;
163            let mut eq = None;
164            let mut i = start;
165            let end = loop {
166                i = crate::scan::find_any(self.input, i, *b"&=%+");
167                match self.input.get(i) {
168                    None | Some(b'&') => break i,
169                    Some(b'=') if eq.is_none() => {
170                        eq = Some(i);
171                        flags |= HAS_EQUALS;
172                    }
173                    Some(b'%' | b'+') => {
174                        flags |= if eq.is_some() {
175                            VALUE_DECODE
176                        } else {
177                            NAME_DECODE
178                        };
179                    }
180                    Some(_) => {}
181                }
182                i = i.wrapping_add(1);
183            };
184            self.pos = end.saturating_add(1).min(self.input.len());
185            if end == start {
186                continue;
187            }
188            let seq = self.input.get(start..end).unwrap_or(&[]);
189            let (name, value) = match eq {
190                Some(e) => {
191                    let at = e.wrapping_sub(start);
192                    (
193                        seq.get(..at).unwrap_or(&[]),
194                        seq.get(at.wrapping_add(1)..).unwrap_or(&[]),
195                    )
196                }
197                None => (seq, &[][..]),
198            };
199            let pair = RawPair {
200                name,
201                value,
202                flags,
203                offset: start as u64,
204            };
205            if let Err(e) = self.budget.admit(&pair) {
206                return self.fail(e);
207            }
208            return Some(Ok(pair));
209        }
210    }
211}
212
213impl core::iter::FusedIterator for RawPairs<'_> {}
214
215/// One `name=value` sequence, undecoded.
216///
217/// `Debug` prints the offset and the lengths only: values can be secrets.
218#[derive(Clone, Copy)]
219pub struct RawPair<'a> {
220    name: &'a [u8],
221    value: &'a [u8],
222    flags: u8,
223    offset: u64,
224}
225
226impl fmt::Debug for RawPair<'_> {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        f.debug_struct("RawPair")
229            .field("offset", &self.offset)
230            .field("name_len", &self.name.len())
231            .field("value_len", &self.value.len())
232            .finish()
233    }
234}
235
236impl<'a> RawPair<'a> {
237    /// Splits one sequence (no `&` in it) found at `offset` in the body.
238    pub(crate) fn split(seq: &'a [u8], offset: u64) -> RawPair<'a> {
239        let mut flags = 0u8;
240        let mut eq = None;
241        let mut i = 0usize;
242        loop {
243            i = crate::scan::find_any(seq, i, *b"&=%+");
244            match seq.get(i) {
245                None => break,
246                Some(b'=') if eq.is_none() => {
247                    eq = Some(i);
248                    flags |= HAS_EQUALS;
249                }
250                Some(b'%' | b'+') => {
251                    flags |= if eq.is_some() {
252                        VALUE_DECODE
253                    } else {
254                        NAME_DECODE
255                    };
256                }
257                Some(_) => {}
258            }
259            i = i.wrapping_add(1);
260        }
261        let (name, value) = match eq {
262            Some(e) => (
263                seq.get(..e).unwrap_or(&[]),
264                seq.get(e.wrapping_add(1)..).unwrap_or(&[]),
265            ),
266            None => (seq, &[][..]),
267        };
268        RawPair {
269            name,
270            value,
271            flags,
272            offset,
273        }
274    }
275
276    fn value_offset(&self) -> Location {
277        Location::at(
278            self.offset
279                .saturating_add(self.name.len() as u64)
280                .saturating_add(1),
281        )
282    }
283
284    /// The name as sent, before decoding.
285    ///
286    /// # Examples
287    ///
288    /// ```
289    /// let p = form_net::raw_pairs(b"a+b=1", &form_net::Limits::DEFAULT).next().unwrap()?;
290    /// assert_eq!(p.name_raw(), b"a+b");
291    /// # Ok::<(), form_net::FormError>(())
292    /// ```
293    #[must_use]
294    pub const fn name_raw(&self) -> &'a [u8] {
295        self.name
296    }
297
298    /// The value as sent, before decoding.
299    ///
300    /// # Examples
301    ///
302    /// ```
303    /// let p = form_net::raw_pairs(b"a=%41", &form_net::Limits::DEFAULT).next().unwrap()?;
304    /// assert_eq!(p.value_raw(), b"%41");
305    /// # Ok::<(), form_net::FormError>(())
306    /// ```
307    #[must_use]
308    pub const fn value_raw(&self) -> &'a [u8] {
309        self.value
310    }
311
312    /// Whether the sequence contained `=`: `a` and `a=` both have an empty
313    /// value, but only `a=` has an equals sign.
314    ///
315    /// # Examples
316    ///
317    /// ```
318    /// use form_net::Limits;
319    ///
320    /// let mut it = form_net::raw_pairs(b"a&b=", &Limits::DEFAULT);
321    /// assert!(!it.next().unwrap()?.has_equals());
322    /// assert!(it.next().unwrap()?.has_equals());
323    /// # Ok::<(), form_net::FormError>(())
324    /// ```
325    #[must_use]
326    pub const fn has_equals(&self) -> bool {
327        self.flags & HAS_EQUALS != 0
328    }
329
330    /// Whether the name contains `%` or `+`.
331    ///
332    /// # Examples
333    ///
334    /// ```
335    /// let p = form_net::raw_pairs(b"a+b=1", &form_net::Limits::DEFAULT).next().unwrap()?;
336    /// assert!(p.name_needs_decoding() && !p.value_needs_decoding());
337    /// # Ok::<(), form_net::FormError>(())
338    /// ```
339    #[must_use]
340    pub const fn name_needs_decoding(&self) -> bool {
341        self.flags & NAME_DECODE != 0
342    }
343
344    /// Whether the value contains `%` or `+`.
345    ///
346    /// # Examples
347    ///
348    /// ```
349    /// let p = form_net::raw_pairs(b"a=1%20", &form_net::Limits::DEFAULT).next().unwrap()?;
350    /// assert!(p.value_needs_decoding());
351    /// # Ok::<(), form_net::FormError>(())
352    /// ```
353    #[must_use]
354    pub const fn value_needs_decoding(&self) -> bool {
355        self.flags & VALUE_DECODE != 0
356    }
357
358    /// The name with no copy, when it needs no decoding and is valid UTF-8.
359    ///
360    /// # Examples
361    ///
362    /// ```
363    /// let p = form_net::raw_pairs(b"email=x", &form_net::Limits::DEFAULT).next().unwrap()?;
364    /// assert_eq!(p.name_str(), Some("email"));
365    /// # Ok::<(), form_net::FormError>(())
366    /// ```
367    #[must_use]
368    pub fn name_str(&self) -> Option<&'a str> {
369        if self.name_needs_decoding() {
370            None
371        } else {
372            core::str::from_utf8(self.name).ok()
373        }
374    }
375
376    /// The value with no copy, when it needs no decoding and is valid UTF-8.
377    ///
378    /// # Examples
379    ///
380    /// ```
381    /// let p = form_net::raw_pairs(b"q=a+b", &form_net::Limits::DEFAULT).next().unwrap()?;
382    /// assert_eq!(p.value_str(), None);
383    /// # Ok::<(), form_net::FormError>(())
384    /// ```
385    #[must_use]
386    pub fn value_str(&self) -> Option<&'a str> {
387        if self.value_needs_decoding() {
388            None
389        } else {
390            core::str::from_utf8(self.value).ok()
391        }
392    }
393
394    /// Decodes the name into `out` (see [`decode_into`](crate::decode_into)).
395    ///
396    /// # Errors
397    ///
398    /// `Encoding { InvalidPercent | InvalidUtf8 }` located in the body;
399    /// `BufferTooSmall` when `out` is shorter than the raw name.
400    ///
401    /// # Examples
402    ///
403    /// ```
404    /// use form_net::PercentPolicy;
405    ///
406    /// let p = form_net::raw_pairs(b"a%20b=1", &form_net::Limits::DEFAULT).next().unwrap()?;
407    /// let mut out = [0u8; 16];
408    /// assert_eq!(p.decode_name(&mut out, PercentPolicy::Refuse)?, "a b");
409    /// # Ok::<(), form_net::FormError>(())
410    /// ```
411    pub fn decode_name<'b>(
412        &self,
413        out: &'b mut [u8],
414        policy: PercentPolicy,
415    ) -> Result<&'b str, FormError> {
416        decode_component(self.name, out, policy).map_err(|e| e.shifted(self.offset))
417    }
418
419    /// Decodes the value into `out` (see [`decode_into`](crate::decode_into)).
420    ///
421    /// # Errors
422    ///
423    /// As [`decode_name`](Self::decode_name).
424    ///
425    /// # Examples
426    ///
427    /// ```
428    /// use form_net::PercentPolicy;
429    ///
430    /// let p = form_net::raw_pairs(b"q=rust+forms", &form_net::Limits::DEFAULT).next().unwrap()?;
431    /// let mut out = [0u8; 16];
432    /// assert_eq!(p.decode_value(&mut out, PercentPolicy::Refuse)?, "rust forms");
433    /// # Ok::<(), form_net::FormError>(())
434    /// ```
435    pub fn decode_value<'b>(
436        &self,
437        out: &'b mut [u8],
438        policy: PercentPolicy,
439    ) -> Result<&'b str, FormError> {
440        let at = self
441            .offset
442            .saturating_add(self.name.len() as u64)
443            .saturating_add(1);
444        decode_component(self.value, out, policy).map_err(|e| e.shifted(at))
445    }
446
447    /// The byte offset of the sequence in the body.
448    ///
449    /// # Examples
450    ///
451    /// ```
452    /// let mut it = form_net::raw_pairs(b"a=1&bb=2", &form_net::Limits::DEFAULT);
453    /// let _ = it.next();
454    /// assert_eq!(it.next().unwrap()?.offset(), 4);
455    /// # Ok::<(), form_net::FormError>(())
456    /// ```
457    #[must_use]
458    pub const fn offset(&self) -> u64 {
459        self.offset
460    }
461}
462
463#[cfg(test)]
464mod tests {
465    use super::*;
466    use alloc::vec::Vec;
467
468    type Pairs<'a> = Vec<(&'a [u8], &'a [u8])>;
469
470    fn pairs(input: &[u8]) -> Result<Pairs<'_>, FormError> {
471        raw_pairs(input, &Limits::DEFAULT)
472            .map(|p| p.map(|p| (p.name_raw(), p.value_raw())))
473            .collect()
474    }
475
476    #[test]
477    fn test_raw_pairs_whatwg_splitting() {
478        assert_eq!(pairs(b"").unwrap(), []);
479        assert_eq!(pairs(b"&&&").unwrap(), []);
480        assert_eq!(pairs(b"=").unwrap(), [(&b""[..], &b""[..])]);
481        assert_eq!(pairs(b"a==b").unwrap(), [(&b"a"[..], &b"=b"[..])]);
482        assert_eq!(pairs(b"=a").unwrap(), [(&b""[..], &b"a"[..])]);
483        assert_eq!(pairs(b"a;b=c").unwrap(), [(&b"a;b"[..], &b"c"[..])]);
484        assert_eq!(
485            pairs(b"a=1&b=2&").unwrap(),
486            [(&b"a"[..], &b"1"[..]), (&b"b"[..], &b"2"[..])]
487        );
488    }
489
490    #[test]
491    fn test_raw_pairs_flags_per_side() {
492        let p = raw_pairs(b"a%=b", &Limits::DEFAULT)
493            .next()
494            .unwrap()
495            .unwrap();
496        assert!(p.name_needs_decoding() && !p.value_needs_decoding());
497        let p = raw_pairs(b"a=b+%", &Limits::DEFAULT)
498            .next()
499            .unwrap()
500            .unwrap();
501        assert!(!p.name_needs_decoding() && p.value_needs_decoding());
502        let p = raw_pairs(b"a=b=+", &Limits::DEFAULT)
503            .next()
504            .unwrap()
505            .unwrap();
506        assert!(p.value_needs_decoding() && p.has_equals());
507    }
508
509    #[test]
510    fn test_raw_pairs_field_limit_n_and_n_plus_one() {
511        let l = Limits::DEFAULT.with_max_fields(2);
512        assert_eq!(raw_pairs(b"a&b", &l).count(), 2);
513        let r: Result<Vec<_>, _> = raw_pairs(b"a&b&c", &l).collect();
514        assert!(matches!(
515            r,
516            Err(FormError::Limit {
517                limit: Limit::Fields,
518                at
519            }) if at.offset == 4
520        ));
521    }
522
523    #[test]
524    fn test_raw_pairs_length_limits_at_the_edge() {
525        let l = Limits::DEFAULT
526            .with_max_name_bytes(3)
527            .with_max_value_bytes(3);
528        assert!(raw_pairs(b"abc=def", &l).all(|p| p.is_ok()));
529        let e = raw_pairs(b"abcd=1", &l).next().unwrap().unwrap_err();
530        assert!(matches!(
531            e,
532            FormError::Limit {
533                limit: Limit::NameLength,
534                ..
535            }
536        ));
537        let e = raw_pairs(b"a=defg", &l).next().unwrap().unwrap_err();
538        assert!(matches!(e, FormError::Limit { limit: Limit::ValueLength, at } if at.offset == 2));
539        let l = Limits::DEFAULT.with_max_text_bytes(4);
540        let r: Result<Vec<_>, _> = raw_pairs(b"a=12&b=345", &l).collect();
541        assert!(matches!(
542            r,
543            Err(FormError::Limit {
544                limit: Limit::TextTotal,
545                ..
546            })
547        ));
548    }
549
550    #[test]
551    fn test_raw_pairs_body_limit_counts_empty_sequences() {
552        let l = Limits::DEFAULT.with_max_body_bytes(4);
553        assert_eq!(raw_pairs(b"&&&&", &l).count(), 0);
554        let e = raw_pairs(b"&&&&&", &l).next().unwrap().unwrap_err();
555        assert!(matches!(
556            e,
557            FormError::Limit {
558                limit: Limit::Body,
559                ..
560            }
561        ));
562    }
563
564    #[test]
565    fn test_raw_pairs_stops_after_error() {
566        let l = Limits::DEFAULT.with_max_fields(0);
567        let mut it = raw_pairs(b"a&b", &l);
568        assert!(it.next().unwrap().is_err());
569        assert!(it.next().is_none());
570    }
571
572    #[test]
573    fn test_raw_pair_debug_hides_values() {
574        let p = raw_pairs(b"password=hunter2", &Limits::DEFAULT)
575            .next()
576            .unwrap()
577            .unwrap();
578        let s = alloc::format!("{p:?}");
579        assert!(!s.contains("hunter2") && !s.contains("password"));
580    }
581}