Skip to main content

form_net/
hook.rs

1//! Hooks for other crates to take part in a parse without a second pass:
2//! an [`Inspector`] sees every part before it is stored, and a [`Sink`]
3//! decides where file data goes.
4//!
5//! Hooks are generic parameters, so a hook that does nothing
6//! ([`NoInspector`], [`DiscardSink`]) compiles to nothing.
7
8use crate::error::FormError;
9use crate::multipart::parser::{PartHead, PartSummary};
10
11/// Why an inspector refused the input: the HTTP status it wants and a
12/// stable, machine-readable code for the reason.
13///
14/// [`FormError::status_hint`](crate::FormError::status_hint) returns the
15/// status (400 when it is outside 400 to 599: a refusal is never a success);
16/// [`FormError::code`](crate::FormError::code) returns the code. The code
17/// must never contain input.
18///
19/// # Examples
20///
21/// ```
22/// use form_net::Refusal;
23///
24/// let r = Refusal::new(415, "customs.avatar_not_image");
25/// assert_eq!((r.status, r.code), (415, "customs.avatar_not_image"));
26/// ```
27#[non_exhaustive]
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub struct Refusal {
30    /// The HTTP status the inspector wants.
31    pub status: u16,
32    /// The inspector's stable code for the reason.
33    pub code: &'static str,
34}
35
36impl Refusal {
37    /// A refusal with this status and code.
38    #[must_use]
39    pub const fn new(status: u16, code: &'static str) -> Refusal {
40        Refusal { status, code }
41    }
42}
43
44/// What an inspector says about what it saw.
45///
46/// # Examples
47///
48/// ```
49/// use form_net::{Refusal, Verdict};
50///
51/// let v = Verdict::Refuse(Refusal::new(422, "example.not_allowed"));
52/// assert!(matches!(v, Verdict::Refuse(r) if r.status == 422));
53/// ```
54#[must_use]
55#[non_exhaustive]
56#[derive(Clone, Copy, Debug, PartialEq, Eq)]
57pub enum Verdict {
58    /// Go on.
59    Continue,
60    /// Discard the rest of this part (or this field); it still counts
61    /// against the budgets and is left out of the result.
62    Skip,
63    /// Stop the parse with this refusal.
64    Refuse(Refusal),
65}
66
67/// Takes part in a parse: called for every part head, the first bytes of
68/// every file, every data chunk, every complete text field, every part end,
69/// the end of the body, and once on any failure.
70///
71/// Every method has a default, so an inspector implements only what it
72/// checks.
73///
74/// **Order guarantee**, per part and at every split of the input:
75///
76/// ```text
77/// part_start ─▶ sniff (file parts, when sniff_bytes > 0) ─▶ data* ─▶ field (text fields) ─▶ part_end
78/// ... after the last part: end
79/// ... on any error, at any point: failed (once), then the error is returned
80/// ```
81///
82/// For urlencoded bodies only `field`, `end`, and `failed` are called.
83///
84/// # Examples
85///
86/// ```
87/// use form_net::{Inspector, PartHead, Refusal, Verdict};
88///
89/// /// Allows only PNG and JPEG avatars, judged by their bytes.
90/// struct ImagesOnly;
91///
92/// impl Inspector for ImagesOnly {
93///     fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
94///         match (head.name(), head.is_file()) {
95///             ("avatar", true) | ("title", false) => Verdict::Continue,
96///             _ => Verdict::Refuse(Refusal::new(422, "example.unexpected_field")),
97///         }
98///     }
99///     fn sniff(&mut self, _head: &PartHead<'_>, first: &[u8]) -> Verdict {
100///         if first.starts_with(b"\x89PNG\r\n\x1a\n") || first.starts_with(b"\xff\xd8\xff") {
101///             Verdict::Continue
102///         } else {
103///             Verdict::Refuse(Refusal::new(415, "example.avatar_not_image"))
104///         }
105///     }
106/// }
107/// ```
108pub trait Inspector {
109    /// A part's headers were read and passed every rule. Called before any
110    /// byte of the part reaches a sink.
111    fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
112        let _ = head;
113        Verdict::Continue
114    }
115
116    /// The first `min(sniff_bytes, part length)` bytes of a file part, as one
117    /// contiguous slice however the input was split, before any `data` call
118    /// for that part.
119    fn sniff(&mut self, head: &PartHead<'_>, first: &[u8]) -> Verdict {
120        let _ = (head, first);
121        Verdict::Continue
122    }
123
124    /// Bytes of part `part`'s data, text fields included.
125    fn data(&mut self, part: u32, chunk: &[u8]) -> Verdict {
126        let _ = (part, chunk);
127        Verdict::Continue
128    }
129
130    /// A complete text field (multipart) or a decoded pair (urlencoded): the
131    /// value as bytes, so the inspector can apply its own charset rules.
132    /// `Skip` leaves the field out of the result.
133    fn field(&mut self, name: &str, value: &[u8]) -> Verdict {
134        let _ = (name, value);
135        Verdict::Continue
136    }
137
138    /// A part ended. `Skip` here is treated as `Continue`.
139    fn part_end(&mut self, summary: &PartSummary) -> Verdict {
140        let _ = summary;
141        Verdict::Continue
142    }
143
144    /// The body ended. `Skip` here is treated as `Continue`.
145    fn end(&mut self) -> Verdict {
146        Verdict::Continue
147    }
148
149    /// The parse failed with `error` (for any reason, this inspector's
150    /// refusal included). Called once; it cannot change the outcome. The
151    /// hook for security signals: a stream of refusals from one client is a
152    /// probe.
153    fn failed(&mut self, error: &FormError) {
154        let _ = error;
155    }
156}
157
158impl<I: Inspector + ?Sized> Inspector for &mut I {
159    fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
160        (**self).part_start(head)
161    }
162    fn sniff(&mut self, head: &PartHead<'_>, first: &[u8]) -> Verdict {
163        (**self).sniff(head, first)
164    }
165    fn data(&mut self, part: u32, chunk: &[u8]) -> Verdict {
166        (**self).data(part, chunk)
167    }
168    fn field(&mut self, name: &str, value: &[u8]) -> Verdict {
169        (**self).field(name, value)
170    }
171    fn part_end(&mut self, summary: &PartSummary) -> Verdict {
172        (**self).part_end(summary)
173    }
174    fn end(&mut self) -> Verdict {
175        (**self).end()
176    }
177    fn failed(&mut self, error: &FormError) {
178        (**self).failed(error);
179    }
180}
181
182/// Two inspectors in one pass, `self.0` first (D-088). A refusal from either
183/// wins and the second is not asked; otherwise the part is skipped if either
184/// says `Skip`. `failed` reaches both. Nest pairs for longer chains:
185/// `(customs, (sentinel, app))`.
186///
187/// # Examples
188///
189/// ```
190/// use form_net::{Inspector, Limits, Policy, Refusal, Verdict};
191///
192/// struct NoAdmin;
193/// impl Inspector for NoAdmin {
194///     fn field(&mut self, _name: &str, value: &[u8]) -> Verdict {
195///         if value == b"admin" { Verdict::Refuse(Refusal::new(403, "app.admin")) } else { Verdict::Continue }
196///     }
197/// }
198/// struct Count(u32);
199/// impl Inspector for Count {
200///     fn field(&mut self, _name: &str, _value: &[u8]) -> Verdict {
201///         self.0 += 1;
202///         Verdict::Continue
203///     }
204/// }
205///
206/// let mut chain = (Count(0), NoAdmin);
207/// let r = form_net::parse_urlencoded_inspected(b"a=1&b=admin", &Limits::DEFAULT, &Policy::DEFAULT, &mut chain);
208/// assert_eq!(r.unwrap_err().status_hint(), 403);
209/// assert_eq!(chain.0 .0, 2);
210/// ```
211impl<A: Inspector, B: Inspector> Inspector for (A, B) {
212    fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
213        both(self.0.part_start(head), || self.1.part_start(head))
214    }
215    fn sniff(&mut self, head: &PartHead<'_>, first: &[u8]) -> Verdict {
216        both(self.0.sniff(head, first), || self.1.sniff(head, first))
217    }
218    fn data(&mut self, part: u32, chunk: &[u8]) -> Verdict {
219        both(self.0.data(part, chunk), || self.1.data(part, chunk))
220    }
221    fn field(&mut self, name: &str, value: &[u8]) -> Verdict {
222        both(self.0.field(name, value), || self.1.field(name, value))
223    }
224    fn part_end(&mut self, summary: &PartSummary) -> Verdict {
225        both(self.0.part_end(summary), || self.1.part_end(summary))
226    }
227    fn end(&mut self) -> Verdict {
228        both(self.0.end(), || self.1.end())
229    }
230    fn failed(&mut self, error: &FormError) {
231        self.0.failed(error);
232        self.1.failed(error);
233    }
234}
235
236/// Combines two verdicts: the first refusal wins, then any skip.
237fn both(first: Verdict, second: impl FnOnce() -> Verdict) -> Verdict {
238    if matches!(first, Verdict::Refuse(_)) {
239        return first;
240    }
241    match second() {
242        Verdict::Refuse(r) => Verdict::Refuse(r),
243        Verdict::Skip => Verdict::Skip,
244        _ => first,
245    }
246}
247
248/// A boxed inspector, for a chain chosen at run time (D-088).
249#[cfg(feature = "alloc")]
250impl<I: Inspector + ?Sized> Inspector for alloc::boxed::Box<I> {
251    fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
252        (**self).part_start(head)
253    }
254    fn sniff(&mut self, head: &PartHead<'_>, first: &[u8]) -> Verdict {
255        (**self).sniff(head, first)
256    }
257    fn data(&mut self, part: u32, chunk: &[u8]) -> Verdict {
258        (**self).data(part, chunk)
259    }
260    fn field(&mut self, name: &str, value: &[u8]) -> Verdict {
261        (**self).field(name, value)
262    }
263    fn part_end(&mut self, summary: &PartSummary) -> Verdict {
264        (**self).part_end(summary)
265    }
266    fn end(&mut self) -> Verdict {
267        (**self).end()
268    }
269    fn failed(&mut self, error: &FormError) {
270        (**self).failed(error);
271    }
272}
273
274/// An inspector that accepts everything; it compiles to nothing.
275///
276/// # Examples
277///
278/// ```
279/// use form_net::{Limits, NoInspector, Policy};
280///
281/// let form = form_net::parse_urlencoded_inspected(b"a=1", &Limits::DEFAULT, &Policy::DEFAULT, &mut NoInspector)?;
282/// assert_eq!(form.get("a"), Some("1"));
283/// # Ok::<(), form_net::FormError>(())
284/// ```
285#[derive(Clone, Copy, Debug, Default)]
286pub struct NoInspector;
287
288impl Inspector for NoInspector {}
289
290/// Where file parts go: one part at a time, `open`, any number of `write`,
291/// then `close` or `abort`.
292///
293/// The [`Driver`](crate::Driver) opens a sink only after the inspector has
294/// accepted the part's head and sniff window and the first chunk of data
295/// (or at the end of an empty file), so a refused or skipped part never
296/// reaches storage. Budgets are enforced before `write` is called, so a sink
297/// never has to count. `abort` must release everything, cannot fail, and
298/// must do nothing when no part is open; drivers call it on every error
299/// path.
300///
301/// # Examples
302///
303/// ```
304/// use form_net::{FormError, PartHead, Sink};
305///
306/// /// Counts bytes per file without keeping them.
307/// struct Counter(u64);
308///
309/// impl Sink for Counter {
310///     type Output = u64;
311///     fn open(&mut self, _head: &PartHead<'_>) -> Result<(), FormError> {
312///         self.0 = 0;
313///         Ok(())
314///     }
315///     fn write(&mut self, chunk: &[u8]) -> Result<(), FormError> {
316///         self.0 += chunk.len() as u64;
317///         Ok(())
318///     }
319///     fn close(&mut self) -> Result<u64, FormError> {
320///         Ok(self.0)
321///     }
322///     fn abort(&mut self) {}
323/// }
324/// ```
325pub trait Sink {
326    /// What a closed part becomes.
327    type Output;
328
329    /// A file part starts.
330    ///
331    /// # Errors
332    ///
333    /// The sink's own errors (storage, I/O).
334    fn open(&mut self, head: &PartHead<'_>) -> Result<(), FormError>;
335
336    /// More bytes of the current file part.
337    ///
338    /// # Errors
339    ///
340    /// The sink's own errors.
341    fn write(&mut self, chunk: &[u8]) -> Result<(), FormError>;
342
343    /// The current file part ended.
344    ///
345    /// # Errors
346    ///
347    /// The sink's own errors.
348    fn close(&mut self) -> Result<Self::Output, FormError>;
349
350    /// Discards the current file part, if any, and releases everything.
351    fn abort(&mut self);
352}
353
354/// A sink that discards file data and reports how many bytes it saw.
355///
356/// # Examples
357///
358/// ```
359/// use form_net::{DiscardSink, Sink};
360///
361/// let mut s = DiscardSink::default();
362/// s.write(b"abc")?;
363/// assert_eq!(s.close()?, 3);
364/// # Ok::<(), form_net::FormError>(())
365/// ```
366#[derive(Clone, Copy, Debug, Default)]
367pub struct DiscardSink {
368    bytes: u64,
369}
370
371impl Sink for DiscardSink {
372    type Output = u64;
373
374    fn open(&mut self, _head: &PartHead<'_>) -> Result<(), FormError> {
375        self.bytes = 0;
376        Ok(())
377    }
378
379    fn write(&mut self, chunk: &[u8]) -> Result<(), FormError> {
380        self.bytes = self.bytes.saturating_add(chunk.len() as u64);
381        Ok(())
382    }
383
384    fn close(&mut self) -> Result<u64, FormError> {
385        Ok(core::mem::take(&mut self.bytes))
386    }
387
388    fn abort(&mut self) {
389        self.bytes = 0;
390    }
391}
392
393#[cfg(feature = "alloc")]
394mod memory {
395    use alloc::string::String;
396    use alloc::vec::Vec;
397
398    use super::Sink;
399    use crate::error::FormError;
400    use crate::extract::FileData;
401    use crate::multipart::parser::PartHead;
402
403    /// A sink that keeps each file in memory as a [`FileData`].
404    ///
405    /// # Examples
406    ///
407    /// ```
408    /// use form_net::{Driver, MemorySink, MultipartParser, Boundary, Limits, Policy, NoInspector, Progress};
409    ///
410    /// let parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
411    /// let mut driver = Driver::new(parser, MemorySink::new());
412    /// let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a.txt\r\n\r\nhi\r\n--X--";
413    /// let (_, progress) = driver.push(body, true, &mut NoInspector)?;
414    /// match progress {
415    ///     Progress::File(file) => assert_eq!(file.bytes(), b"hi"),
416    ///     _ => unreachable!(),
417    /// }
418    /// # Ok::<(), form_net::FormError>(())
419    /// ```
420    #[derive(Debug, Default)]
421    pub struct MemorySink {
422        current: Option<FileData>,
423    }
424
425    impl MemorySink {
426        /// An empty sink.
427        #[must_use]
428        pub fn new() -> MemorySink {
429            MemorySink { current: None }
430        }
431    }
432
433    impl Sink for MemorySink {
434        type Output = FileData;
435
436        fn open(&mut self, head: &PartHead<'_>) -> Result<(), FormError> {
437            // Text only when it is text: a file name that is not UTF-8 is
438            // reported as absent rather than repaired.
439            let filename = head
440                .filename()
441                .and_then(|f| core::str::from_utf8(f.raw()).ok().map(String::from));
442            let content_type = head
443                .content_type()
444                .and_then(|c| core::str::from_utf8(c.raw()).ok().map(String::from));
445            self.current = Some(FileData::new(Vec::new(), filename, content_type));
446            Ok(())
447        }
448
449        fn write(&mut self, chunk: &[u8]) -> Result<(), FormError> {
450            if let Some(f) = self.current.as_mut() {
451                f.append(chunk);
452            }
453            Ok(())
454        }
455
456        fn close(&mut self) -> Result<FileData, FormError> {
457            Ok(self.current.take().unwrap_or_default())
458        }
459
460        fn abort(&mut self) {
461            self.current = None;
462        }
463    }
464}
465
466#[cfg(feature = "alloc")]
467pub use memory::MemorySink;