Skip to main content

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