Skip to main content

form_net/urlencoded/
stream.rs

1//! The streaming urlencoded parser: chunks in, pairs out, identical for
2//! every split of the body.
3
4use core::fmt;
5
6use crate::error::{FormError, Limit, Location};
7use crate::limits::Limits;
8use crate::urlencoded::raw::{Budget, RawPair};
9
10/// A streaming urlencoded parser over a caller scratch buffer.
11///
12/// A sequence that ends inside the current chunk is returned borrowed from
13/// the input; one that straddles chunks is assembled in the scratch buffer
14/// ([`Limits::urlencoded_buffer_len`] bytes) and returned borrowed from it.
15/// A sequence too long for its budgets is refused as soon as its first
16/// bytes prove it, before it is copied, so the scratch never overflows.
17/// Pairs and errors are the same for every split of the same body.
18///
19/// # Examples
20///
21/// ```
22/// use form_net::{Limits, UrlencodedEvent, UrlencodedParser};
23///
24/// // A small route: the scratch fits on the stack.
25/// const LIMITS: Limits = Limits::DEFAULT.with_max_name_bytes(32).with_max_value_bytes(256);
26/// let mut scratch = [0u8; LIMITS.urlencoded_buffer_len()];
27/// let mut parser = UrlencodedParser::with_buffer(&LIMITS, &mut scratch[..])?;
28/// let mut names = Vec::new();
29/// for (chunk, last) in [(&b"na"[..], false), (b"me=Al&ci", false), (b"ty=Paris", true)] {
30///     let mut rest = chunk;
31///     loop {
32///         let step = parser.parse(rest, last)?;
33///         rest = &rest[step.consumed..];
34///         match step.event {
35///             UrlencodedEvent::Pair(p) => names.push(p.name_raw().to_vec()),
36///             _ => break,
37///         }
38///     }
39/// }
40/// assert_eq!(names, [b"name".to_vec(), b"city".to_vec()]);
41/// # Ok::<(), form_net::FormError>(())
42/// ```
43pub struct UrlencodedParser<B: AsMut<[u8]> + AsRef<[u8]>> {
44    scratch: B,
45    /// Bytes of the current, unfinished sequence held in `scratch`.
46    held: usize,
47    /// Body offset of the held sequence.
48    held_at: u64,
49    /// The last call handed out a pair borrowed from `scratch`.
50    release: bool,
51    /// Body bytes consumed so far.
52    seen: u64,
53    /// The most of `scratch` ever held: only that is wiped (D-103).
54    dirty: usize,
55    budget: Budget,
56    poisoned: bool,
57    ended: bool,
58}
59
60impl<B: AsMut<[u8]> + AsRef<[u8]>> fmt::Debug for UrlencodedParser<B> {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        f.debug_struct("UrlencodedParser")
63            .field("bytes_seen", &self.seen)
64            .field("held", &self.held)
65            .field("ended", &self.ended)
66            .finish_non_exhaustive()
67    }
68}
69
70/// One step of [`UrlencodedParser::parse`]: how much input was consumed and
71/// what it completed.
72#[non_exhaustive]
73#[derive(Debug)]
74pub struct UrlencodedStep<'p> {
75    /// Bytes of the input consumed (or held in the scratch buffer).
76    pub consumed: usize,
77    /// What the consumed bytes completed.
78    pub event: UrlencodedEvent<'p>,
79}
80
81/// What a step of the streaming parser completed.
82#[non_exhaustive]
83#[derive(Clone, Copy, Debug)]
84pub enum UrlencodedEvent<'p> {
85    /// One non-empty `name=value` sequence, undecoded.
86    Pair(RawPair<'p>),
87    /// Every byte of the input was consumed or held; pass the next chunk.
88    NeedInput,
89    /// The body is complete (only after `eof = true`).
90    End,
91}
92
93/// Where the pair of a step lives, decided before any borrow is taken.
94enum Found {
95    Input { start: usize, end: usize, at: u64 },
96    Scratch { len: usize, at: u64 },
97    NeedInput,
98    End,
99}
100
101impl<B: AsMut<[u8]> + AsRef<[u8]>> UrlencodedParser<B> {
102    /// A parser over `scratch`, which must hold at least
103    /// [`Limits::urlencoded_buffer_len`] bytes.
104    ///
105    /// # Errors
106    ///
107    /// `InvalidLimits`; `BufferTooSmall { needed }`.
108    pub fn with_buffer(limits: &Limits, scratch: B) -> Result<Self, FormError> {
109        limits.validate()?;
110        let needed = limits.urlencoded_buffer_len();
111        if scratch.as_ref().len() < needed {
112            return Err(FormError::BufferTooSmall { needed });
113        }
114        Ok(UrlencodedParser {
115            scratch,
116            held: 0,
117            held_at: 0,
118            release: false,
119            seen: 0,
120            dirty: 0,
121            budget: Budget::new(limits),
122            poisoned: false,
123            ended: false,
124        })
125    }
126
127    /// Feeds `input` (`eof = true` says it ends the body) and returns the
128    /// next pair, a request for more input, or the end.
129    ///
130    /// # Errors
131    ///
132    /// The errors of [`raw_pairs`](crate::raw_pairs); `Poisoned` after an
133    /// error until [`reset`](Self::reset).
134    pub fn parse<'p>(
135        &'p mut self,
136        input: &'p [u8],
137        eof: bool,
138    ) -> Result<UrlencodedStep<'p>, FormError> {
139        if self.poisoned {
140            return Err(FormError::Poisoned);
141        }
142        if self.release {
143            self.release = false;
144            self.clear_held();
145        }
146        let (consumed, found) = match self.step(input, eof) {
147            Ok(r) => r,
148            Err(e) => {
149                self.poisoned = true;
150                self.clear_held();
151                return Err(e);
152            }
153        };
154        let event = match found {
155            Found::Input { start, end, at } => {
156                UrlencodedEvent::Pair(RawPair::split(input.get(start..end).unwrap_or(&[]), at))
157            }
158            Found::Scratch { len, at } => {
159                self.release = true;
160                let seq = self.scratch.as_ref().get(..len).unwrap_or(&[]);
161                UrlencodedEvent::Pair(RawPair::split(seq, at))
162            }
163            Found::NeedInput => UrlencodedEvent::NeedInput,
164            Found::End => UrlencodedEvent::End,
165        };
166        Ok(UrlencodedStep { consumed, event })
167    }
168
169    /// Clears the counters and the poisoned state and wipes the scratch, for
170    /// the next body.
171    pub fn reset(&mut self) {
172        self.wipe_used();
173        self.held = 0;
174        self.held_at = 0;
175        self.release = false;
176        self.seen = 0;
177        self.budget.reset();
178        self.poisoned = false;
179        self.ended = false;
180    }
181
182    /// Body bytes consumed so far.
183    #[must_use]
184    pub const fn bytes_seen(&self) -> u64 {
185        self.seen
186    }
187
188    fn wipe_used(&mut self) {
189        let used = self.dirty;
190        if let Some(region) = self.scratch.as_mut().get_mut(..used) {
191            crate::wipe::wipe(region);
192        }
193        self.dirty = 0;
194    }
195
196    fn clear_held(&mut self) {
197        if let Some(held) = self.scratch.as_mut().get_mut(..self.held) {
198            crate::wipe::wipe(held);
199        }
200        self.held = 0;
201    }
202
203    fn step(&mut self, input: &[u8], eof: bool) -> Result<(usize, Found), FormError> {
204        if self.ended {
205            return Ok((0, Found::End));
206        }
207        let body = self.budget.body;
208        let mut pos = 0usize;
209        loop {
210            let rest = input.get(pos..).unwrap_or(&[]);
211            let allowed = usize::try_from(body.saturating_sub(self.seen)).unwrap_or(usize::MAX);
212            let window = rest.get(..allowed.min(rest.len())).unwrap_or(&[]);
213            let over = rest.len() > window.len();
214            let amp = crate::scan::find_any(window, 0, *b"&&&&").min(window.len());
215            let terminated = amp < window.len();
216            let piece = window.get(..amp).unwrap_or(&[]);
217            if !terminated && (!eof || over) {
218                // The sequence goes on past this chunk (or past the body
219                // budget): check what is known, then hold it or refuse.
220                self.check_partial(piece)?;
221                if over {
222                    return Err(FormError::limit(Limit::Body, Location::at(body)));
223                }
224                self.hold(piece);
225                pos = pos.saturating_add(piece.len());
226                self.seen = self.seen.saturating_add(piece.len() as u64);
227                return Ok((pos, Found::NeedInput));
228            }
229            // The sequence is complete: `piece` ends at `&` or at the end of
230            // the body.
231            let start = pos;
232            let at = self.seen;
233            let seq_len = self.held.saturating_add(piece.len());
234            pos = pos.saturating_add(piece.len());
235            self.seen = self.seen.saturating_add(piece.len() as u64);
236            if terminated {
237                pos = pos.saturating_add(1);
238                self.seen = self.seen.saturating_add(1);
239            }
240            if seq_len == 0 {
241                if terminated {
242                    continue;
243                }
244                self.ended = true;
245                return Ok((pos, Found::End));
246            }
247            if self.held == 0 {
248                self.budget.admit(&RawPair::split(piece, at))?;
249                return Ok((
250                    pos,
251                    Found::Input {
252                        start,
253                        end: start.saturating_add(piece.len()),
254                        at,
255                    },
256                ));
257            }
258            // Part of the sequence is held: the rest joins it in scratch.
259            self.check_partial(piece)?;
260            self.hold(piece);
261            let at = self.held_at;
262            let len = self.held;
263            let seq = self.scratch.as_ref().get(..len).unwrap_or(&[]);
264            self.budget.admit(&RawPair::split(seq, at))?;
265            return Ok((pos, Found::Scratch { len, at }));
266        }
267    }
268
269    /// Refuses the held sequence plus `more` when its complete form is
270    /// certain to be refused.
271    fn check_partial(&self, more: &[u8]) -> Result<(), FormError> {
272        let held = self.scratch.as_ref().get(..self.held).unwrap_or(&[]);
273        let at = if self.held == 0 {
274            self.seen
275        } else {
276            self.held_at
277        };
278        let (name_len, has_equals, value_len) = match held.iter().position(|&b| b == b'=') {
279            Some(e) => (
280                e,
281                true,
282                held.len()
283                    .saturating_sub(e)
284                    .saturating_sub(1)
285                    .saturating_add(more.len()),
286            ),
287            None => match more.iter().position(|&b| b == b'=') {
288                Some(e) => (
289                    held.len().saturating_add(e),
290                    true,
291                    more.len().saturating_sub(e).saturating_sub(1),
292                ),
293                None => (held.len().saturating_add(more.len()), false, 0),
294            },
295        };
296        self.budget
297            .admit_partial(at, name_len, has_equals, value_len)
298    }
299
300    /// Appends `bytes` to the held sequence. `check_partial` passed, so
301    /// they fit.
302    fn hold(&mut self, bytes: &[u8]) {
303        if bytes.is_empty() {
304            return;
305        }
306        if self.held == 0 {
307            self.held_at = self.seen;
308        }
309        let start = self.held;
310        let end = start.saturating_add(bytes.len());
311        if let Some(dst) = self.scratch.as_mut().get_mut(start..end) {
312            dst.copy_from_slice(bytes);
313            self.held = end;
314            self.dirty = self.dirty.max(end);
315        }
316    }
317}
318
319impl<B: AsMut<[u8]> + AsRef<[u8]>> Drop for UrlencodedParser<B> {
320    fn drop(&mut self) {
321        self.wipe_used();
322    }
323}
324
325#[cfg(feature = "alloc")]
326impl UrlencodedParser<alloc::vec::Vec<u8>> {
327    /// A parser that owns its scratch buffer.
328    ///
329    /// # Errors
330    ///
331    /// `InvalidLimits`.
332    ///
333    /// # Examples
334    ///
335    /// ```
336    /// use form_net::{Limits, UrlencodedEvent, UrlencodedParser};
337    ///
338    /// let mut parser = UrlencodedParser::new(&Limits::DEFAULT)?;
339    /// let step = parser.parse(b"a=1", true)?;
340    /// assert!(matches!(step.event, UrlencodedEvent::Pair(p) if p.value_raw() == b"1"));
341    /// # Ok::<(), form_net::FormError>(())
342    /// ```
343    pub fn new(limits: &Limits) -> Result<Self, FormError> {
344        limits.validate()?;
345        Self::with_buffer(limits, alloc::vec![0u8; limits.urlencoded_buffer_len()])
346    }
347}