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;