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