form_net/multipart/parser.rs
1//! The sans-IO multipart parser: a state machine over the delimiter matcher
2//! and the header reader.
3
4use core::fmt;
5
6use crate::error::{EncodingError, FormError, Limit, Location, PolicyRefusal, SyntaxError};
7use crate::limits::{CHARSET_CAPTURE, EmptyFilePolicy, Limits, Policy, TokenPlacement};
8use crate::multipart::boundary::{Boundary, Hit};
9use crate::multipart::filename::FileName;
10use crate::multipart::header::{Areas, BlockScan, HeadFacts, Text, read_block};
11
12/// Whether a part is a text field or a file.
13///
14/// # Examples
15///
16/// ```
17/// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n1\r\n--X--";
18/// let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", body)?;
19/// assert_eq!(mp.parts()[0].kind(), form_net::PartKind::Field);
20/// # Ok::<(), form_net::FormError>(())
21/// ```
22#[non_exhaustive]
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum PartKind {
25 /// A text field: no `filename` parameter.
26 Field,
27 /// A file: a `filename` parameter (even an empty one) or an accepted
28 /// `filename*`.
29 File,
30}
31
32/// The `charset` a part declared.
33///
34/// `Unspecified` means no `charset` parameter: UTF-8 is assumed unless a
35/// `_charset_` field before the part said otherwise. `Other` is any label
36/// that is not UTF-8 or US-ASCII; its text is refused by every text accessor.
37///
38/// # Examples
39///
40/// ```
41/// use form_net::Charset;
42///
43/// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n1\r\n--X--";
44/// # let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", body)?;
45/// assert_eq!(mp.parts()[0].content_type().unwrap().charset(), Charset::Utf8);
46/// # Ok::<(), form_net::FormError>(())
47/// ```
48#[non_exhaustive]
49#[derive(Clone, Copy, Debug, PartialEq, Eq)]
50pub enum Charset {
51 /// `utf-8`.
52 Utf8,
53 /// `us-ascii`.
54 Ascii,
55 /// No `charset` parameter.
56 Unspecified,
57 /// Any other label.
58 Other,
59}
60
61/// How a text value is read, from its charset label.
62#[cfg(feature = "alloc")]
63#[derive(Clone, Copy, Debug, PartialEq, Eq)]
64pub(crate) enum TextRule {
65 /// UTF-8 (no label, or `utf-8`).
66 Utf8,
67 /// `us-ascii`: every byte below 0x80. WHATWG readers decode the label as
68 /// windows-1252, so a higher byte would read differently there (D-150).
69 Ascii,
70 /// Any other label.
71 Refuse,
72}
73
74#[cfg(feature = "alloc")]
75impl TextRule {
76 /// Checks `bytes` against the rule, then as UTF-8.
77 pub(crate) fn check(self, bytes: &[u8]) -> Result<(), EncodingError> {
78 match self {
79 TextRule::Refuse => Err(EncodingError::UnsupportedCharset),
80 TextRule::Ascii if !bytes.is_ascii() => Err(EncodingError::UnsupportedCharset),
81 _ if core::str::from_utf8(bytes).is_err() => Err(EncodingError::InvalidUtf8),
82 _ => Ok(()),
83 }
84 }
85}
86
87/// The rule a declared `_charset_` value sets for the text after it.
88#[cfg(feature = "alloc")]
89pub(crate) fn declared_rule(declared: Option<&[u8]>) -> TextRule {
90 match declared {
91 None => TextRule::Utf8,
92 Some(label) => match Charset::from_label(label) {
93 Charset::Utf8 => TextRule::Utf8,
94 Charset::Ascii => TextRule::Ascii,
95 _ => TextRule::Refuse,
96 },
97 }
98}
99
100impl Charset {
101 /// How a text part with this charset is read: by its own label, else by
102 /// the `_charset_` declared before it, else as UTF-8.
103 #[cfg(feature = "alloc")]
104 pub(crate) const fn rule(self, declared: TextRule) -> TextRule {
105 match self {
106 Charset::Utf8 => TextRule::Utf8,
107 Charset::Ascii => TextRule::Ascii,
108 Charset::Other => TextRule::Refuse,
109 Charset::Unspecified => declared,
110 }
111 }
112
113 pub(crate) fn from_label(label: &[u8]) -> Charset {
114 if label.eq_ignore_ascii_case(b"utf-8") {
115 Charset::Utf8
116 } else if label.eq_ignore_ascii_case(b"us-ascii") {
117 Charset::Ascii
118 } else {
119 Charset::Other
120 }
121 }
122}
123
124/// One step of the parser: how many input bytes it consumed, and what
125/// happened.
126///
127/// # Examples
128///
129/// ```
130/// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
131/// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
132/// let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n1\r\n--X--";
133/// let step = parser.parse(body, true)?;
134/// assert!(matches!(step.event, MultipartEvent::PartStart(_)));
135/// // The rest of the body goes to the next call.
136/// assert!(step.consumed > 0 && step.consumed < body.len());
137/// # Ok::<(), form_net::FormError>(())
138/// ```
139#[non_exhaustive]
140#[derive(Debug)]
141pub struct MultipartStep<'p> {
142 /// Bytes of the input consumed; pass the rest (and later new bytes) to
143 /// the next call.
144 pub consumed: usize,
145 /// What happened.
146 pub event: MultipartEvent<'p>,
147}
148
149/// What the parser found.
150///
151/// Per part the order is always `PartStart`, any number of `Data` (none for
152/// an empty part), `PartEnd`. A `Data` slice is never empty. After the last
153/// part comes `End`. `NeedInput` can appear anywhere between.
154///
155/// `Debug` prints the length of `Data`, never the bytes.
156///
157/// # Examples
158///
159/// ```
160/// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
161/// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
162/// let mut rest = &b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n12\r\n--X--"[..];
163/// let mut seen = String::new();
164/// loop {
165/// let step = parser.parse(rest, true)?;
166/// rest = &rest[step.consumed..];
167/// match step.event {
168/// MultipartEvent::PartStart(head) => seen.push_str(head.name()),
169/// MultipartEvent::Data(bytes) => seen.push_str(&format!(" {}", bytes.len())),
170/// MultipartEvent::PartEnd(_) => seen.push_str(" end"),
171/// MultipartEvent::End | MultipartEvent::NeedInput => break,
172/// _ => {}
173/// }
174/// }
175/// assert_eq!(seen, "a 2 end");
176/// # Ok::<(), form_net::FormError>(())
177/// ```
178#[non_exhaustive]
179pub enum MultipartEvent<'p> {
180 /// A part's headers were read and checked.
181 PartStart(PartHead<'p>),
182 /// Bytes of the current part's data.
183 Data(&'p [u8]),
184 /// The current part ended.
185 PartEnd(PartSummary),
186 /// The close delimiter was read: the form is complete.
187 End,
188 /// Every byte given was consumed; give more.
189 NeedInput,
190}
191
192impl fmt::Debug for MultipartEvent<'_> {
193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194 match self {
195 Self::PartStart(head) => f.debug_tuple("PartStart").field(head).finish(),
196 Self::Data(bytes) => write!(f, "Data({} bytes)", bytes.len()),
197 Self::PartEnd(summary) => f.debug_tuple("PartEnd").field(summary).finish(),
198 Self::End => f.write_str("End"),
199 Self::NeedInput => f.write_str("NeedInput"),
200 }
201 }
202}
203
204/// What one part was.
205///
206/// # Examples
207///
208/// ```
209/// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
210/// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
211/// let mut rest = &b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\nhello\r\n--X--"[..];
212/// let summary = loop {
213/// let step = parser.parse(rest, true)?;
214/// rest = &rest[step.consumed..];
215/// if let MultipartEvent::PartEnd(summary) = step.event {
216/// break summary;
217/// }
218/// };
219/// assert_eq!((summary.index, summary.bytes, summary.skipped), (0, 5, false));
220/// assert_eq!(summary.kind, PartKind::Field);
221/// # Ok::<(), form_net::FormError>(())
222/// ```
223#[non_exhaustive]
224#[derive(Clone, Copy, Debug, PartialEq, Eq)]
225pub struct PartSummary {
226 /// The part index, from 0.
227 pub index: u32,
228 /// Field or file.
229 pub kind: PartKind,
230 /// The length of the part's data.
231 pub bytes: u64,
232 /// Whether the part was skipped (by `skip_part` or an inspector).
233 pub skipped: bool,
234}
235
236/// The validated header block of one part, borrowed.
237///
238/// A head is only produced after every header budget and rule passed.
239///
240/// # Examples
241///
242/// ```
243/// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
244/// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
245/// let body = b"--X\r\nContent-Disposition: form-data; name=doc; filename=a.pdf\r\nContent-Type: application/pdf\r\n\r\n%PDF\r\n--X--";
246/// let MultipartEvent::PartStart(head) = parser.parse(body, true)?.event else { unreachable!() };
247/// assert_eq!(head.name(), "doc");
248/// assert!(head.is_file());
249/// assert!(head.content_type().unwrap().is("application", "pdf"));
250/// # Ok::<(), form_net::FormError>(())
251/// ```
252#[derive(Clone, Copy)]
253pub struct PartHead<'p> {
254 index: u32,
255 block: &'p [u8],
256 name: &'p str,
257 name_raw: &'p [u8],
258 kind: PartKind,
259 filename: Option<FileName<'p>>,
260 content_type: Option<&'p [u8]>,
261 charset: Charset,
262}
263
264impl fmt::Debug for PartHead<'_> {
265 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266 f.debug_struct("PartHead")
267 .field("index", &self.index)
268 .field("name", &self.name)
269 .field("kind", &self.kind)
270 .field("filename", &self.filename)
271 .finish_non_exhaustive()
272 }
273}
274
275impl<'p> PartHead<'p> {
276 /// A head over bytes kept elsewhere (an owned copy).
277 #[cfg(feature = "alloc")]
278 #[allow(clippy::too_many_arguments)]
279 pub(crate) const fn from_parts(
280 index: u32,
281 block: &'p [u8],
282 name: &'p str,
283 name_raw: &'p [u8],
284 kind: PartKind,
285 filename: Option<FileName<'p>>,
286 content_type: Option<&'p [u8]>,
287 charset: Charset,
288 ) -> Self {
289 Self {
290 index,
291 block,
292 name,
293 name_raw,
294 kind,
295 filename,
296 content_type,
297 charset,
298 }
299 }
300
301 /// The header block, the final CRLF CRLF included.
302 #[cfg(feature = "alloc")]
303 pub(crate) const fn block(&self) -> &'p [u8] {
304 self.block
305 }
306
307 /// The part index, from 0, in body order.
308 ///
309 /// # Examples
310 ///
311 /// ```
312 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
313 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
314 /// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n1\r\n--X--";
315 /// let MultipartEvent::PartStart(head) = parser.parse(body, true)?.event else { unreachable!() };
316 /// assert_eq!(head.index(), 0);
317 /// # Ok::<(), form_net::FormError>(())
318 /// ```
319 #[must_use]
320 pub const fn index(&self) -> u32 {
321 self.index
322 }
323
324 /// The field name, with `%0A`, `%0D`, `%22` decoded to LF, CR, `"` (no
325 /// other escape is decoded). Always valid UTF-8 and never empty.
326 ///
327 /// # Examples
328 ///
329 /// ```
330 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
331 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
332 /// # let body = b"--X\r\nContent-Disposition: form-data; name=\"a%22b\"\r\n\r\n1\r\n--X--";
333 /// let MultipartEvent::PartStart(head) = parser.parse(body, true)?.event else { unreachable!() };
334 /// assert_eq!(head.name(), "a\"b");
335 /// # Ok::<(), form_net::FormError>(())
336 /// ```
337 #[must_use]
338 pub const fn name(&self) -> &'p str {
339 self.name
340 }
341
342 /// The field name as sent.
343 ///
344 /// # Examples
345 ///
346 /// ```
347 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
348 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
349 /// # let body = b"--X\r\nContent-Disposition: form-data; name=\"a%22b\"\r\n\r\n1\r\n--X--";
350 /// let MultipartEvent::PartStart(head) = parser.parse(body, true)?.event else { unreachable!() };
351 /// assert_eq!(head.name_raw(), b"a%22b");
352 /// # Ok::<(), form_net::FormError>(())
353 /// ```
354 #[must_use]
355 pub const fn name_raw(&self) -> &'p [u8] {
356 self.name_raw
357 }
358
359 /// Field or file.
360 ///
361 /// # Examples
362 ///
363 /// ```
364 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
365 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
366 /// # let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a.txt\r\n\r\nhi\r\n--X--";
367 /// let MultipartEvent::PartStart(head) = parser.parse(body, true)?.event else { unreachable!() };
368 /// assert_eq!(head.kind(), PartKind::File);
369 /// # Ok::<(), form_net::FormError>(())
370 /// ```
371 #[must_use]
372 pub const fn kind(&self) -> PartKind {
373 self.kind
374 }
375
376 /// Whether this is a file part.
377 ///
378 /// # Examples
379 ///
380 /// ```
381 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
382 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
383 /// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n1\r\n--X--";
384 /// let MultipartEvent::PartStart(head) = parser.parse(body, true)?.event else { unreachable!() };
385 /// assert!(!head.is_file());
386 /// # Ok::<(), form_net::FormError>(())
387 /// ```
388 #[must_use]
389 pub const fn is_file(&self) -> bool {
390 matches!(self.kind, PartKind::File)
391 }
392
393 /// The client's file name, for file parts.
394 ///
395 /// # Examples
396 ///
397 /// ```
398 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
399 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
400 /// # let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a.txt\r\n\r\nhi\r\n--X--";
401 /// let MultipartEvent::PartStart(head) = parser.parse(body, true)?.event else { unreachable!() };
402 /// assert_eq!(head.filename().unwrap().raw(), b"a.txt");
403 /// # Ok::<(), form_net::FormError>(())
404 /// ```
405 #[must_use]
406 pub const fn filename(&self) -> Option<FileName<'p>> {
407 self.filename
408 }
409
410 /// The declared `Content-Type`: a hint, never trusted.
411 ///
412 /// # Examples
413 ///
414 /// ```
415 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
416 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
417 /// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\nContent-Type: text/plain\r\n\r\n1\r\n--X--";
418 /// let MultipartEvent::PartStart(head) = parser.parse(body, true)?.event else { unreachable!() };
419 /// assert!(head.content_type().unwrap().is("text", "plain"));
420 /// # Ok::<(), form_net::FormError>(())
421 /// ```
422 #[must_use]
423 pub fn content_type(&self) -> Option<ContentType<'p>> {
424 self.content_type.and_then(ContentType::parse)
425 }
426
427 /// The part's own `charset` parameter (`Unspecified` when absent).
428 ///
429 /// # Examples
430 ///
431 /// ```
432 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
433 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
434 /// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\nContent-Type: text/plain; charset=us-ascii\r\n\r\n1\r\n--X--";
435 /// let MultipartEvent::PartStart(head) = parser.parse(body, true)?.event else { unreachable!() };
436 /// assert_eq!(head.charset(), Charset::Ascii);
437 /// # Ok::<(), form_net::FormError>(())
438 /// ```
439 #[must_use]
440 pub const fn charset(&self) -> Charset {
441 self.charset
442 }
443
444 /// Every header line, in order, as raw `(name, value)` bytes.
445 ///
446 /// # Examples
447 ///
448 /// ```
449 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
450 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
451 /// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\nContent-Type: text/plain\r\n\r\n1\r\n--X--";
452 /// let MultipartEvent::PartStart(head) = parser.parse(body, true)?.event else { unreachable!() };
453 /// let (name, value) = head.headers().nth(1).unwrap();
454 /// assert_eq!((name, value), (&b"Content-Type"[..], &b"text/plain"[..]));
455 /// # Ok::<(), form_net::FormError>(())
456 /// ```
457 #[must_use]
458 pub fn headers(&self) -> Headers<'p> {
459 Headers { rest: self.block }
460 }
461
462 /// The size of the header block, the final CRLF CRLF included.
463 ///
464 /// # Examples
465 ///
466 /// ```
467 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
468 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
469 /// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n1\r\n--X--";
470 /// let MultipartEvent::PartStart(head) = parser.parse(body, true)?.event else { unreachable!() };
471 /// // The block ends with the blank line.
472 /// let block = "Content-Disposition: form-data; name=a\r\n\r\n";
473 /// assert_eq!(head.header_bytes() as usize, block.len());
474 /// # Ok::<(), form_net::FormError>(())
475 /// ```
476 #[must_use]
477 pub const fn header_bytes(&self) -> u32 {
478 self.block.len() as u32
479 }
480}
481
482/// A part's declared `Content-Type`, already checked. **A hint**: the client
483/// chose it.
484///
485/// # Examples
486///
487/// ```
488/// # let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a.png\r\nContent-Type: Image/PNG; q=\"x y\"\r\n\r\n\r\n--X--";
489/// # let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", body)?;
490/// let ct = mp.file("f").unwrap().content_type().unwrap();
491/// assert!(ct.is("image", "png"));
492/// assert_eq!((ct.type_(), ct.subtype()), ("Image", "PNG"));
493/// assert_eq!(ct.parameter("Q"), Some(&b"x y"[..]));
494/// # Ok::<(), form_net::FormError>(())
495/// ```
496#[derive(Clone, Copy)]
497pub struct ContentType<'p> {
498 raw: &'p [u8],
499 media: parse_net::MediaType<'p>,
500}
501
502impl fmt::Debug for ContentType<'_> {
503 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
504 f.debug_struct("ContentType")
505 .field("type", &self.type_())
506 .field("subtype", &self.subtype())
507 .finish_non_exhaustive()
508 }
509}
510
511impl<'p> ContentType<'p> {
512 pub(crate) fn parse(raw: &'p [u8]) -> Option<ContentType<'p>> {
513 parse_net::media::parse(raw, &crate::kind::HEADER_LIMITS)
514 .ok()
515 .map(|media| ContentType { raw, media })
516 }
517
518 /// The value as sent.
519 ///
520 /// # Examples
521 ///
522 /// ```
523 /// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n1\r\n--X--";
524 /// # let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", body)?;
525 /// let ct = mp.parts()[0].content_type().unwrap();
526 /// assert_eq!(ct.raw(), b"text/plain; charset=utf-8");
527 /// # Ok::<(), form_net::FormError>(())
528 /// ```
529 #[must_use]
530 pub const fn raw(&self) -> &'p [u8] {
531 self.raw
532 }
533
534 /// The type, as sent.
535 ///
536 /// # Examples
537 ///
538 /// ```
539 /// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n1\r\n--X--";
540 /// # let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", body)?;
541 /// let ct = mp.parts()[0].content_type().unwrap();
542 /// assert_eq!(ct.type_(), "text");
543 /// # Ok::<(), form_net::FormError>(())
544 /// ```
545 #[must_use]
546 pub fn type_(&self) -> &'p str {
547 core::str::from_utf8(self.media.type_()).unwrap_or("")
548 }
549
550 /// The subtype, as sent.
551 ///
552 /// # Examples
553 ///
554 /// ```
555 /// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n1\r\n--X--";
556 /// # let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", body)?;
557 /// let ct = mp.parts()[0].content_type().unwrap();
558 /// assert_eq!(ct.subtype(), "plain");
559 /// # Ok::<(), form_net::FormError>(())
560 /// ```
561 #[must_use]
562 pub fn subtype(&self) -> &'p str {
563 core::str::from_utf8(self.media.subtype()).unwrap_or("")
564 }
565
566 /// Whether the type and subtype are these, ASCII case-insensitively.
567 ///
568 /// # Examples
569 ///
570 /// ```
571 /// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n1\r\n--X--";
572 /// # let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", body)?;
573 /// let ct = mp.parts()[0].content_type().unwrap();
574 /// assert!(ct.is("TEXT", "Plain"));
575 /// assert!(!ct.is("text", "html"));
576 /// # Ok::<(), form_net::FormError>(())
577 /// ```
578 #[must_use]
579 pub fn is(&self, type_: &str, subtype: &str) -> bool {
580 self.media.is(type_, subtype)
581 }
582
583 /// A parameter's value by name (ASCII case-insensitive), without
584 /// surrounding quotes; backslashes are literal, as browsers send them.
585 ///
586 /// # Examples
587 ///
588 /// ```
589 /// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n1\r\n--X--";
590 /// # let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", body)?;
591 /// let ct = mp.parts()[0].content_type().unwrap();
592 /// assert_eq!(ct.parameter("Charset"), Some(&b"utf-8"[..]));
593 /// assert_eq!(ct.parameter("boundary"), None);
594 /// # Ok::<(), form_net::FormError>(())
595 /// ```
596 #[must_use]
597 pub fn parameter(&self, name: &str) -> Option<&'p [u8]> {
598 self.media
599 .params()
600 .get(name)
601 .map(|w| crate::kind::word_raw(&w))
602 }
603
604 /// The `charset` parameter.
605 ///
606 /// # Examples
607 ///
608 /// ```
609 /// use form_net::Charset;
610 ///
611 /// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n1\r\n--X--";
612 /// # let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", body)?;
613 /// let ct = mp.parts()[0].content_type().unwrap();
614 /// assert_eq!(ct.charset(), Charset::Utf8);
615 /// # Ok::<(), form_net::FormError>(())
616 /// ```
617 #[must_use]
618 pub fn charset(&self) -> Charset {
619 self.parameter("charset")
620 .map_or(Charset::Unspecified, Charset::from_label)
621 }
622}
623
624/// Every header line of a part, in order, as raw `(name, value)` bytes with
625/// the value trimmed.
626///
627/// `Debug` prints the header names only: values carry file names.
628///
629/// # Examples
630///
631/// ```
632/// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
633/// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
634/// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\nContent-Type: text/plain\r\n\r\n1\r\n--X--";
635/// let MultipartEvent::PartStart(head) = parser.parse(body, true)?.event else { unreachable!() };
636/// let names: Vec<&[u8]> = head.headers().map(|(name, _)| name).collect();
637/// assert_eq!(names, [&b"Content-Disposition"[..], &b"Content-Type"[..]]);
638/// # Ok::<(), form_net::FormError>(())
639/// ```
640#[derive(Clone)]
641pub struct Headers<'p> {
642 rest: &'p [u8],
643}
644
645impl fmt::Debug for Headers<'_> {
646 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
647 let mut list = f.debug_list();
648 for (name, _) in self.clone() {
649 list.entry(&core::str::from_utf8(name).unwrap_or("?"));
650 }
651 list.finish()
652 }
653}
654
655impl<'p> Iterator for Headers<'p> {
656 type Item = (&'p [u8], &'p [u8]);
657
658 fn next(&mut self) -> Option<Self::Item> {
659 let lf = self.rest.iter().position(|&b| b == b'\n')?;
660 let line = self.rest.get(..lf.saturating_sub(1)).unwrap_or(&[]);
661 self.rest = self.rest.get(lf.wrapping_add(1)..).unwrap_or(&[]);
662 if line.is_empty() {
663 self.rest = &[];
664 return None;
665 }
666 let colon = line.iter().position(|&b| b == b':').unwrap_or(line.len());
667 let name = line.get(..colon).unwrap_or(&[]);
668 let value = line.get(colon.wrapping_add(1)..).unwrap_or(&[]);
669 Some((name, parse_net::text::trim_ows(value)))
670 }
671}
672
673#[derive(Clone, Copy, Debug, PartialEq, Eq)]
674enum State {
675 Preamble,
676 /// After a delimiter: expecting `--`, padding, or CRLF.
677 AfterDelimiter(After),
678 Headers,
679 Body,
680 /// The delimiter that ends the current part was found; `PartEnd` is due.
681 EndOfPart,
682 Epilogue,
683 Poisoned,
684}
685
686#[derive(Clone, Copy, Debug, PartialEq, Eq)]
687enum After {
688 Start,
689 Dash,
690 Padding(u8),
691 Cr,
692}
693
694/// The token field's progress.
695#[derive(Clone, Copy, Debug, PartialEq, Eq)]
696enum Token {
697 NotSeen,
698 Capturing,
699 Captured,
700}
701
702const SKIPPING: u8 = 1;
703const IS_TOKEN: u8 = 2;
704const IS_CHARSET: u8 = 4;
705const DEFERRED: u8 = 8;
706
707/// Where a head's block is.
708#[derive(Clone, Copy, Debug)]
709pub(crate) enum Source {
710 /// In the input, from this index.
711 Input(usize),
712 /// In the parser buffer, from 0.
713 Buffer,
714}
715
716/// What a call to `advance` produced, as positions only.
717#[derive(Clone, Copy, Debug)]
718pub(crate) enum Outcome {
719 PartStart(HeadFacts, Source),
720 /// `input[start..end]`.
721 DataInput(usize, usize),
722 /// The first bytes of the delimiter.
723 DataDelimiter(usize),
724 PartEnd(PartSummary),
725 End,
726 NeedInput,
727}
728
729/// The sans-IO `multipart/form-data` parser.
730///
731/// Feed it bytes as they arrive, in chunks of any size split at any byte;
732/// it returns one [`MultipartEvent`] per call and how many bytes it consumed. The
733/// events are identical for every split of a body that parses; for a body
734/// that fails, the error is identical, though a split body may emit data
735/// before it that a whole one does not. It never allocates after
736/// construction. The delimiter search skips 32 bytes at a time to a line
737/// break followed by `-` and compares the delimiter there a word at a time;
738/// bytes that may start a delimiter at a chunk's end are carried and
739/// scanned again with the next chunk (at most 73 per boundary), so the
740/// search stays linear. The streaming automaton (a Knuth-Morris-Pratt
741/// table) only carries that state between chunks.
742///
743/// The buffer holds the header block, the decoded field name, the decoded
744/// `filename*` (under `AcceptAlone`), the token, and the `_charset_` value:
745/// [`Limits::multipart_buffer_len`] bytes. It is wiped on `reset` and on
746/// drop.
747///
748/// # Examples
749///
750/// ```
751/// use form_net::{MultipartEvent, FormKind, Limits, MultipartParser, Policy};
752///
753/// let content_type = b"multipart/form-data; boundary=X";
754/// let body = b"--X\r\nContent-Disposition: form-data; name=\"title\"\r\n\r\nHoliday\r\n--X--\r\n";
755/// let FormKind::Multipart(boundary) = FormKind::detect(content_type)? else { unreachable!() };
756/// let mut parser = MultipartParser::new(boundary, &Limits::DEFAULT, &Policy::DEFAULT)?;
757///
758/// let mut events = Vec::new();
759/// let mut chunks = body.chunks(7).peekable(); // any split gives the same events
760/// while let Some(chunk) = chunks.next() {
761/// let last = chunks.peek().is_none();
762/// let mut rest = chunk;
763/// loop {
764/// let step = parser.parse(rest, last)?;
765/// rest = &rest[step.consumed..];
766/// match step.event {
767/// MultipartEvent::PartStart(head) => events.push(format!("start {}", head.name())),
768/// MultipartEvent::Data(bytes) => events.push(format!("data {}", bytes.len())),
769/// MultipartEvent::PartEnd(summary) => events.push(format!("end {}", summary.bytes)),
770/// MultipartEvent::End => break,
771/// MultipartEvent::NeedInput => break,
772/// _ => {}
773/// }
774/// }
775/// }
776/// assert!(parser.is_done());
777/// assert_eq!(events.first().map(String::as_str), Some("start title"));
778/// assert_eq!(events.last().map(String::as_str), Some("end 7"));
779/// # Ok::<(), form_net::FormError>(())
780/// ```
781pub struct MultipartParser<B: AsMut<[u8]> + AsRef<[u8]>> {
782 boundary: Boundary,
783 limits: Limits,
784 policy: Policy,
785 buffer: B,
786 state: State,
787 /// Delimiter bytes matched and not yet known to be data.
788 k: u8,
789 /// Bytes of a lone CR or LF and `--boundary` matched (D-113); while
790 /// `k > 0` it follows from `k`.
791 bare: u8,
792 flags: u8,
793 token: Token,
794 token_len: u16,
795 charset_len: u8,
796 charset_done: bool,
797 scan: BlockScan,
798 /// Header bytes copied into the buffer.
799 copied: u32,
800 /// The longest header block read so far: every byte the parser writes
801 /// into the header, name, and file-name areas lies within this many
802 /// bytes of each area's start (decoding never lengthens), so the wipe
803 /// covers exactly what was written (D-103).
804 dirty: u32,
805 parts: u32,
806 files: u32,
807 fields: u32,
808 kind: PartKind,
809 part_bytes: u64,
810 text_total: u64,
811 data_start: u64,
812 preamble: u64,
813 epilogue: u64,
814 /// Where the current header block starts in the body.
815 head_start: u64,
816 /// Where the epilogue starts in the body.
817 epilogue_start: u64,
818 bytes_seen: u64,
819}
820
821impl<B: AsMut<[u8]> + AsRef<[u8]>> fmt::Debug for MultipartParser<B> {
822 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
823 f.debug_struct("MultipartParser")
824 .field("boundary", &self.boundary)
825 .field("state", &self.state)
826 .field("bytes_seen", &self.bytes_seen)
827 .field("parts_seen", &self.parts)
828 .finish_non_exhaustive()
829 }
830}
831
832#[cfg(feature = "alloc")]
833impl MultipartParser<alloc::vec::Vec<u8>> {
834 /// A parser with its own buffer of
835 /// [`Limits::multipart_buffer_len`] bytes, allocated once.
836 ///
837 /// # Errors
838 ///
839 /// `InvalidLimits` from validating the limits with the policy.
840 ///
841 /// # Examples
842 ///
843 /// ```
844 /// use form_net::{Boundary, Limits, MultipartParser, Policy};
845 ///
846 /// let parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
847 /// assert_eq!(parser.bytes_seen(), 0);
848 /// # Ok::<(), form_net::FormError>(())
849 /// ```
850 pub fn new(boundary: Boundary, limits: &Limits, policy: &Policy) -> Result<Self, FormError> {
851 limits.validate_with(policy)?;
852 let buffer = alloc::vec![0u8; limits.multipart_buffer_len(policy)];
853 Self::with_buffer(boundary, limits, policy, buffer)
854 }
855}
856
857impl<B: AsMut<[u8]> + AsRef<[u8]>> MultipartParser<B> {
858 /// A parser over a buffer the caller provides; it never allocates.
859 ///
860 /// # Errors
861 ///
862 /// `InvalidLimits`; `BufferTooSmall` when `buffer` is shorter than
863 /// [`Limits::multipart_buffer_len`].
864 ///
865 /// # Examples
866 ///
867 /// ```
868 /// use form_net::{Boundary, Limits, MultipartParser, Policy};
869 ///
870 /// const LIMITS: Limits = Limits::DEFAULT.with_max_part_header_bytes(1024);
871 /// const LEN: usize = LIMITS.multipart_buffer_len(&Policy::DEFAULT);
872 /// let mut buffer = [0u8; LEN];
873 /// let parser = MultipartParser::with_buffer(Boundary::new(b"X")?, &LIMITS, &Policy::DEFAULT, &mut buffer[..])?;
874 /// # Ok::<(), form_net::FormError>(())
875 /// ```
876 pub fn with_buffer(
877 boundary: Boundary,
878 limits: &Limits,
879 policy: &Policy,
880 buffer: B,
881 ) -> Result<Self, FormError> {
882 limits.validate_with(policy)?;
883 let needed = limits.multipart_buffer_len(policy);
884 if buffer.as_ref().len() < needed {
885 return Err(FormError::BufferTooSmall { needed });
886 }
887 Ok(Self {
888 boundary,
889 limits: *limits,
890 policy: *policy,
891 buffer,
892 state: State::Preamble,
893 // The body may start with `--boundary`: start as if CRLF had
894 // been seen.
895 k: 2,
896 bare: 1,
897 flags: 0,
898 token: Token::NotSeen,
899 token_len: 0,
900 charset_len: 0,
901 charset_done: false,
902 scan: BlockScan::default(),
903 copied: 0,
904 dirty: 0,
905 parts: 0,
906 files: 0,
907 fields: 0,
908 kind: PartKind::Field,
909 part_bytes: 0,
910 text_total: 0,
911 data_start: 0,
912 preamble: 0,
913 epilogue: 0,
914 head_start: 0,
915 epilogue_start: 0,
916 bytes_seen: 0,
917 })
918 }
919
920 /// Consumes a prefix of `input` and returns one event. `eof = true` says
921 /// `input` ends the body.
922 ///
923 /// # Errors
924 ///
925 /// Every multipart error (see the crate's `docs/API.md`); `Poisoned`
926 /// after any error until [`reset`](Self::reset).
927 ///
928 /// # Examples
929 ///
930 /// ```
931 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
932 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
933 /// let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n1\r\n--X--";
934 /// // The first 10 bytes are not a whole header block: give more.
935 /// let step = parser.parse(&body[..10], false)?;
936 /// assert!(matches!(step.event, MultipartEvent::NeedInput));
937 /// let rest = &body[step.consumed..];
938 /// let step = parser.parse(rest, true)?;
939 /// assert!(matches!(step.event, MultipartEvent::PartStart(head) if head.name() == "a"));
940 /// # Ok::<(), form_net::FormError>(())
941 /// ```
942 pub fn parse<'p>(
943 &'p mut self,
944 input: &'p [u8],
945 eof: bool,
946 ) -> Result<MultipartStep<'p>, FormError> {
947 let (consumed, outcome) = self.advance(input, eof)?;
948 Ok(MultipartStep {
949 consumed,
950 event: self.view(outcome, input),
951 })
952 }
953
954 /// Discards the rest of the current part: its bytes are consumed and
955 /// counted against the budgets but produce no `Data` events, and its
956 /// summary has `skipped = true`. Outside a part it does nothing.
957 ///
958 /// # Examples
959 ///
960 /// ```
961 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
962 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
963 /// # let body = b"--X\r\nContent-Disposition: form-data; name=big\r\n\r\nunwanted\r\n--X--";
964 /// let step = parser.parse(body, true)?;
965 /// # assert!(matches!(step.event, MultipartEvent::PartStart(_)));
966 /// let mut rest = &body[step.consumed..];
967 /// parser.skip_part();
968 /// let summary = loop {
969 /// let step = parser.parse(rest, true)?;
970 /// rest = &rest[step.consumed..];
971 /// assert!(!matches!(step.event, MultipartEvent::Data(_)));
972 /// if let MultipartEvent::PartEnd(summary) = step.event {
973 /// break summary;
974 /// }
975 /// };
976 /// assert!(summary.skipped);
977 /// # Ok::<(), form_net::FormError>(())
978 /// ```
979 pub fn skip_part(&mut self) {
980 if matches!(self.state, State::Body) {
981 self.flags |= SKIPPING;
982 }
983 }
984
985 /// Returns the parser to its initial state for a new body, wipes its
986 /// buffer, and clears the poisoned state. It never allocates.
987 ///
988 /// # Examples
989 ///
990 /// ```
991 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
992 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
993 /// assert!(parser.parse(b"no delimiter here", true).is_err());
994 /// // Poisoned until reset; reset also takes the next body's boundary.
995 /// assert!(parser.parse(b"", true).is_err());
996 /// parser.reset(Boundary::new(b"Y")?);
997 /// let body = b"--Y\r\nContent-Disposition: form-data; name=a\r\n\r\n1\r\n--Y--";
998 /// assert!(matches!(parser.parse(body, true)?.event, MultipartEvent::PartStart(_)));
999 /// # Ok::<(), form_net::FormError>(())
1000 /// ```
1001 pub fn reset(&mut self, boundary: Boundary) {
1002 self.wipe_used();
1003 let limits = self.limits;
1004 let policy = self.policy;
1005 self.boundary = boundary;
1006 self.state = State::Preamble;
1007 self.k = 2;
1008 self.bare = 1;
1009 self.flags = 0;
1010 self.token = Token::NotSeen;
1011 self.token_len = 0;
1012 self.charset_len = 0;
1013 self.charset_done = false;
1014 self.scan = BlockScan::default();
1015 self.copied = 0;
1016 self.parts = 0;
1017 self.files = 0;
1018 self.fields = 0;
1019 self.kind = PartKind::Field;
1020 self.part_bytes = 0;
1021 self.text_total = 0;
1022 self.data_start = 0;
1023 self.preamble = 0;
1024 self.epilogue = 0;
1025 self.head_start = 0;
1026 self.epilogue_start = 0;
1027 self.bytes_seen = 0;
1028 self.limits = limits;
1029 self.policy = policy;
1030 }
1031
1032 /// Body bytes consumed so far.
1033 ///
1034 /// # Examples
1035 ///
1036 /// ```
1037 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
1038 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
1039 /// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n1\r\n--X--";
1040 /// let consumed = parser.parse(body, true)?.consumed;
1041 /// assert_eq!(parser.bytes_seen(), consumed as u64);
1042 /// # Ok::<(), form_net::FormError>(())
1043 /// ```
1044 #[must_use]
1045 pub const fn bytes_seen(&self) -> u64 {
1046 self.bytes_seen
1047 }
1048
1049 /// Parts started so far.
1050 ///
1051 /// # Examples
1052 ///
1053 /// ```
1054 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
1055 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
1056 /// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n1\r\n--X--";
1057 /// parser.parse(body, true)?;
1058 /// assert_eq!(parser.parts_seen(), 1);
1059 /// # Ok::<(), form_net::FormError>(())
1060 /// ```
1061 #[must_use]
1062 pub const fn parts_seen(&self) -> u32 {
1063 self.parts
1064 }
1065
1066 /// File parts counted so far.
1067 ///
1068 /// # Examples
1069 ///
1070 /// ```
1071 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
1072 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
1073 /// # let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a.txt\r\n\r\nhi\r\n--X--";
1074 /// parser.parse(body, true)?;
1075 /// assert_eq!(parser.files_seen(), 1);
1076 /// # Ok::<(), form_net::FormError>(())
1077 /// ```
1078 #[must_use]
1079 pub const fn files_seen(&self) -> u32 {
1080 self.files
1081 }
1082
1083 /// Whether the close delimiter was read.
1084 ///
1085 /// # Examples
1086 ///
1087 /// ```
1088 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
1089 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
1090 /// let mut rest = &b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n1\r\n--X--"[..];
1091 /// assert!(!parser.is_done());
1092 /// while !parser.is_done() {
1093 /// let step = parser.parse(rest, true)?;
1094 /// rest = &rest[step.consumed..];
1095 /// }
1096 /// // The close delimiter was read.
1097 /// assert!(rest.is_empty());
1098 /// # Ok::<(), form_net::FormError>(())
1099 /// ```
1100 #[must_use]
1101 pub fn is_done(&self) -> bool {
1102 self.state == State::Epilogue
1103 }
1104
1105 /// The limits the parser was built with.
1106 ///
1107 /// # Examples
1108 ///
1109 /// ```
1110 /// use form_net::{Boundary, Limits, MultipartParser, Policy};
1111 ///
1112 /// let limits = Limits::DEFAULT.with_max_part_header_bytes(1024);
1113 /// let parser = MultipartParser::new(Boundary::new(b"X")?, &limits, &Policy::DEFAULT)?;
1114 /// assert_eq!(parser.limits(), &limits);
1115 /// # Ok::<(), form_net::FormError>(())
1116 /// ```
1117 #[must_use]
1118 pub const fn limits(&self) -> &Limits {
1119 &self.limits
1120 }
1121
1122 /// The policy the parser was built with.
1123 ///
1124 /// # Examples
1125 ///
1126 /// ```
1127 /// use form_net::{Boundary, Limits, MultipartParser, Policy, TokenField};
1128 ///
1129 /// let policy = Policy::DEFAULT.with_token_field(TokenField::new("csrf"));
1130 /// let parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &policy)?;
1131 /// assert_eq!(parser.policy(), &policy);
1132 /// # Ok::<(), form_net::FormError>(())
1133 /// ```
1134 #[must_use]
1135 pub const fn policy(&self) -> &Policy {
1136 &self.policy
1137 }
1138
1139 /// The token field's value, once its part has ended.
1140 ///
1141 /// # Examples
1142 ///
1143 /// ```
1144 /// use form_net::{Boundary, Limits, MultipartParser, Policy, TokenField};
1145 ///
1146 /// let policy = Policy::DEFAULT.with_token_field(TokenField::new("csrf"));
1147 /// let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &policy)?;
1148 /// let mut rest = &b"--X\r\nContent-Disposition: form-data; name=csrf\r\n\r\nabc123\r\n--X--"[..];
1149 /// while !parser.is_done() {
1150 /// let step = parser.parse(rest, true)?;
1151 /// rest = &rest[step.consumed..];
1152 /// }
1153 /// assert_eq!(parser.captured_token(), Some("abc123"));
1154 /// # Ok::<(), form_net::FormError>(())
1155 /// ```
1156 #[must_use]
1157 pub fn captured_token(&self) -> Option<&str> {
1158 if self.token != Token::Captured {
1159 return None;
1160 }
1161 let start = self.token_area();
1162 let bytes = self
1163 .buffer
1164 .as_ref()
1165 .get(start..start.saturating_add(usize::from(self.token_len)))?;
1166 core::str::from_utf8(bytes).ok()
1167 }
1168
1169 /// The value of the last complete `_charset_` field.
1170 ///
1171 /// # Examples
1172 ///
1173 /// ```
1174 /// # use form_net::{Boundary, Charset, Limits, MultipartEvent, MultipartParser, PartKind, Policy};
1175 /// # let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
1176 /// let mut rest = &b"--X\r\nContent-Disposition: form-data; name=_charset_\r\n\r\nutf-8\r\n--X--"[..];
1177 /// while !parser.is_done() {
1178 /// let step = parser.parse(rest, true)?;
1179 /// rest = &rest[step.consumed..];
1180 /// }
1181 /// assert_eq!(parser.declared_charset(), Some(&b"utf-8"[..]));
1182 /// # Ok::<(), form_net::FormError>(())
1183 /// ```
1184 #[must_use]
1185 pub fn declared_charset(&self) -> Option<&[u8]> {
1186 if !self.charset_done {
1187 return None;
1188 }
1189 let start = self.charset_area();
1190 self.buffer
1191 .as_ref()
1192 .get(start..start.saturating_add(usize::from(self.charset_len)))
1193 }
1194
1195 /// The decoded name of the last head, `len` bytes in the name area.
1196 #[cfg(feature = "alloc")]
1197 pub(crate) fn area_name(&self, len: u32) -> &str {
1198 let start = self.name_area();
1199 let bytes = self
1200 .buffer
1201 .as_ref()
1202 .get(start..start.saturating_add(len as usize))
1203 .unwrap_or(&[]);
1204 core::str::from_utf8(bytes).unwrap_or("")
1205 }
1206
1207 /// The decoded `filename*` of the last head, `len` bytes.
1208 #[cfg(feature = "alloc")]
1209 pub(crate) fn area_filename(&self, len: u32) -> &[u8] {
1210 let start = self.filename_area();
1211 self.buffer
1212 .as_ref()
1213 .get(start..start.saturating_add(len as usize))
1214 .unwrap_or(&[])
1215 }
1216
1217 /// Where the current part's data starts in the body.
1218 #[cfg(feature = "alloc")]
1219 pub(crate) const fn data_start(&self) -> u64 {
1220 self.data_start
1221 }
1222
1223 // Buffer layout: header block, name, filename (AcceptAlone), token, charset.
1224
1225 fn name_area(&self) -> usize {
1226 self.limits.max_part_header_bytes as usize
1227 }
1228
1229 fn filename_area(&self) -> usize {
1230 self.name_area().saturating_add(self.limits.name() as usize)
1231 }
1232
1233 fn token_area(&self) -> usize {
1234 let filename = if matches!(
1235 self.policy.extended_filename,
1236 crate::ExtendedFilenamePolicy::AcceptAlone
1237 ) {
1238 self.limits.filename() as usize
1239 } else {
1240 0
1241 };
1242 self.filename_area().saturating_add(filename)
1243 }
1244
1245 fn charset_area(&self) -> usize {
1246 let token = if self.policy.token_field.is_some() {
1247 usize::from(self.limits.token())
1248 } else {
1249 0
1250 };
1251 self.token_area().saturating_add(token)
1252 }
1253
1254 /// The location of the current part's header block.
1255 fn head_at(&self) -> Location {
1256 Location::in_part(self.parts.checked_sub(1), self.head_start)
1257 }
1258
1259 /// The location of the first data byte past a budget of `limit` of
1260 /// which `used` is spent, in the current part.
1261 fn excess(&self, used: u64, limit: u64) -> Location {
1262 let into_part = self.part_bytes.saturating_add(limit.saturating_sub(used));
1263 Location::in_part(
1264 self.parts.checked_sub(1),
1265 self.data_start.saturating_add(into_part),
1266 )
1267 }
1268
1269 fn at(&self, pos: usize) -> Location {
1270 self.at_offset(self.bytes_seen.saturating_add(pos as u64))
1271 }
1272
1273 /// As [`at`](Self::at), for an offset in the whole body.
1274 fn at_offset(&self, offset: u64) -> Location {
1275 let part = if matches!(self.state, State::Preamble | State::Epilogue) {
1276 None
1277 } else {
1278 self.parts.checked_sub(1)
1279 };
1280 Location::in_part(part, offset)
1281 }
1282
1283 /// Runs the state machine and returns positions only, so callers that
1284 /// own the input for longer (the buffered parser) can slice it
1285 /// themselves.
1286 pub(crate) fn advance(
1287 &mut self,
1288 input: &[u8],
1289 eof: bool,
1290 ) -> Result<(usize, Outcome), FormError> {
1291 if self.state == State::Poisoned {
1292 return Err(FormError::Poisoned);
1293 }
1294 match self.run(input, eof) {
1295 Ok((consumed, outcome)) => {
1296 self.bytes_seen = self.bytes_seen.saturating_add(consumed as u64);
1297 Ok((consumed, outcome))
1298 }
1299 Err(e) => {
1300 self.state = State::Poisoned;
1301 Err(e)
1302 }
1303 }
1304 }
1305
1306 /// An input that crosses `max_body_bytes`: off the hot path. It parses
1307 /// what fits, as a split at the limit would, and refuses only when a byte
1308 /// past the limit is needed, so an error inside the limit is reported
1309 /// the same however the body was split (D-080, D-145).
1310 #[cold]
1311 #[inline(never)]
1312 fn run_over_limit(&mut self, input: &[u8]) -> Result<(usize, Outcome), FormError> {
1313 let over = FormError::limit(Limit::Body, Location::at(self.limits.body()));
1314 let room = self.limits.body().saturating_sub(self.bytes_seen);
1315 if room == 0 {
1316 return Err(over);
1317 }
1318 let within = input.get(..room as usize).unwrap_or(&[]);
1319 let (consumed, outcome) = self.run(within, false)?;
1320 // More input is needed, or the body ended with bytes left past the
1321 // limit (epilogue): either way a byte past it is read.
1322 if matches!(outcome, Outcome::NeedInput | Outcome::End) {
1323 return Err(over);
1324 }
1325 Ok((consumed, outcome))
1326 }
1327
1328 fn run(&mut self, input: &[u8], eof: bool) -> Result<(usize, Outcome), FormError> {
1329 // The comparison saturates, so `u64::MAX` is no limit at all.
1330 if self.bytes_seen.saturating_add(input.len() as u64) > self.limits.body() {
1331 return self.run_over_limit(input);
1332 }
1333 let mut pos = 0usize;
1334 loop {
1335 let hay = input.get(pos..).unwrap_or(&[]);
1336 match self.state {
1337 State::Poisoned => return Err(FormError::Poisoned),
1338 State::Preamble => {
1339 if let Some(after) = self.preamble_step(hay)? {
1340 pos = pos.wrapping_add(after);
1341 self.state = State::AfterDelimiter(After::Start);
1342 continue;
1343 }
1344 if eof {
1345 return Err(FormError::syntax(
1346 SyntaxError::NoInitialDelimiter,
1347 self.at(input.len()),
1348 ));
1349 }
1350 return Ok((input.len(), Outcome::NeedInput));
1351 }
1352 State::AfterDelimiter(sub) => {
1353 let Some(&c) = hay.first() else {
1354 return self.need_input(input.len(), eof);
1355 };
1356 pos = pos.wrapping_add(1);
1357 let at = self.at(pos);
1358 self.state = match (sub, c) {
1359 (After::Start, b'-') => State::AfterDelimiter(After::Dash),
1360 (After::Dash, b'-') => {
1361 self.state = State::Epilogue;
1362 self.epilogue_start = self.bytes_seen.saturating_add(pos as u64);
1363 return Ok((pos, Outcome::End));
1364 }
1365 (After::Start, b' ' | b'\t') => {
1366 if self.limits.max_padding_bytes == 0 {
1367 return Err(FormError::limit(Limit::Padding, at));
1368 }
1369 State::AfterDelimiter(After::Padding(1))
1370 }
1371 (After::Padding(n), b' ' | b'\t') => {
1372 if n >= self.limits.max_padding_bytes {
1373 return Err(FormError::limit(Limit::Padding, at));
1374 }
1375 State::AfterDelimiter(After::Padding(n.saturating_add(1)))
1376 }
1377 (After::Start | After::Padding(_), b'\r') => {
1378 State::AfterDelimiter(After::Cr)
1379 }
1380 (After::Cr, b'\n') => {
1381 self.parts = self.parts.saturating_add(1);
1382 if self.parts > self.limits.max_parts {
1383 return Err(FormError::limit(Limit::Parts, self.at(pos)));
1384 }
1385 self.scan = BlockScan::default();
1386 self.copied = 0;
1387 self.head_start = self.bytes_seen.saturating_add(pos as u64);
1388 State::Headers
1389 }
1390 (After::Start | After::Padding(_), b'\n') => {
1391 return Err(FormError::syntax(SyntaxError::BareLineFeed, at));
1392 }
1393 _ => return Err(FormError::syntax(SyntaxError::BoundaryInContent, at)),
1394 };
1395 }
1396 State::Headers => {
1397 let at = self.head_at();
1398 match self.scan.feed(hay, &self.limits, at)? {
1399 Some(end) => {
1400 let source = if self.copied == 0 {
1401 Source::Input(pos)
1402 } else {
1403 self.copy_header(hay.get(..end).unwrap_or(&[]), at)?;
1404 Source::Buffer
1405 };
1406 let block_start_in_input = pos;
1407 pos = pos.wrapping_add(end);
1408 let facts =
1409 self.read_head(input, source, block_start_in_input, end, at)?;
1410 let (is_token, is_charset) = {
1411 let block = self.block(&facts, source, input);
1412 let name = self.name_bytes(&facts, block);
1413 let token = self
1414 .policy
1415 .token_field
1416 .is_some_and(|t| name == t.name().as_bytes());
1417 (token, name == b"_charset_")
1418 };
1419 self.start_part(&facts, is_token, is_charset, at)?;
1420 self.data_start = self.bytes_seen.saturating_add(pos as u64);
1421 return Ok((pos, Outcome::PartStart(facts, source)));
1422 }
1423 None => {
1424 self.copy_header(hay, at)?;
1425 return self.need_input(input.len(), eof);
1426 }
1427 }
1428 }
1429 State::Body => {
1430 if hay.is_empty() {
1431 return self.need_input(input.len(), eof);
1432 }
1433 match self.body_step(hay, pos, input.len(), eof)? {
1434 BodyStep::Emit(consumed, outcome) => return Ok((consumed, outcome)),
1435 BodyStep::Continue(next) => pos = next,
1436 }
1437 }
1438 State::EndOfPart => {
1439 let summary = self.end_part(self.at(pos))?;
1440 self.state = State::AfterDelimiter(After::Start);
1441 return Ok((pos, Outcome::PartEnd(summary)));
1442 }
1443 State::Epilogue => {
1444 self.epilogue = self.epilogue.saturating_add(hay.len() as u64);
1445 if self.epilogue > self.limits.epilogue() {
1446 let at = self.epilogue_start.saturating_add(self.limits.epilogue());
1447 return Err(FormError::limit(Limit::Epilogue, Location::at(at)));
1448 }
1449 return Ok((input.len(), Outcome::End));
1450 }
1451 }
1452 }
1453 }
1454
1455 fn need_input(&self, len: usize, eof: bool) -> Result<(usize, Outcome), FormError> {
1456 if eof {
1457 Err(FormError::syntax(SyntaxError::Truncated, self.at(len)))
1458 } else {
1459 Ok((len, Outcome::NeedInput))
1460 }
1461 }
1462
1463 /// The refusal for `--boundary` after a lone CR or LF, ending at `after`
1464 /// in a `hay` that starts at `pos` in the input (D-113).
1465 /// The location is the line break's, in whichever chunk it came.
1466 fn bare_break(&self, after: usize, pos: usize) -> FormError {
1467 let end = self
1468 .bytes_seen
1469 .saturating_add(pos.wrapping_add(after) as u64);
1470 let bare = self.boundary.len().saturating_sub(1) as u64;
1471 FormError::syntax(
1472 SyntaxError::BoundaryInContent,
1473 self.at_offset(end.saturating_sub(bare)),
1474 )
1475 }
1476
1477 /// Scans the preamble. Returns the index in `hay` just after the first
1478 /// delimiter, or `None` when all of `hay` was consumed.
1479 fn preamble_step(&mut self, hay: &[u8]) -> Result<Option<usize>, FormError> {
1480 let len = self.boundary.len() as u64;
1481 // A delimiter must end within max_preamble_bytes + its own length.
1482 let budget = self.limits.preamble().saturating_add(len);
1483 let room = usize::try_from(budget.saturating_sub(self.preamble)).unwrap_or(usize::MAX);
1484 let window = hay.get(..room.min(hay.len())).unwrap_or(hay);
1485 let (k, m, found) = self
1486 .boundary
1487 .run(usize::from(self.k), usize::from(self.bare), window);
1488 self.k = k as u8;
1489 self.bare = m as u8;
1490 match found {
1491 Hit::BareBreak(after) => Err(self.bare_break(after, 0)),
1492 Hit::Delimiter(after) => {
1493 // Real bytes up to the end of the delimiter; a delimiter that
1494 // includes the two virtual bytes (CRLF) starts the body.
1495 let end = self.preamble.saturating_add(after as u64);
1496 let preamble = end.saturating_sub(len);
1497 if preamble > self.limits.preamble() {
1498 return Err(FormError::limit(
1499 Limit::Preamble,
1500 Location::at(self.limits.preamble()),
1501 ));
1502 }
1503 self.k = 0;
1504 self.bare = 0;
1505 Ok(Some(after))
1506 }
1507 Hit::None => {
1508 self.preamble = self.preamble.saturating_add(window.len() as u64);
1509 if window.len() < hay.len() || self.preamble >= budget {
1510 return Err(FormError::limit(
1511 Limit::Preamble,
1512 Location::at(self.limits.preamble()),
1513 ));
1514 }
1515 Ok(None)
1516 }
1517 }
1518 }
1519
1520 fn copy_header(&mut self, bytes: &[u8], at: Location) -> Result<(), FormError> {
1521 let start = self.copied as usize;
1522 let end = start.saturating_add(bytes.len());
1523 if end > self.limits.max_part_header_bytes as usize {
1524 return Err(FormError::limit(Limit::PartHeaderBytes, at));
1525 }
1526 match self.buffer.as_mut().get_mut(start..end) {
1527 Some(dst) => dst.copy_from_slice(bytes),
1528 None => return Err(FormError::limit(Limit::PartHeaderBytes, at)),
1529 }
1530 self.copied = end as u32;
1531 self.dirty = self.dirty.max(self.copied);
1532 Ok(())
1533 }
1534
1535 fn read_head(
1536 &mut self,
1537 input: &[u8],
1538 source: Source,
1539 start: usize,
1540 end: usize,
1541 at: Location,
1542 ) -> Result<HeadFacts, FormError> {
1543 let name_area = self.name_area();
1544 let filename_area = self.filename_area();
1545 let filename_end = self.token_area();
1546 let limits = self.limits;
1547 let policy = self.policy;
1548 let copied = self.copied as usize;
1549 let block_len = match source {
1550 Source::Input(_) => end,
1551 Source::Buffer => copied,
1552 };
1553 self.dirty = self.dirty.max(u32::try_from(block_len).unwrap_or(u32::MAX));
1554 let buf = self.buffer.as_mut();
1555 let (head, rest) = buf.split_at_mut(name_area.min(buf.len()));
1556 let (name, rest) =
1557 rest.split_at_mut(filename_area.saturating_sub(name_area).min(rest.len()));
1558 let filename = rest
1559 .get_mut(..filename_end.saturating_sub(filename_area))
1560 .unwrap_or(&mut []);
1561 let block = match source {
1562 Source::Input(_) => input.get(start..start.saturating_add(end)).unwrap_or(&[]),
1563 Source::Buffer => head.get(..copied).unwrap_or(&[]),
1564 };
1565 read_block(block, &limits, &policy, Areas { name, filename }, at)
1566 }
1567
1568 /// The budgets and rules that apply when a part starts.
1569 fn start_part(
1570 &mut self,
1571 facts: &HeadFacts,
1572 is_token: bool,
1573 is_charset: bool,
1574 at: Location,
1575 ) -> Result<(), FormError> {
1576 self.state = State::Body;
1577 self.k = 0;
1578 // The data starts after the blank line's LF: `--boundary` right
1579 // there reads as an empty part to an RFC 2046 reader (D-113).
1580 self.bare = 1;
1581 self.flags = 0;
1582 self.kind = facts.kind;
1583 self.part_bytes = 0;
1584 match facts.kind {
1585 PartKind::Field => {
1586 self.fields = self.fields.saturating_add(1);
1587 if self.fields > self.limits.max_fields {
1588 return Err(FormError::limit(Limit::Fields, at));
1589 }
1590 if is_token {
1591 if self.token != Token::NotSeen {
1592 return Err(FormError::policy(PolicyRefusal::DuplicateToken, at));
1593 }
1594 self.token = Token::Capturing;
1595 self.token_len = 0;
1596 self.flags |= IS_TOKEN;
1597 }
1598 if is_charset {
1599 self.flags |= IS_CHARSET;
1600 self.charset_len = 0;
1601 self.charset_done = false;
1602 }
1603 }
1604 PartKind::File => {
1605 let empty = matches!(facts.filename, Some((Text::Block(span), _)) if span.len == 0)
1606 || matches!(facts.filename, Some((Text::Area(0), _)));
1607 if empty && self.policy.empty_files == EmptyFilePolicy::Skip {
1608 self.flags |= DEFERRED;
1609 } else {
1610 self.count_file(at)?;
1611 }
1612 }
1613 }
1614 Ok(())
1615 }
1616
1617 /// The block of a head, wherever it is.
1618 fn block<'a>(&'a self, facts: &HeadFacts, source: Source, input: &'a [u8]) -> &'a [u8] {
1619 let len = facts.block_len as usize;
1620 match source {
1621 Source::Input(start) => input.get(start..start.saturating_add(len)).unwrap_or(&[]),
1622 Source::Buffer => self.buffer.as_ref().get(..len).unwrap_or(&[]),
1623 }
1624 }
1625
1626 /// The decoded name of a head.
1627 fn name_bytes<'a>(&'a self, facts: &HeadFacts, block: &'a [u8]) -> &'a [u8] {
1628 match facts.name {
1629 Text::Block(span) => span.get(block),
1630 Text::Area(len) => {
1631 let start = self.name_area();
1632 self.buffer
1633 .as_ref()
1634 .get(start..start.saturating_add(len as usize))
1635 .unwrap_or(&[])
1636 }
1637 }
1638 }
1639
1640 /// The borrowed view of a head.
1641 fn head_view<'p>(&'p self, facts: &HeadFacts, source: Source, input: &'p [u8]) -> PartHead<'p> {
1642 let block = self.block(facts, source, input);
1643 let name = core::str::from_utf8(self.name_bytes(facts, block)).unwrap_or("");
1644 let filename = facts.filename.map(|(text, origin)| {
1645 let raw = match text {
1646 Text::Block(span) => span.get(block),
1647 Text::Area(len) => {
1648 let start = self.filename_area();
1649 self.buffer
1650 .as_ref()
1651 .get(start..start.saturating_add(len as usize))
1652 .unwrap_or(&[])
1653 }
1654 };
1655 FileName::new(raw, origin)
1656 });
1657 PartHead {
1658 index: self.parts.saturating_sub(1),
1659 block,
1660 name,
1661 name_raw: facts.name_raw.get(block),
1662 kind: facts.kind,
1663 filename,
1664 content_type: facts.content_type.map(|span| span.get(block)),
1665 charset: facts.charset,
1666 }
1667 }
1668
1669 /// Turns positions into borrowed events.
1670 fn view<'p>(&'p self, outcome: Outcome, input: &'p [u8]) -> MultipartEvent<'p> {
1671 match outcome {
1672 Outcome::PartStart(facts, source) => {
1673 MultipartEvent::PartStart(self.head_view(&facts, source, input))
1674 }
1675 Outcome::DataInput(start, end) => {
1676 MultipartEvent::Data(input.get(start..end).unwrap_or(&[]))
1677 }
1678 Outcome::DataDelimiter(n) => {
1679 MultipartEvent::Data(self.boundary.delimiter().get(..n).unwrap_or(&[]))
1680 }
1681 Outcome::PartEnd(summary) => MultipartEvent::PartEnd(summary),
1682 Outcome::End => MultipartEvent::End,
1683 Outcome::NeedInput => MultipartEvent::NeedInput,
1684 }
1685 }
1686
1687 fn count_file(&mut self, at: Location) -> Result<(), FormError> {
1688 self.files = self.files.saturating_add(1);
1689 if self.files > self.limits.max_files {
1690 return Err(FormError::limit(Limit::Files, at));
1691 }
1692 if let Some(t) = self.policy.token_field {
1693 if t.placement() == TokenPlacement::BeforeFirstFile && self.token != Token::Captured {
1694 return Err(FormError::policy(PolicyRefusal::TokenFieldLate, at));
1695 }
1696 }
1697 Ok(())
1698 }
1699
1700 /// Checks budgets for `n` more data bytes of the current part, and
1701 /// captures the token or `_charset_` value.
1702 fn take_data(&mut self, bytes: &[u8]) -> Result<(), FormError> {
1703 let n = bytes.len() as u64;
1704 if n == 0 {
1705 return Ok(());
1706 }
1707 if self.flags & DEFERRED != 0 {
1708 self.flags &= !DEFERRED;
1709 let at = Location::in_part(self.parts.checked_sub(1), self.data_start);
1710 self.count_file(at)?;
1711 }
1712 let total = self.part_bytes.saturating_add(n);
1713 match self.kind {
1714 PartKind::File => {
1715 let limit = self.limits.file();
1716 if total > limit {
1717 let at = self.excess(self.part_bytes, limit);
1718 return Err(FormError::limit(Limit::FileSize, at));
1719 }
1720 }
1721 PartKind::Field => {
1722 let limit = u64::from(self.limits.value());
1723 if total > limit {
1724 let at = self.excess(self.part_bytes, limit);
1725 return Err(FormError::limit(Limit::ValueLength, at));
1726 }
1727 let text = self.text_total.saturating_add(n);
1728 let limit = self.limits.text();
1729 if text > limit {
1730 let at = self.excess(self.text_total, limit);
1731 return Err(FormError::limit(Limit::TextTotal, at));
1732 }
1733 self.text_total = text;
1734 }
1735 }
1736 if self.flags & IS_TOKEN != 0 {
1737 let start = self
1738 .token_area()
1739 .saturating_add(usize::from(self.token_len));
1740 let end = start.saturating_add(bytes.len());
1741 let limit = u64::from(self.limits.token());
1742 let at = self.excess(u64::from(self.token_len), limit);
1743 if end
1744 > self
1745 .token_area()
1746 .saturating_add(usize::from(self.limits.token()))
1747 {
1748 return Err(FormError::limit(Limit::TokenLength, at));
1749 }
1750 match self.buffer.as_mut().get_mut(start..end) {
1751 Some(dst) => dst.copy_from_slice(bytes),
1752 None => return Err(FormError::limit(Limit::TokenLength, at)),
1753 }
1754 self.token_len = self.token_len.saturating_add(bytes.len() as u16);
1755 }
1756 if self.flags & IS_CHARSET != 0 {
1757 let start = self
1758 .charset_area()
1759 .saturating_add(usize::from(self.charset_len));
1760 let end = start.saturating_add(bytes.len());
1761 let at = self.excess(u64::from(self.charset_len), CHARSET_CAPTURE as u64);
1762 if end > self.charset_area().saturating_add(CHARSET_CAPTURE) {
1763 return Err(FormError::encoding(EncodingError::UnsupportedCharset, at));
1764 }
1765 match self.buffer.as_mut().get_mut(start..end) {
1766 Some(dst) => dst.copy_from_slice(bytes),
1767 None => return Err(FormError::encoding(EncodingError::UnsupportedCharset, at)),
1768 }
1769 self.charset_len = self.charset_len.saturating_add(bytes.len() as u8);
1770 }
1771 self.part_bytes = total;
1772 Ok(())
1773 }
1774
1775 fn end_part(&mut self, at: Location) -> Result<PartSummary, FormError> {
1776 if self.flags & IS_TOKEN != 0 {
1777 let start = self.token_area();
1778 let bytes = self
1779 .buffer
1780 .as_ref()
1781 .get(start..start.saturating_add(usize::from(self.token_len)))
1782 .unwrap_or(&[]);
1783 if core::str::from_utf8(bytes).is_err() {
1784 return Err(FormError::encoding(EncodingError::InvalidUtf8, at));
1785 }
1786 self.token = Token::Captured;
1787 }
1788 if self.flags & IS_CHARSET != 0 {
1789 self.charset_done = true;
1790 }
1791 let summary = PartSummary {
1792 index: self.parts.saturating_sub(1),
1793 kind: self.kind,
1794 bytes: self.part_bytes,
1795 skipped: self.flags & SKIPPING != 0,
1796 };
1797 self.flags = 0;
1798 self.k = 0;
1799 Ok(summary)
1800 }
1801
1802 /// One step in a part's data. `pos` is where `hay` starts in the input.
1803 fn body_step(
1804 &mut self,
1805 hay: &[u8],
1806 pos: usize,
1807 input_len: usize,
1808 eof: bool,
1809 ) -> Result<BodyStep, FormError> {
1810 let len = self.boundary.len();
1811 let k0 = usize::from(self.k);
1812 if k0 > 0 {
1813 // Bytes from earlier chunks that matched the start of the
1814 // delimiter: resolve them before anything else. The delimiter
1815 // has one CR, so the match either goes on or all of it is over.
1816 let need = self.boundary.delimiter().get(k0..).unwrap_or(&[]);
1817 let (need_len, n) = (need.len(), super::boundary::shared(need, hay));
1818 if n == need_len {
1819 // The delimiter started inside the held bytes.
1820 self.k = 0;
1821 self.bare = 0;
1822 self.state = State::EndOfPart;
1823 return self.emit_delimiter_bytes(0, pos.wrapping_add(n));
1824 }
1825 if n < hay.len() {
1826 // Every held byte is data; scan the input again from 0,
1827 // with the bare-break state the held bytes leave: they
1828 // are a CR, a CR LF, or a CR LF and dashes (D-113).
1829 self.k = 0;
1830 self.bare = k0.max(2).wrapping_sub(1) as u8;
1831 return self.emit_delimiter_bytes(k0, pos);
1832 }
1833 // All of `hay` extended the partial match.
1834 if eof {
1835 // Every byte is data now: charge it first, as a split body
1836 // charges it before it learns the body ended (D-145).
1837 let boundary = self.boundary;
1838 self.take_data(boundary.delimiter().get(..k0).unwrap_or(&[]))?;
1839 self.take_data(hay)?;
1840 return Err(FormError::syntax(
1841 SyntaxError::Truncated,
1842 self.at(input_len),
1843 ));
1844 }
1845 self.k = k0.saturating_add(n) as u8;
1846 return self.emit_delimiter_bytes(0, input_len);
1847 }
1848 let (k, m, found) = self.boundary.run(0, usize::from(self.bare), hay);
1849 match found {
1850 Hit::BareBreak(after) => {
1851 // The data before the break is charged first, as a split
1852 // before the break would charge it (D-145).
1853 let start = after.saturating_sub(len.saturating_sub(1));
1854 self.take_data(hay.get(..start).unwrap_or(&[]))?;
1855 Err(self.bare_break(after, pos))
1856 }
1857 Hit::Delimiter(after) => {
1858 let data = after.saturating_sub(len);
1859 self.k = 0;
1860 self.bare = 0;
1861 self.state = State::EndOfPart;
1862 let bytes = hay.get(..data).unwrap_or(&[]);
1863 self.emit_input_bytes(bytes, pos, pos.wrapping_add(data), pos.wrapping_add(after))
1864 }
1865 Hit::None => {
1866 if eof {
1867 // As above: the rest is data, charged before `Truncated`.
1868 self.take_data(hay)?;
1869 return Err(FormError::syntax(
1870 SyntaxError::Truncated,
1871 self.at(input_len),
1872 ));
1873 }
1874 self.k = k as u8;
1875 self.bare = m as u8;
1876 let data_end = hay.len().saturating_sub(k);
1877 let bytes = hay.get(..data_end).unwrap_or(&[]);
1878 self.emit_input_bytes(bytes, pos, pos.wrapping_add(data_end), input_len)
1879 }
1880 }
1881 }
1882
1883 /// Emits `delimiter[..n]` as data (or skips it), consuming up to
1884 /// `consumed`.
1885 fn emit_delimiter_bytes(&mut self, n: usize, consumed: usize) -> Result<BodyStep, FormError> {
1886 let boundary = self.boundary;
1887 let bytes = boundary.delimiter().get(..n).unwrap_or(&[]);
1888 self.take_data(bytes)?;
1889 if n == 0 || self.flags & SKIPPING != 0 {
1890 return Ok(BodyStep::Continue(consumed));
1891 }
1892 Ok(BodyStep::Emit(consumed, Outcome::DataDelimiter(n)))
1893 }
1894
1895 /// Emits `input[start..end]` as data (or skips it), consuming up to
1896 /// `consumed`.
1897 fn emit_input_bytes(
1898 &mut self,
1899 bytes: &[u8],
1900 start: usize,
1901 end: usize,
1902 consumed: usize,
1903 ) -> Result<BodyStep, FormError> {
1904 self.take_data(bytes)?;
1905 if start == end || self.flags & SKIPPING != 0 {
1906 return Ok(BodyStep::Continue(consumed));
1907 }
1908 Ok(BodyStep::Emit(consumed, Outcome::DataInput(start, end)))
1909 }
1910}
1911
1912/// What a body step did.
1913enum BodyStep {
1914 Emit(usize, Outcome),
1915 Continue(usize),
1916}
1917
1918impl<B: AsMut<[u8]> + AsRef<[u8]>> MultipartParser<B> {
1919 /// Wipes every byte the parser wrote into its buffer, and nothing more:
1920 /// a buffer of the default size is 9.6 KiB, and a login form writes a
1921 /// few hundred bytes of it (D-103).
1922 fn wipe_used(&mut self) {
1923 let used = self.dirty as usize;
1924 let regions = [
1925 (0, used.min(self.name_area())),
1926 (
1927 self.name_area(),
1928 used.min(self.filename_area().saturating_sub(self.name_area())),
1929 ),
1930 (
1931 self.filename_area(),
1932 used.min(self.token_area().saturating_sub(self.filename_area())),
1933 ),
1934 (self.token_area(), usize::from(self.token_len)),
1935 // The whole capture area: a second, shorter `_charset_` value
1936 // resets the length over the tail of the first (found by review).
1937 (self.charset_area(), CHARSET_CAPTURE),
1938 ];
1939 let buf = self.buffer.as_mut();
1940 for (start, len) in regions {
1941 if let Some(region) = buf.get_mut(start..start.saturating_add(len)) {
1942 crate::wipe::wipe(region);
1943 }
1944 }
1945 self.dirty = 0;
1946 }
1947}
1948
1949impl<B: AsMut<[u8]> + AsRef<[u8]>> Drop for MultipartParser<B> {
1950 fn drop(&mut self) {
1951 self.wipe_used();
1952 }
1953}
1954
1955#[cfg(test)]
1956mod tests {
1957 use super::*;
1958
1959 /// D-103: the wipe covers every byte the parser wrote. With a caller
1960 /// buffer the result is visible: after a body that writes every area
1961 /// (header blocks, an escaped name, a decoded `filename*`, the token,
1962 /// `_charset_`), dropping or resetting leaves the buffer all zeros.
1963 #[test]
1964 fn test_wipe_covers_every_area_written() {
1965 let policy = Policy::DEFAULT
1966 .with_extended_filename(crate::ExtendedFilenamePolicy::AcceptAlone)
1967 .with_token_field(crate::TokenField::new("csrf"));
1968 let limits = Limits::DEFAULT.with_max_part_header_bytes(512);
1969 let long = "x".repeat(300);
1970 let body = alloc::format!(
1971 "--X\r\nContent-Disposition: form-data; name=csrf\r\n\r\nsecret-token-value\r\n\
1972 --X\r\nContent-Disposition: form-data; name=_charset_\r\n\r\nUTF-8\r\n\
1973 --X\r\nContent-Disposition: form-data; name=\"pass%22word\"\r\nX-Pad: {long}\r\n\r\nhunter2\r\n\
1974 --X\r\nContent-Disposition: form-data; name=f; filename*=UTF-8''caf%C3%A9.txt\r\n\r\nfile\r\n--X--"
1975 );
1976 // Two `_charset_` values, the second shorter: the first one's tail
1977 // must not survive (found by review).
1978 let body = body.replace(
1979 "name=_charset_\r\n\r\nUTF-8\r\n",
1980 "name=_charset_\r\n\r\nunicode-1-1-utf-8\r\n--X\r\nContent-Disposition: form-data; name=_charset_\r\n\r\nutf8\r\n",
1981 );
1982 assert!(body.contains("unicode-1-1-utf-8"));
1983 for split in [usize::MAX, 7, 1] {
1984 let mut buf = alloc::vec![0u8; limits.multipart_buffer_len(&policy)];
1985 {
1986 let mut p = MultipartParser::with_buffer(
1987 Boundary::new(b"X").unwrap(),
1988 &limits,
1989 &policy,
1990 &mut buf[..],
1991 )
1992 .unwrap();
1993 let bytes = body.as_bytes();
1994 let chunks: alloc::vec::Vec<&[u8]> = bytes.chunks(split.min(bytes.len())).collect();
1995 let mut ended = false;
1996 for (i, c) in chunks.iter().enumerate() {
1997 let last = i + 1 == chunks.len();
1998 let mut rest = *c;
1999 loop {
2000 let step = p.parse(rest, last).unwrap();
2001 rest = &rest[step.consumed..];
2002 match step.event {
2003 MultipartEvent::NeedInput => break,
2004 MultipartEvent::End => {
2005 ended = true;
2006 break;
2007 }
2008 _ => {}
2009 }
2010 }
2011 }
2012 assert!(ended);
2013 assert_eq!(p.captured_token(), Some("secret-token-value"));
2014 }
2015 assert!(
2016 buf.iter().all(|&b| b == 0),
2017 "split {split}: bytes left at {:?}",
2018 buf.iter().position(|&b| b != 0)
2019 );
2020 }
2021 }
2022
2023 /// Feeds `body` whole and returns the first error, or `None` at `End`.
2024 #[cfg(feature = "alloc")]
2025 fn run(p: &mut MultipartParser<alloc::vec::Vec<u8>>, body: &[u8]) -> Option<FormError> {
2026 let mut rest = body;
2027 loop {
2028 match p.parse(rest, true) {
2029 Err(e) => return Some(e),
2030 Ok(step) => {
2031 rest = &rest[step.consumed..];
2032 if matches!(step.event, MultipartEvent::End) {
2033 return None;
2034 }
2035 }
2036 }
2037 }
2038 }
2039
2040 /// T-28, through the counter seam: counters set near `u64::MAX` never
2041 /// wrap. Under finite limits they still refuse; under `u64::MAX` limits
2042 /// they saturate and positions stay at the maximum.
2043 #[cfg(feature = "alloc")]
2044 #[test]
2045 fn test_attack_t28_counters_near_u64_max_never_wrap() {
2046 let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\nvalue\r\n--X--";
2047 let new = |limits: &Limits| {
2048 MultipartParser::new(Boundary::new(b"X").unwrap(), limits, &Policy::DEFAULT).unwrap()
2049 };
2050 let mut p = new(&Limits::DEFAULT);
2051 p.bytes_seen = u64::MAX - 4;
2052 assert!(matches!(
2053 run(&mut p, body),
2054 Some(FormError::Limit {
2055 limit: crate::error::Limit::Body,
2056 ..
2057 })
2058 ));
2059
2060 let mut p = new(&Limits::DEFAULT);
2061 p.text_total = u64::MAX - 2;
2062 assert!(matches!(
2063 run(&mut p, body),
2064 Some(FormError::Limit {
2065 limit: crate::error::Limit::TextTotal,
2066 ..
2067 })
2068 ));
2069
2070 let open = Limits::DEFAULT
2071 .with_max_body_bytes(u64::MAX)
2072 .with_max_text_bytes(u64::MAX);
2073 let mut p = new(&open);
2074 p.bytes_seen = u64::MAX - 4;
2075 assert!(run(&mut p, body).is_none());
2076 assert_eq!(p.bytes_seen, u64::MAX);
2077 }
2078
2079 #[test]
2080 fn test_parser_size_is_bounded() {
2081 // NFR-18 as measured: the boundary and its matcher table are 149 of
2082 // these bytes; the buffer is separate.
2083 assert!(core::mem::size_of::<MultipartParser<&mut [u8]>>() <= 384);
2084 assert!(core::mem::size_of::<Boundary>() <= 150);
2085 }
2086
2087 #[cfg(feature = "alloc")]
2088 #[test]
2089 fn test_debug_never_shows_data_or_file_names() {
2090 let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=\"secret.pdf\"\r\n\r\nhunter2\r\n--X--";
2091 let mut p = MultipartParser::new(
2092 Boundary::new(b"X").unwrap(),
2093 &Limits::DEFAULT,
2094 &Policy::DEFAULT,
2095 )
2096 .unwrap();
2097 let mut rest: &[u8] = body;
2098 let mut seen = alloc::string::String::new();
2099 loop {
2100 let step = p.parse(rest, true).unwrap();
2101 rest = &rest[step.consumed..];
2102 if let MultipartEvent::PartStart(head) = &step.event {
2103 seen.push_str(&alloc::format!("{:?}", head.headers()));
2104 }
2105 seen.push_str(&alloc::format!("{step:?}"));
2106 if matches!(step.event, MultipartEvent::End) {
2107 break;
2108 }
2109 }
2110 assert!(seen.contains("Data(7 bytes)"), "{seen}");
2111 assert!(
2112 !seen.contains("hunter2") && !seen.contains("secret"),
2113 "{seen}"
2114 );
2115 }
2116
2117 #[test]
2118 fn test_charset_labels() {
2119 assert_eq!(Charset::from_label(b"UTF-8"), Charset::Utf8);
2120 assert_eq!(Charset::from_label(b"us-ascii"), Charset::Ascii);
2121 assert_eq!(Charset::from_label(b"utf8"), Charset::Other);
2122 }
2123}