Skip to main content

form_net/multipart/
owned_head.rs

1//! An owned copy of a part head, for code that needs the head after the
2//! `parse` call that produced it.
3
4use alloc::vec::Vec;
5use core::fmt;
6use core::ops::Range;
7
8use crate::multipart::filename::{FileName, FileNameSource};
9use crate::multipart::parser::{Charset, PartHead, PartKind};
10
11/// An owned copy of a [`PartHead`]. Its buffer is reused for the next head
12/// copied into it, so a driver that keeps one never allocates per part once
13/// the buffer has grown to the largest head.
14///
15/// # Examples
16///
17/// ```
18/// use form_net::{Boundary, Event, Limits, MultipartParser, OwnedPartHead, Policy};
19///
20/// let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
21/// let body = b"--X\r\nContent-Disposition: form-data; name=\"a\"\r\n\r\n1\r\n--X--";
22/// let mut kept = OwnedPartHead::new();
23/// let step = parser.parse(body, true)?;
24/// if let Event::PartStart(head) = step.event {
25///     kept.copy_from(&head);
26/// }
27/// assert_eq!(kept.view().name(), "a");
28/// # Ok::<(), form_net::FormError>(())
29/// ```
30#[derive(Clone, Default)]
31pub struct OwnedPartHead {
32    buf: Vec<u8>,
33    index: u32,
34    block: Range<usize>,
35    name: Range<usize>,
36    name_raw: Range<usize>,
37    kind: Option<PartKind>,
38    filename: Option<(Range<usize>, FileNameSource)>,
39    content_type: Option<Range<usize>>,
40    charset: Option<Charset>,
41}
42
43impl fmt::Debug for OwnedPartHead {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        fmt::Debug::fmt(&self.view(), f)
46    }
47}
48
49impl OwnedPartHead {
50    /// An empty head; [`view`](Self::view) of it is a field with no name.
51    #[must_use]
52    pub fn new() -> OwnedPartHead {
53        OwnedPartHead::default()
54    }
55
56    /// An empty head whose buffer already holds `capacity` bytes.
57    #[must_use]
58    pub fn with_capacity(capacity: usize) -> OwnedPartHead {
59        let mut head = OwnedPartHead::default();
60        head.buf = Vec::with_capacity(capacity);
61        head
62    }
63
64    fn push(&mut self, bytes: &[u8]) -> Range<usize> {
65        let start = self.buf.len();
66        // The capacity was reserved in `copy_from`, so this never moves.
67        crate::wipe::append_wiping(&mut self.buf, bytes, bytes.len(), usize::MAX);
68        start..self.buf.len()
69    }
70
71    /// Replaces this copy with `head`. The old bytes are wiped first, and a
72    /// buffer too small for the new head is wiped and freed before its
73    /// replacement is allocated, so no copy of a head is freed unwiped and
74    /// two buffers are never held at once. The header block is copied once:
75    /// the name, file name, and content type that lie inside it are kept as
76    /// positions in that copy (D-140).
77    pub fn copy_from(&mut self, head: &PartHead<'_>) {
78        crate::wipe::wipe(&mut self.buf);
79        self.buf.clear();
80        let block = head.block();
81        let outside = |s: &[u8]| {
82            if within(block, s).is_some() {
83                0
84            } else {
85                s.len()
86            }
87        };
88        let needed = [
89            block.len(),
90            outside(head.name().as_bytes()),
91            outside(head.name_raw()),
92            head.filename().map_or(0, |f| outside(f.raw())),
93            head.content_type().map_or(0, |c| outside(c.raw())),
94        ]
95        .iter()
96        .fold(0usize, |a, n| a.saturating_add(*n));
97        if needed > self.buf.capacity() {
98            let mut old = core::mem::take(&mut self.buf);
99            crate::wipe::wipe_vec(&mut old);
100            drop(old);
101            self.buf = Vec::with_capacity(needed);
102        }
103        self.index = head.index();
104        self.block = self.push(block);
105        let base = self.block.start;
106        let mut place = |s: &[u8]| match within(block, s) {
107            Some(r) => r.start.saturating_add(base)..r.end.saturating_add(base),
108            None => {
109                // As `push`, on the buffer alone, so the fields stay free.
110                let start = self.buf.len();
111                crate::wipe::append_wiping(&mut self.buf, s, s.len(), usize::MAX);
112                start..self.buf.len()
113            }
114        };
115        self.name = place(head.name().as_bytes());
116        self.name_raw = place(head.name_raw());
117        self.kind = Some(head.kind());
118        self.filename = head.filename().map(|f| (place(f.raw()), f.source()));
119        self.content_type = head.content_type().map(|c| place(c.raw()));
120        self.charset = Some(head.charset());
121    }
122
123    /// The usual borrowed view.
124    #[must_use]
125    pub fn view(&self) -> PartHead<'_> {
126        let get = |r: &Range<usize>| self.buf.get(r.clone()).unwrap_or(&[]);
127        PartHead::from_parts(
128            self.index,
129            get(&self.block),
130            core::str::from_utf8(get(&self.name)).unwrap_or(""),
131            get(&self.name_raw),
132            self.kind.unwrap_or(PartKind::Field),
133            self.filename
134                .as_ref()
135                .map(|(r, source)| FileName::new(get(r), *source)),
136            self.content_type.as_ref().map(get),
137            self.charset.unwrap_or(Charset::Unspecified),
138        )
139    }
140}
141
142impl Drop for OwnedPartHead {
143    fn drop(&mut self) {
144        // Only the length can hold head bytes: `copy_from` wipes the old
145        // bytes before every reuse and moves to a fresh buffer (wiping the
146        // old one) when it must grow, so the spare capacity, 9.6 KiB under
147        // the default limits, was never written (D-103).
148        crate::wipe::wipe(&mut self.buf);
149    }
150}
151
152/// Where `part` lies inside `whole`, when it is a subslice of it: compared
153/// by address, so equal bytes elsewhere do not count.
154fn within(whole: &[u8], part: &[u8]) -> Option<Range<usize>> {
155    let start = (part.as_ptr() as usize).checked_sub(whole.as_ptr() as usize)?;
156    let end = start.checked_add(part.len())?;
157    (end <= whole.len()).then_some(start..end)
158}