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/// Verifies the anti-forgery token the moment its field ends, before any
291/// later part starts: a forged upload is refused before its first file
292/// opens a sink, so it costs no disk.
293///
294/// `verify` is called once, with the value of the text field named `name`
295/// (byte for byte, as [`TokenField`](crate::TokenField) matches it); it
296/// does the comparison (csrf-sec's `Csrf::verify`, constant time) and
297/// returns `Err` with the refusal to report. The check refuses, with
298/// status 403:
299///
300/// - `csrf.missing`: a file part starts before a token was verified, or the
301///   body ends without one;
302/// - `csrf.duplicate`: a second field with the token's name (two tokens
303///   would let an attacker choose which one is verified);
304/// - `csrf.not_text`: a token that is not UTF-8;
305/// - whatever `verify` returns.
306///
307/// It works for multipart and urlencoded bodies alike, and composes with
308/// other inspectors as a pair: `(customs, check)`. Browsers send fields in
309/// document order, so the form puts the hidden token input before its file
310/// inputs. With `TokenField::anywhere`, where files may come first, verify
311/// [`captured_token`](crate::MultipartParser::captured_token) (or the
312/// upload's) after the parse instead. The token field stays in the result; leave it out of a typed
313/// form with `ignore`.
314///
315/// Cost: one name comparison per text field, and `verify` once.
316///
317/// # Examples
318///
319/// ```
320/// use form_net::{Limits, Policy, Refusal, TokenCheck};
321///
322/// # struct Csrf;
323/// # impl Csrf { fn verify(&self, _session: &str, token: &str) -> bool { token == "good" } }
324/// # let (csrf, session) = (Csrf, "session-id");
325/// let mut check = TokenCheck::new("csrf_token", |token| {
326///     if csrf.verify(session, token) { Ok(()) } else { Err(Refusal::new(403, "csrf.rejected")) }
327/// });
328/// let body = b"csrf_token=good&email=a%40b.example";
329/// let form = form_net::parse_urlencoded_inspected(body, &Limits::DEFAULT, &Policy::DEFAULT, &mut check)?;
330/// assert!(check.verified());
331/// assert_eq!(form.get("email"), Some("a@b.example"));
332///
333/// let mut check = TokenCheck::new("csrf_token", |_| Err(Refusal::new(403, "csrf.rejected")));
334/// let err = form_net::parse_urlencoded_inspected(b"csrf_token=bad", &Limits::DEFAULT, &Policy::DEFAULT, &mut check)
335///     .unwrap_err();
336/// assert_eq!((err.status_hint(), err.code()), (403, "csrf.rejected"));
337/// # Ok::<(), form_net::FormError>(())
338/// ```
339pub struct TokenCheck<V> {
340    name: &'static str,
341    verify: V,
342    seen: bool,
343    verified: bool,
344}
345
346impl<V: FnMut(&str) -> Result<(), Refusal>> TokenCheck<V> {
347    /// A check of the field `name` with `verify`.
348    #[must_use]
349    pub const fn new(name: &'static str, verify: V) -> TokenCheck<V> {
350        TokenCheck {
351            name,
352            verify,
353            seen: false,
354            verified: false,
355        }
356    }
357
358    /// Whether the token was seen and `verify` accepted it.
359    #[must_use]
360    pub const fn verified(&self) -> bool {
361        self.verified
362    }
363}
364
365impl<V> core::fmt::Debug for TokenCheck<V> {
366    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
367        f.debug_struct("TokenCheck")
368            .field("name", &self.name)
369            .field("seen", &self.seen)
370            .field("verified", &self.verified)
371            .finish_non_exhaustive()
372    }
373}
374
375const TOKEN_MISSING: Refusal = Refusal::new(403, "csrf.missing");
376const TOKEN_DUPLICATE: Refusal = Refusal::new(403, "csrf.duplicate");
377const TOKEN_NOT_TEXT: Refusal = Refusal::new(403, "csrf.not_text");
378
379impl<V: FnMut(&str) -> Result<(), Refusal>> Inspector for TokenCheck<V> {
380    fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
381        if head.is_file() && !self.verified {
382            return Verdict::Refuse(TOKEN_MISSING);
383        }
384        Verdict::Continue
385    }
386
387    fn field(&mut self, name: &str, value: &[u8]) -> Verdict {
388        if name != self.name {
389            return Verdict::Continue;
390        }
391        if self.seen {
392            return Verdict::Refuse(TOKEN_DUPLICATE);
393        }
394        self.seen = true;
395        let Ok(token) = core::str::from_utf8(value) else {
396            return Verdict::Refuse(TOKEN_NOT_TEXT);
397        };
398        match (self.verify)(token) {
399            Ok(()) => {
400                self.verified = true;
401                Verdict::Continue
402            }
403            Err(refusal) => Verdict::Refuse(refusal),
404        }
405    }
406
407    fn end(&mut self) -> Verdict {
408        if self.verified {
409            Verdict::Continue
410        } else {
411            Verdict::Refuse(TOKEN_MISSING)
412        }
413    }
414}
415
416/// Where file parts go: one part at a time, `open`, any number of `write`,
417/// then `close` or `abort`.
418///
419/// The [`Driver`](crate::Driver) opens a sink only after the inspector has
420/// accepted the part's head and sniff window and the first chunk of data
421/// (or at the end of an empty file), so a refused or skipped part never
422/// reaches storage. Budgets are enforced before `write` is called, so a sink
423/// never has to count. `abort` must release everything, cannot fail, and
424/// must do nothing when no part is open; drivers call it on every error
425/// path.
426///
427/// # Examples
428///
429/// ```
430/// use form_net::{FormError, PartHead, Sink};
431///
432/// /// Counts bytes per file without keeping them.
433/// struct Counter(u64);
434///
435/// impl Sink for Counter {
436///     type Output = u64;
437///     fn open(&mut self, _head: &PartHead<'_>) -> Result<(), FormError> {
438///         self.0 = 0;
439///         Ok(())
440///     }
441///     fn write(&mut self, chunk: &[u8]) -> Result<(), FormError> {
442///         self.0 += chunk.len() as u64;
443///         Ok(())
444///     }
445///     fn close(&mut self) -> Result<u64, FormError> {
446///         Ok(self.0)
447///     }
448///     fn abort(&mut self) {}
449/// }
450/// ```
451pub trait Sink {
452    /// What a closed part becomes.
453    type Output;
454
455    /// A file part starts.
456    ///
457    /// # Errors
458    ///
459    /// The sink's own errors (storage, I/O).
460    fn open(&mut self, head: &PartHead<'_>) -> Result<(), FormError>;
461
462    /// More bytes of the current file part.
463    ///
464    /// # Errors
465    ///
466    /// The sink's own errors.
467    fn write(&mut self, chunk: &[u8]) -> Result<(), FormError>;
468
469    /// The current file part ended.
470    ///
471    /// # Errors
472    ///
473    /// The sink's own errors.
474    fn close(&mut self) -> Result<Self::Output, FormError>;
475
476    /// Discards the current file part, if any, and releases everything.
477    fn abort(&mut self);
478}
479
480/// A sink that discards file data and reports how many bytes it saw.
481///
482/// # Examples
483///
484/// ```
485/// use form_net::{DiscardSink, Sink};
486///
487/// let mut s = DiscardSink::default();
488/// s.write(b"abc")?;
489/// assert_eq!(s.close()?, 3);
490/// # Ok::<(), form_net::FormError>(())
491/// ```
492#[derive(Clone, Copy, Debug, Default)]
493pub struct DiscardSink {
494    bytes: u64,
495}
496
497impl Sink for DiscardSink {
498    type Output = u64;
499
500    fn open(&mut self, _head: &PartHead<'_>) -> Result<(), FormError> {
501        self.bytes = 0;
502        Ok(())
503    }
504
505    fn write(&mut self, chunk: &[u8]) -> Result<(), FormError> {
506        self.bytes = self.bytes.saturating_add(chunk.len() as u64);
507        Ok(())
508    }
509
510    fn close(&mut self) -> Result<u64, FormError> {
511        Ok(core::mem::take(&mut self.bytes))
512    }
513
514    fn abort(&mut self) {
515        self.bytes = 0;
516    }
517}
518
519#[cfg(feature = "alloc")]
520mod memory {
521    use alloc::string::String;
522    use alloc::vec::Vec;
523
524    use super::Sink;
525    use crate::error::FormError;
526    use crate::extract::FileData;
527    use crate::multipart::parser::PartHead;
528
529    /// A sink that keeps each file in memory as a [`FileData`].
530    ///
531    /// # Examples
532    ///
533    /// ```
534    /// use form_net::{Driver, MemorySink, MultipartParser, Boundary, Limits, Policy, NoInspector, Progress};
535    ///
536    /// let parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
537    /// let mut driver = Driver::new(parser, MemorySink::new());
538    /// let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a.txt\r\n\r\nhi\r\n--X--";
539    /// let (_, progress) = driver.push(body, true, &mut NoInspector)?;
540    /// match progress {
541    ///     Progress::File(file) => assert_eq!(file.bytes(), b"hi"),
542    ///     _ => unreachable!(),
543    /// }
544    /// # Ok::<(), form_net::FormError>(())
545    /// ```
546    #[derive(Debug, Default)]
547    pub struct MemorySink {
548        current: Option<FileData>,
549    }
550
551    impl MemorySink {
552        /// An empty sink.
553        #[must_use]
554        pub fn new() -> MemorySink {
555            MemorySink { current: None }
556        }
557    }
558
559    impl Sink for MemorySink {
560        type Output = FileData;
561
562        fn open(&mut self, head: &PartHead<'_>) -> Result<(), FormError> {
563            // Text only when it is text: a file name that is not UTF-8 is
564            // reported as absent rather than repaired.
565            let filename = head
566                .filename()
567                .and_then(|f| core::str::from_utf8(f.raw()).ok().map(String::from));
568            let content_type = head
569                .content_type()
570                .and_then(|c| core::str::from_utf8(c.raw()).ok().map(String::from));
571            self.current = Some(FileData::new(Vec::new(), filename, content_type));
572            Ok(())
573        }
574
575        fn write(&mut self, chunk: &[u8]) -> Result<(), FormError> {
576            if let Some(f) = self.current.as_mut() {
577                f.append(chunk);
578            }
579            Ok(())
580        }
581
582        fn close(&mut self) -> Result<FileData, FormError> {
583            Ok(self.current.take().unwrap_or_default())
584        }
585
586        fn abort(&mut self) {
587            self.current = None;
588        }
589    }
590}
591
592#[cfg(feature = "alloc")]
593pub use memory::MemorySink;