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    ///
39    /// # Examples
40    ///
41    /// ```
42    /// use form_net::{Refusal, Verdict};
43    ///
44    /// const BLOCKED: Refusal = Refusal::new(403, "app.blocked");
45    /// let verdict = Verdict::Refuse(BLOCKED);
46    /// assert_eq!(verdict, Verdict::Refuse(Refusal::new(403, "app.blocked")));
47    /// ```
48    #[must_use]
49    pub const fn new(status: u16, code: &'static str) -> Refusal {
50        Refusal { status, code }
51    }
52}
53
54/// What an inspector says about what it saw.
55///
56/// # Examples
57///
58/// ```
59/// use form_net::{Refusal, Verdict};
60///
61/// let v = Verdict::Refuse(Refusal::new(422, "example.not_allowed"));
62/// assert!(matches!(v, Verdict::Refuse(r) if r.status == 422));
63/// ```
64#[must_use]
65#[non_exhaustive]
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67pub enum Verdict {
68    /// Go on.
69    Continue,
70    /// Discard the rest of this part (or this field); it still counts
71    /// against the budgets and is left out of the result.
72    Skip,
73    /// Stop the parse with this refusal.
74    Refuse(Refusal),
75}
76
77/// Takes part in a parse: called for every part head, the first bytes of
78/// every file, every data chunk, every complete text field, every part end,
79/// the end of the body, and once on any failure.
80///
81/// Every method has a default, so an inspector implements only what it
82/// checks.
83///
84/// **Order guarantee**, per part and at every split of the input:
85///
86/// ```text
87/// part_start ─▶ sniff (file parts, when sniff_bytes > 0) ─▶ data* ─▶ field (text fields) ─▶ part_end
88/// ... after the last part: end
89/// ... on any error, at any point: failed (once), then the error is returned
90/// ```
91///
92/// For urlencoded bodies only `field`, `end`, and `failed` are called.
93///
94/// # Examples
95///
96/// ```
97/// use form_net::{Inspector, PartHead, Refusal, Verdict};
98///
99/// /// Allows only PNG and JPEG avatars, judged by their bytes.
100/// struct ImagesOnly;
101///
102/// impl Inspector for ImagesOnly {
103///     fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
104///         match (head.name(), head.is_file()) {
105///             ("avatar", true) | ("title", false) => Verdict::Continue,
106///             _ => Verdict::Refuse(Refusal::new(422, "example.unexpected_field")),
107///         }
108///     }
109///     fn sniff(&mut self, _head: &PartHead<'_>, first: &[u8]) -> Verdict {
110///         if first.starts_with(b"\x89PNG\r\n\x1a\n") || first.starts_with(b"\xff\xd8\xff") {
111///             Verdict::Continue
112///         } else {
113///             Verdict::Refuse(Refusal::new(415, "example.avatar_not_image"))
114///         }
115///     }
116/// }
117/// ```
118pub trait Inspector {
119    /// A part's headers were read and passed every rule. Called before any
120    /// byte of the part reaches a sink.
121    ///
122    /// # Examples
123    ///
124    /// ```
125    /// use form_net::{Inspector, PartHead, Refusal, Verdict};
126    ///
127    /// struct NoFiles;
128    /// impl Inspector for NoFiles {
129    ///     fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
130    ///         if head.is_file() { Verdict::Refuse(Refusal::new(415, "app.no_files")) } else { Verdict::Continue }
131    ///     }
132    /// }
133    ///
134    /// # let ct = b"multipart/form-data; boundary=X";
135    /// # let (limits, policy) = (form_net::Limits::DEFAULT, form_net::Policy::DEFAULT);
136    /// let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a.exe\r\n\r\nMZ..\r\n--X--";
137    /// let r = form_net::parse_multipart_inspected(ct, body, &limits, &policy, &mut NoFiles);
138    /// assert_eq!(r.unwrap_err().code(), "app.no_files");
139    /// ```
140    fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
141        let _ = head;
142        Verdict::Continue
143    }
144
145    /// The first `min(sniff_bytes, part length)` bytes of a file part, as one
146    /// contiguous slice however the input was split, before any `data` call
147    /// for that part.
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use form_net::{Inspector, PartHead, Refusal, Verdict};
153    ///
154    /// struct NoPrograms;
155    /// impl Inspector for NoPrograms {
156    ///     fn sniff(&mut self, _head: &PartHead<'_>, first: &[u8]) -> Verdict {
157    ///         if first.starts_with(b"MZ") { Verdict::Refuse(Refusal::new(415, "app.program")) } else { Verdict::Continue }
158    ///     }
159    /// }
160    ///
161    /// # let ct = b"multipart/form-data; boundary=X";
162    /// # let (limits, policy) = (form_net::Limits::DEFAULT, form_net::Policy::DEFAULT);
163    /// let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a.exe\r\n\r\nMZ..\r\n--X--";
164    /// let r = form_net::parse_multipart_inspected(ct, body, &limits, &policy, &mut NoPrograms);
165    /// assert_eq!(r.unwrap_err().status_hint(), 415);
166    /// ```
167    fn sniff(&mut self, head: &PartHead<'_>, first: &[u8]) -> Verdict {
168        let _ = (head, first);
169        Verdict::Continue
170    }
171
172    /// Bytes of part `part`'s data, text fields included.
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// use form_net::{Inspector, Verdict};
178    ///
179    /// struct Bytes(usize);
180    /// impl Inspector for Bytes {
181    ///     fn data(&mut self, _part: u32, chunk: &[u8]) -> Verdict {
182    ///         self.0 += chunk.len();
183    ///         Verdict::Continue
184    ///     }
185    /// }
186    ///
187    /// # let ct = b"multipart/form-data; boundary=X";
188    /// # let (limits, policy) = (form_net::Limits::DEFAULT, form_net::Policy::DEFAULT);
189    /// let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n12\r\n--X\r\nContent-Disposition: form-data; name=f; filename=a.txt\r\n\r\nabc\r\n--X--";
190    /// let mut seen = Bytes(0);
191    /// form_net::parse_multipart_inspected(ct, body, &limits, &policy, &mut seen)?;
192    /// assert_eq!(seen.0, 5);
193    /// # Ok::<(), form_net::FormError>(())
194    /// ```
195    fn data(&mut self, part: u32, chunk: &[u8]) -> Verdict {
196        let _ = (part, chunk);
197        Verdict::Continue
198    }
199
200    /// A complete text field (multipart) or a decoded pair (urlencoded): the
201    /// value as bytes, so the inspector can apply its own charset rules.
202    /// `Skip` leaves the field out of the result.
203    ///
204    /// # Examples
205    ///
206    /// ```
207    /// use form_net::{Inspector, Limits, Policy, Verdict};
208    ///
209    /// /// Leaves debugging fields out of the result.
210    /// struct DropDebug;
211    /// impl Inspector for DropDebug {
212    ///     fn field(&mut self, name: &str, _value: &[u8]) -> Verdict {
213    ///         if name.starts_with("debug_") { Verdict::Skip } else { Verdict::Continue }
214    ///     }
215    /// }
216    ///
217    /// let form = form_net::parse_urlencoded_inspected(b"a=1&debug_sql=1", &Limits::DEFAULT, &Policy::DEFAULT, &mut DropDebug)?;
218    /// assert_eq!((form.field("a"), form.field("debug_sql")), (Some("1"), None));
219    /// # Ok::<(), form_net::FormError>(())
220    /// ```
221    fn field(&mut self, name: &str, value: &[u8]) -> Verdict {
222        let _ = (name, value);
223        Verdict::Continue
224    }
225
226    /// A part ended. `Skip` here is treated as `Continue`.
227    ///
228    /// # Examples
229    ///
230    /// ```
231    /// use form_net::{Inspector, PartSummary, Verdict};
232    ///
233    /// struct Sizes(Vec<u64>);
234    /// impl Inspector for Sizes {
235    ///     fn part_end(&mut self, summary: &PartSummary) -> Verdict {
236    ///         self.0.push(summary.bytes);
237    ///         Verdict::Continue
238    ///     }
239    /// }
240    ///
241    /// # let ct = b"multipart/form-data; boundary=X";
242    /// # let (limits, policy) = (form_net::Limits::DEFAULT, form_net::Policy::DEFAULT);
243    /// let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n12\r\n--X\r\nContent-Disposition: form-data; name=b\r\n\r\nabc\r\n--X--";
244    /// let mut sizes = Sizes(Vec::new());
245    /// form_net::parse_multipart_inspected(ct, body, &limits, &policy, &mut sizes)?;
246    /// assert_eq!(sizes.0, [2, 3]);
247    /// # Ok::<(), form_net::FormError>(())
248    /// ```
249    fn part_end(&mut self, summary: &PartSummary) -> Verdict {
250        let _ = summary;
251        Verdict::Continue
252    }
253
254    /// The body ended. `Skip` here is treated as `Continue`.
255    ///
256    /// # Examples
257    ///
258    /// ```
259    /// use form_net::{Inspector, Limits, Policy, Refusal, Verdict};
260    ///
261    /// /// Refuses a body without an `email` field, once the whole body is read.
262    /// struct NeedsEmail(bool);
263    /// impl Inspector for NeedsEmail {
264    ///     fn field(&mut self, name: &str, _value: &[u8]) -> Verdict {
265    ///         self.0 |= name == "email";
266    ///         Verdict::Continue
267    ///     }
268    ///     fn end(&mut self) -> Verdict {
269    ///         if self.0 { Verdict::Continue } else { Verdict::Refuse(Refusal::new(422, "app.no_email")) }
270    ///     }
271    /// }
272    ///
273    /// let r = form_net::parse_urlencoded_inspected(b"name=a", &Limits::DEFAULT, &Policy::DEFAULT, &mut NeedsEmail(false));
274    /// assert_eq!(r.unwrap_err().code(), "app.no_email");
275    /// ```
276    fn end(&mut self) -> Verdict {
277        Verdict::Continue
278    }
279
280    /// The parse failed with `error` (for any reason, this inspector's
281    /// refusal included). Called once; it cannot change the outcome. The
282    /// hook for security signals: a stream of refusals from one client is a
283    /// probe.
284    ///
285    /// # Examples
286    ///
287    /// ```
288    /// use form_net::{FormError, Inspector, Limits, Policy};
289    ///
290    /// /// Keeps the code of the error, for a detection layer.
291    /// struct LastError(Option<&'static str>);
292    /// impl Inspector for LastError {
293    ///     fn failed(&mut self, error: &FormError) {
294    ///         self.0 = Some(error.code());
295    ///     }
296    /// }
297    ///
298    /// let mut log = LastError(None);
299    /// let _ = form_net::parse_urlencoded_inspected(b"a=%", &Limits::DEFAULT, &Policy::DEFAULT, &mut log);
300    /// assert_eq!(log.0, Some("encoding.invalid_percent"));
301    /// ```
302    fn failed(&mut self, error: &FormError) {
303        let _ = error;
304    }
305}
306
307impl<I: Inspector + ?Sized> Inspector for &mut I {
308    fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
309        (**self).part_start(head)
310    }
311    fn sniff(&mut self, head: &PartHead<'_>, first: &[u8]) -> Verdict {
312        (**self).sniff(head, first)
313    }
314    fn data(&mut self, part: u32, chunk: &[u8]) -> Verdict {
315        (**self).data(part, chunk)
316    }
317    fn field(&mut self, name: &str, value: &[u8]) -> Verdict {
318        (**self).field(name, value)
319    }
320    fn part_end(&mut self, summary: &PartSummary) -> Verdict {
321        (**self).part_end(summary)
322    }
323    fn end(&mut self) -> Verdict {
324        (**self).end()
325    }
326    fn failed(&mut self, error: &FormError) {
327        (**self).failed(error);
328    }
329}
330
331/// Two inspectors in one pass, `self.0` first (D-088). A refusal from either
332/// wins and the second is not asked; otherwise the part is skipped if either
333/// says `Skip`. `failed` reaches both. Nest pairs for longer chains:
334/// `(customs, (sentinel, app))`.
335///
336/// # Examples
337///
338/// ```
339/// use form_net::{Inspector, Limits, Policy, Refusal, Verdict};
340///
341/// struct NoAdmin;
342/// impl Inspector for NoAdmin {
343///     fn field(&mut self, _name: &str, value: &[u8]) -> Verdict {
344///         if value == b"admin" { Verdict::Refuse(Refusal::new(403, "app.admin")) } else { Verdict::Continue }
345///     }
346/// }
347/// struct Count(u32);
348/// impl Inspector for Count {
349///     fn field(&mut self, _name: &str, _value: &[u8]) -> Verdict {
350///         self.0 += 1;
351///         Verdict::Continue
352///     }
353/// }
354///
355/// let mut chain = (Count(0), NoAdmin);
356/// let r = form_net::parse_urlencoded_inspected(b"a=1&b=admin", &Limits::DEFAULT, &Policy::DEFAULT, &mut chain);
357/// assert_eq!(r.unwrap_err().status_hint(), 403);
358/// assert_eq!(chain.0 .0, 2);
359/// ```
360impl<A: Inspector, B: Inspector> Inspector for (A, B) {
361    fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
362        both(self.0.part_start(head), || self.1.part_start(head))
363    }
364    fn sniff(&mut self, head: &PartHead<'_>, first: &[u8]) -> Verdict {
365        both(self.0.sniff(head, first), || self.1.sniff(head, first))
366    }
367    fn data(&mut self, part: u32, chunk: &[u8]) -> Verdict {
368        both(self.0.data(part, chunk), || self.1.data(part, chunk))
369    }
370    fn field(&mut self, name: &str, value: &[u8]) -> Verdict {
371        both(self.0.field(name, value), || self.1.field(name, value))
372    }
373    fn part_end(&mut self, summary: &PartSummary) -> Verdict {
374        both(self.0.part_end(summary), || self.1.part_end(summary))
375    }
376    fn end(&mut self) -> Verdict {
377        both(self.0.end(), || self.1.end())
378    }
379    fn failed(&mut self, error: &FormError) {
380        self.0.failed(error);
381        self.1.failed(error);
382    }
383}
384
385/// Combines two verdicts: the first refusal wins, then any skip.
386fn both(first: Verdict, second: impl FnOnce() -> Verdict) -> Verdict {
387    if matches!(first, Verdict::Refuse(_)) {
388        return first;
389    }
390    match second() {
391        Verdict::Refuse(r) => Verdict::Refuse(r),
392        Verdict::Skip => Verdict::Skip,
393        _ => first,
394    }
395}
396
397/// A boxed inspector, for a chain chosen at run time (D-088).
398#[cfg(feature = "alloc")]
399impl<I: Inspector + ?Sized> Inspector for alloc::boxed::Box<I> {
400    fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
401        (**self).part_start(head)
402    }
403    fn sniff(&mut self, head: &PartHead<'_>, first: &[u8]) -> Verdict {
404        (**self).sniff(head, first)
405    }
406    fn data(&mut self, part: u32, chunk: &[u8]) -> Verdict {
407        (**self).data(part, chunk)
408    }
409    fn field(&mut self, name: &str, value: &[u8]) -> Verdict {
410        (**self).field(name, value)
411    }
412    fn part_end(&mut self, summary: &PartSummary) -> Verdict {
413        (**self).part_end(summary)
414    }
415    fn end(&mut self) -> Verdict {
416        (**self).end()
417    }
418    fn failed(&mut self, error: &FormError) {
419        (**self).failed(error);
420    }
421}
422
423/// An inspector that accepts everything; it compiles to nothing.
424///
425/// # Examples
426///
427/// ```
428/// use form_net::{Limits, NoInspector, Policy};
429///
430/// let form = form_net::parse_urlencoded_inspected(b"a=1", &Limits::DEFAULT, &Policy::DEFAULT, &mut NoInspector)?;
431/// assert_eq!(form.field("a"), Some("1"));
432/// # Ok::<(), form_net::FormError>(())
433/// ```
434#[derive(Clone, Copy, Debug, Default)]
435pub struct NoInspector;
436
437impl Inspector for NoInspector {}
438
439/// Verifies the anti-forgery token the moment its field ends, before any
440/// later part starts: a forged upload is refused before its first file
441/// opens a sink, so it costs no disk.
442///
443/// `verify` is called once, with the value of the text field named `name`
444/// (byte for byte, as [`TokenField`](crate::TokenField) matches it); it
445/// does the comparison (csrf-sec's `Csrf::verify`, constant time) and
446/// returns `Err` with the refusal to report. The check refuses, with
447/// status 403:
448///
449/// - `csrf.missing`: a file part starts before a token was verified, or the
450///   body ends without one;
451/// - `csrf.duplicate`: a second field with the token's name (two tokens
452///   would let an attacker choose which one is verified);
453/// - `csrf.not_text`: a token that is not UTF-8;
454/// - whatever `verify` returns.
455///
456/// Another inspector in the pair must not skip the token's part: the check
457/// never sees a value that was not read, so it refuses the body with
458/// `csrf.token_skipped` and status 500, a configuration error to fix in the
459/// other inspector, not a client's.
460///
461/// It works for multipart and urlencoded bodies alike, and composes with
462/// other inspectors as a pair: `(customs, check)`. Browsers send fields in
463/// document order, so the form puts the hidden token input before its file
464/// inputs. With `TokenField::anywhere`, where files may come first, verify
465/// [`captured_token`](crate::MultipartParser::captured_token) (or the
466/// upload's) after the parse instead. The token field stays in the result; leave it out of a typed
467/// form with `ignore`.
468///
469/// Cost: one name comparison per text field, and `verify` once.
470///
471/// # Examples
472///
473/// ```
474/// use form_net::{Limits, Policy, Refusal, TokenCheck};
475///
476/// # struct Csrf;
477/// # impl Csrf { fn verify(&self, _session: &str, token: &str) -> bool { token == "good" } }
478/// # let (csrf, session) = (Csrf, "session-id");
479/// let mut check = TokenCheck::new("csrf_token", |token| {
480///     if csrf.verify(session, token) { Ok(()) } else { Err(Refusal::new(403, "csrf.rejected")) }
481/// });
482/// let body = b"csrf_token=good&email=a%40b.example";
483/// let form = form_net::parse_urlencoded_inspected(body, &Limits::DEFAULT, &Policy::DEFAULT, &mut check)?;
484/// assert!(check.verified());
485/// assert_eq!(form.field("email"), Some("a@b.example"));
486///
487/// let mut check = TokenCheck::new("csrf_token", |_| Err(Refusal::new(403, "csrf.rejected")));
488/// let err = form_net::parse_urlencoded_inspected(b"csrf_token=bad", &Limits::DEFAULT, &Policy::DEFAULT, &mut check)
489///     .unwrap_err();
490/// assert_eq!((err.status_hint(), err.code()), (403, "csrf.rejected"));
491/// # Ok::<(), form_net::FormError>(())
492/// ```
493pub struct TokenCheck<V> {
494    name: &'static str,
495    verify: V,
496    seen: bool,
497    verified: bool,
498    /// The token's part started and its value has not come yet.
499    pending: bool,
500}
501
502impl<V: FnMut(&str) -> Result<(), Refusal>> TokenCheck<V> {
503    /// A check of the field `name` with `verify`.
504    ///
505    /// # Examples
506    ///
507    /// ```
508    /// use form_net::{Limits, Policy, Refusal, TokenCheck};
509    ///
510    /// let mut check = TokenCheck::new("csrf_token", |token| {
511    ///     if token == "expected" { Ok(()) } else { Err(Refusal::new(403, "csrf.rejected")) }
512    /// });
513    /// let body = b"csrf_token=forged&amount=100";
514    /// let err = form_net::parse_urlencoded_inspected(body, &Limits::DEFAULT, &Policy::DEFAULT, &mut check).unwrap_err();
515    /// assert_eq!(err.code(), "csrf.rejected");
516    /// ```
517    #[must_use]
518    pub const fn new(name: &'static str, verify: V) -> TokenCheck<V> {
519        TokenCheck {
520            name,
521            verify,
522            seen: false,
523            verified: false,
524            pending: false,
525        }
526    }
527
528    /// Whether the token was seen and `verify` accepted it.
529    ///
530    /// # Examples
531    ///
532    /// ```
533    /// use form_net::{Limits, Policy, TokenCheck};
534    ///
535    /// let mut check = TokenCheck::new("csrf_token", |_token| Ok(()));
536    /// assert!(!check.verified());
537    /// form_net::parse_urlencoded_inspected(b"csrf_token=t", &Limits::DEFAULT, &Policy::DEFAULT, &mut check)?;
538    /// assert!(check.verified());
539    ///
540    /// # Ok::<(), form_net::FormError>(())
541    /// ```
542    #[must_use]
543    pub const fn verified(&self) -> bool {
544        self.verified
545    }
546}
547
548impl<V> core::fmt::Debug for TokenCheck<V> {
549    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
550        f.debug_struct("TokenCheck")
551            .field("name", &self.name)
552            .field("seen", &self.seen)
553            .field("verified", &self.verified)
554            .finish_non_exhaustive()
555    }
556}
557
558const TOKEN_MISSING: Refusal = Refusal::new(403, "csrf.missing");
559const TOKEN_DUPLICATE: Refusal = Refusal::new(403, "csrf.duplicate");
560const TOKEN_NOT_TEXT: Refusal = Refusal::new(403, "csrf.not_text");
561const TOKEN_SKIPPED: Refusal = Refusal::new(500, "csrf.token_skipped");
562
563impl<V: FnMut(&str) -> Result<(), Refusal>> Inspector for TokenCheck<V> {
564    fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
565        if head.is_file() && !self.verified {
566            return Verdict::Refuse(TOKEN_MISSING);
567        }
568        self.pending = !head.is_file() && head.name() == self.name;
569        Verdict::Continue
570    }
571
572    fn field(&mut self, name: &str, value: &[u8]) -> Verdict {
573        if name != self.name {
574            return Verdict::Continue;
575        }
576        self.pending = false;
577        if self.seen {
578            return Verdict::Refuse(TOKEN_DUPLICATE);
579        }
580        self.seen = true;
581        let Ok(token) = core::str::from_utf8(value) else {
582            return Verdict::Refuse(TOKEN_NOT_TEXT);
583        };
584        match (self.verify)(token) {
585            Ok(()) => {
586                self.verified = true;
587                Verdict::Continue
588            }
589            Err(refusal) => Verdict::Refuse(refusal),
590        }
591    }
592
593    fn part_end(&mut self, _summary: &PartSummary) -> Verdict {
594        // The token's part ended without its value reaching `field`:
595        // another inspector skipped it.
596        if core::mem::take(&mut self.pending) {
597            return Verdict::Refuse(TOKEN_SKIPPED);
598        }
599        Verdict::Continue
600    }
601
602    fn end(&mut self) -> Verdict {
603        if self.verified {
604            Verdict::Continue
605        } else {
606            Verdict::Refuse(TOKEN_MISSING)
607        }
608    }
609}
610
611/// Where file parts go: one part at a time, `open`, any number of `write`,
612/// then `close` or `abort`.
613///
614/// The [`Driver`](crate::Driver) opens a sink only after the inspector has
615/// accepted the part's head and sniff window and the first chunk of data
616/// (or at the end of an empty file), so a refused or skipped part never
617/// reaches storage. Budgets are enforced before `write` is called, so a sink
618/// never has to count. `abort` must release everything, cannot fail, and
619/// must do nothing when no part is open; drivers call it on every error
620/// path.
621///
622/// # Examples
623///
624/// ```
625/// use form_net::{FormError, PartHead, Sink};
626///
627/// /// Counts bytes per file without keeping them.
628/// struct Counter(u64);
629///
630/// impl Sink for Counter {
631///     type Output = u64;
632///     fn open(&mut self, _head: &PartHead<'_>) -> Result<(), FormError> {
633///         self.0 = 0;
634///         Ok(())
635///     }
636///     fn write(&mut self, chunk: &[u8]) -> Result<(), FormError> {
637///         self.0 += chunk.len() as u64;
638///         Ok(())
639///     }
640///     fn close(&mut self) -> Result<u64, FormError> {
641///         Ok(self.0)
642///     }
643///     fn abort(&mut self) {}
644/// }
645/// ```
646pub trait Sink {
647    /// What a closed part becomes.
648    type Output;
649
650    /// A file part starts.
651    ///
652    /// # Errors
653    ///
654    /// The sink's own errors (storage, I/O).
655    ///
656    /// # Examples
657    ///
658    /// ```
659    /// use form_net::{MemorySink, OwnedPartHead, Sink};
660    ///
661    /// let head = OwnedPartHead::new();
662    /// let mut sink = MemorySink::new();
663    /// sink.open(&head.view())?;
664    /// sink.write(b"hi")?;
665    /// assert_eq!(sink.close()?.bytes(), b"hi");
666    ///
667    /// # Ok::<(), form_net::FormError>(())
668    /// ```
669    fn open(&mut self, head: &PartHead<'_>) -> Result<(), FormError>;
670
671    /// More bytes of the current file part.
672    ///
673    /// # Errors
674    ///
675    /// The sink's own errors.
676    ///
677    /// # Examples
678    ///
679    /// ```
680    /// use form_net::{DiscardSink, OwnedPartHead, Sink};
681    ///
682    /// let mut sink = DiscardSink::default();
683    /// sink.open(&OwnedPartHead::new().view())?;
684    /// sink.write(b"first ")?;
685    /// sink.write(b"second")?;
686    /// assert_eq!(sink.close()?, 12);
687    ///
688    /// # Ok::<(), form_net::FormError>(())
689    /// ```
690    fn write(&mut self, chunk: &[u8]) -> Result<(), FormError>;
691
692    /// The current file part ended.
693    ///
694    /// # Errors
695    ///
696    /// The sink's own errors.
697    ///
698    /// # Examples
699    ///
700    /// ```
701    /// use form_net::{MemorySink, OwnedPartHead, Sink};
702    ///
703    /// let mut sink = MemorySink::new();
704    /// sink.open(&OwnedPartHead::new().view())?;
705    /// sink.write(b"data")?;
706    /// let file = sink.close()?;
707    /// assert_eq!(file.len(), 4);
708    ///
709    /// # Ok::<(), form_net::FormError>(())
710    /// ```
711    fn close(&mut self) -> Result<Self::Output, FormError>;
712
713    /// Discards the current file part, if any, and releases everything.
714    ///
715    /// # Examples
716    ///
717    /// ```
718    /// use form_net::{MemorySink, OwnedPartHead, Sink};
719    ///
720    /// let mut sink = MemorySink::new();
721    /// sink.open(&OwnedPartHead::new().view())?;
722    /// sink.write(b"partial")?;
723    /// sink.abort();
724    /// // Nothing of the aborted part is kept.
725    /// assert!(sink.close()?.is_empty());
726    ///
727    /// # Ok::<(), form_net::FormError>(())
728    /// ```
729    fn abort(&mut self);
730}
731
732/// A sink that discards file data and reports how many bytes it saw.
733///
734/// # Examples
735///
736/// ```
737/// use form_net::{DiscardSink, Sink};
738///
739/// let mut s = DiscardSink::default();
740/// s.write(b"abc")?;
741/// assert_eq!(s.close()?, 3);
742/// # Ok::<(), form_net::FormError>(())
743/// ```
744#[derive(Clone, Copy, Debug, Default)]
745pub struct DiscardSink {
746    bytes: u64,
747}
748
749impl Sink for DiscardSink {
750    type Output = u64;
751
752    fn open(&mut self, _head: &PartHead<'_>) -> Result<(), FormError> {
753        self.bytes = 0;
754        Ok(())
755    }
756
757    fn write(&mut self, chunk: &[u8]) -> Result<(), FormError> {
758        self.bytes = self.bytes.saturating_add(chunk.len() as u64);
759        Ok(())
760    }
761
762    fn close(&mut self) -> Result<u64, FormError> {
763        Ok(core::mem::take(&mut self.bytes))
764    }
765
766    fn abort(&mut self) {
767        self.bytes = 0;
768    }
769}
770
771#[cfg(feature = "alloc")]
772mod memory {
773    use alloc::string::String;
774    use alloc::vec::Vec;
775
776    use super::Sink;
777    use crate::error::FormError;
778    use crate::extract::FileData;
779    use crate::multipart::parser::PartHead;
780
781    /// A sink that keeps each file in memory as a [`FileData`].
782    ///
783    /// # Examples
784    ///
785    /// ```
786    /// use form_net::{Driver, MemorySink, MultipartParser, Boundary, Limits, Policy, NoInspector, Progress};
787    ///
788    /// let parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
789    /// let mut driver = Driver::new(parser, MemorySink::new());
790    /// let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a.txt\r\n\r\nhi\r\n--X--";
791    /// let (_, progress) = driver.push(body, true, &mut NoInspector)?;
792    /// match progress {
793    ///     Progress::File(file) => assert_eq!(file.bytes(), b"hi"),
794    ///     _ => unreachable!(),
795    /// }
796    /// # Ok::<(), form_net::FormError>(())
797    /// ```
798    #[derive(Debug, Default)]
799    pub struct MemorySink {
800        current: Option<FileData>,
801    }
802
803    impl MemorySink {
804        /// An empty sink.
805        ///
806        /// # Examples
807        ///
808        /// ```
809        /// use form_net::{MemorySink, OwnedPartHead, Sink};
810        ///
811        /// let mut sink = MemorySink::new();
812        /// sink.open(&OwnedPartHead::new().view())?;
813        /// sink.write(b"hi")?;
814        /// assert_eq!(sink.close()?.bytes(), b"hi");
815        ///
816        /// # Ok::<(), form_net::FormError>(())
817        /// ```
818        #[must_use]
819        pub fn new() -> MemorySink {
820            MemorySink { current: None }
821        }
822    }
823
824    impl Sink for MemorySink {
825        type Output = FileData;
826
827        fn open(&mut self, head: &PartHead<'_>) -> Result<(), FormError> {
828            // Text only when it is text: a file name that is not UTF-8 is
829            // reported as absent rather than repaired.
830            let filename = head
831                .filename()
832                .and_then(|f| core::str::from_utf8(f.raw()).ok().map(String::from));
833            let content_type = head
834                .content_type()
835                .and_then(|c| core::str::from_utf8(c.raw()).ok().map(String::from));
836            self.current = Some(FileData::new(Vec::new(), filename, content_type));
837            Ok(())
838        }
839
840        fn write(&mut self, chunk: &[u8]) -> Result<(), FormError> {
841            if let Some(f) = self.current.as_mut() {
842                f.append(chunk);
843            }
844            Ok(())
845        }
846
847        fn close(&mut self) -> Result<FileData, FormError> {
848            Ok(self.current.take().unwrap_or_default())
849        }
850
851        fn abort(&mut self) {
852            self.current = None;
853        }
854    }
855}
856
857#[cfg(feature = "alloc")]
858pub use memory::MemorySink;