Skip to main content

form_net/extract/
mod.rs

1//! Typed extraction: plain Rust structs filled from a form, without serde
2//! and without a procedural macro.
3//!
4//! [`form!`](crate::form) generates a struct and its [`FromForm`]
5//! implementation; each field type implements [`FromField`]. Unknown names
6//! are refused, a scalar given twice is refused, a missing required field is
7//! refused, and every error names the field by its Rust-side name.
8
9mod file;
10mod macros;
11mod secret;
12mod value;
13
14pub use file::FileData;
15pub use secret::Secret;
16
17use crate::error::{ExtractError, FormError};
18use crate::hook::{Inspector, NoInspector};
19use crate::kind::FormKind;
20use crate::limits::{Limits, Policy};
21use crate::multipart::Part;
22
23/// Why one field's value did not convert. [`form!`](crate::form) turns it
24/// into `FormError::Extract` with the field's name.
25///
26/// # Examples
27///
28/// ```
29/// use form_net::{FromField, ValueError};
30///
31/// let mut slot = <u8 as FromField>::Slot::default();
32/// assert_eq!(u8::accept_text(&mut slot, "300"), Err(ValueError::Invalid("out of range")));
33/// ```
34#[non_exhaustive]
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub enum ValueError {
37    /// A required field is absent.
38    Missing,
39    /// A single-valued field was given twice.
40    Duplicate,
41    /// The value does not convert; the text says why.
42    Invalid(&'static str),
43    /// Text was given to a file field.
44    ExpectedFile,
45    /// A file was given to a text field.
46    ExpectedText,
47}
48
49/// A file field's value.
50#[non_exhaustive]
51pub enum FileValue<'a> {
52    /// A part of a buffered body.
53    Buffered(&'a Part<'a>),
54    /// A stored file from an upload.
55    #[cfg(feature = "fs")]
56    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
57    Stored(crate::store::StoredFile),
58}
59
60impl core::fmt::Debug for FileValue<'_> {
61    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
62        match self {
63            FileValue::Buffered(p) => f.debug_tuple("Buffered").field(p).finish(),
64            #[cfg(feature = "fs")]
65            FileValue::Stored(s) => f.debug_tuple("Stored").field(s).finish(),
66        }
67    }
68}
69
70/// How one field type takes its values.
71///
72/// The slot holds what has arrived so far, so every arity rule lives in one
73/// place: a scalar's slot is `Option<T>` (a second value is `Duplicate`,
74/// none is `Missing`), `Vec<T>` pushes, `bool` finishes a missing value as
75/// `false`. Implement it for an application type to validate while
76/// extracting.
77///
78/// # Examples
79///
80/// ```
81/// use form_net::{FromField, ValueError};
82///
83/// pub struct Slug(String);
84///
85/// impl FromField for Slug {
86///     type Slot = Option<Slug>;
87///     fn accept_text(slot: &mut Option<Slug>, value: &str) -> Result<(), ValueError> {
88///         if slot.is_some() { return Err(ValueError::Duplicate); }
89///         if value.is_empty() || value.len() > 64 || !value.bytes().all(|b| b.is_ascii_lowercase() || b == b'-') {
90///             return Err(ValueError::Invalid("lowercase letters and hyphens, 1 to 64"));
91///         }
92///         *slot = Some(Slug(value.to_owned()));
93///         Ok(())
94///     }
95///     fn finish(slot: Option<Slug>) -> Result<Slug, ValueError> {
96///         slot.ok_or(ValueError::Missing)
97///     }
98/// }
99/// ```
100pub trait FromField: Sized {
101    /// What has arrived so far.
102    type Slot: Default;
103
104    /// Takes one text value.
105    ///
106    /// # Errors
107    ///
108    /// `ExpectedFile` by default; the type's own conversion errors.
109    fn accept_text(slot: &mut Self::Slot, value: &str) -> Result<(), ValueError> {
110        let _ = (slot, value);
111        Err(ValueError::ExpectedFile)
112    }
113
114    /// Takes one file.
115    ///
116    /// # Errors
117    ///
118    /// `ExpectedText` by default; the type's own errors.
119    fn accept_file(slot: &mut Self::Slot, file: FileValue<'_>) -> Result<(), ValueError> {
120        let _ = (slot, file);
121        Err(ValueError::ExpectedText)
122    }
123
124    /// The value, once every field has arrived.
125    ///
126    /// # Errors
127    ///
128    /// `Missing`, or the type's own errors.
129    fn finish(slot: Self::Slot) -> Result<Self, ValueError>;
130}
131
132/// What [`form!`](crate::form) generates, and what a hand-written
133/// [`FromForm`] implementation provides. Reports problems as
134/// `FormError::Extract { reason, field }`.
135///
136/// # Examples
137///
138/// ```
139/// use form_net::{ExtractError, FileValue, FormBuilder, FormError, FromForm};
140///
141/// /// Accepts any field names, keeping the last `q`.
142/// #[derive(Default)]
143/// struct LastQ(Option<String>);
144///
145/// impl FormBuilder for LastQ {
146///     type Output = Query;
147///     fn text(&mut self, name: &str, value: &str) -> Result<(), FormError> {
148///         if name == "q" { self.0 = Some(value.to_owned()); }
149///         Ok(())
150///     }
151///     fn file(&mut self, _name: &str, _file: FileValue<'_>) -> Result<(), FormError> {
152///         Err(FormError::Extract { reason: ExtractError::WrongKind, field: Some("q") })
153///     }
154///     fn finish(self) -> Result<Query, FormError> {
155///         Ok(Query { q: self.0.unwrap_or_default() })
156///     }
157/// }
158///
159/// struct Query { q: String }
160/// impl FromForm for Query { type Builder = LastQ; }
161///
162/// assert_eq!(Query::from_query(b"q=a&q=b")?.q, "b");
163/// # Ok::<(), FormError>(())
164/// ```
165pub trait FormBuilder {
166    /// The struct being built.
167    type Output;
168
169    /// One text field.
170    ///
171    /// # Errors
172    ///
173    /// `Extract { .. }`.
174    fn text(&mut self, name: &str, value: &str) -> Result<(), FormError>;
175
176    /// One file.
177    ///
178    /// # Errors
179    ///
180    /// `Extract { .. }`.
181    fn file(&mut self, name: &str, file: FileValue<'_>) -> Result<(), FormError>;
182
183    /// The struct, once every field has arrived.
184    ///
185    /// # Errors
186    ///
187    /// `Extract { Missing | Invalid, .. }`.
188    fn finish(self) -> Result<Self::Output, FormError>;
189}
190
191/// The bytes the decoders use without allocating.
192const STACK: usize = 512;
193
194/// Feeds a urlencoded body to `builder`, decoding each name and value into a
195/// stack buffer when it fits and a heap buffer otherwise.
196fn feed_urlencoded<B: FormBuilder, I: Inspector>(
197    builder: &mut B,
198    input: &[u8],
199    limits: &Limits,
200    policy: &Policy,
201    inspector: &mut I,
202) -> Result<(), FormError> {
203    let mut name_stack = [0u8; STACK];
204    let mut value_stack = [0u8; STACK];
205    let mut heap = alloc::vec::Vec::new();
206    let fed = (|| {
207        for pair in crate::urlencoded::raw::raw_pairs(input, limits) {
208            let pair = pair?;
209            let name_len = pair.name_raw().len();
210            let value_len = pair.value_raw().len();
211            let (name_buf, value_buf): (&mut [u8], &mut [u8]) =
212                if name_len <= STACK && value_len <= STACK {
213                    (&mut name_stack, &mut value_stack)
214                } else {
215                    let needed = name_len.saturating_add(value_len);
216                    crate::wipe::wipe(&mut heap);
217                    heap.clear();
218                    if needed > heap.capacity() {
219                        // A new allocation; the old one is wiped, not freed
220                        // with a decoded value in it.
221                        let mut old =
222                            core::mem::replace(&mut heap, alloc::vec::Vec::with_capacity(needed));
223                        crate::wipe::wipe_vec(&mut old);
224                    }
225                    heap.resize(needed, 0);
226                    heap.split_at_mut(name_len)
227                };
228            let name = pair.decode_name(name_buf, policy.percent)?;
229            let value = pair.decode_value(value_buf, policy.percent)?;
230            let at = crate::error::Location::at(pair.offset());
231            match inspector.field(name, value.as_bytes()) {
232                crate::Verdict::Refuse(refusal) => return Err(FormError::Refused { refusal, at }),
233                crate::Verdict::Skip => {}
234                _ => builder.text(name, value)?,
235            }
236        }
237        Ok(())
238    })();
239    crate::wipe::wipe(&mut name_stack);
240    crate::wipe::wipe(&mut value_stack);
241    crate::wipe::wipe_vec(&mut heap);
242    fed?;
243    if let crate::Verdict::Refuse(refusal) = inspector.end() {
244        return Err(FormError::Refused {
245            refusal,
246            at: crate::error::Location::at(input.len() as u64),
247        });
248    }
249    Ok(())
250}
251
252fn feed_multipart<B: FormBuilder>(
253    builder: &mut B,
254    mp: &crate::Multipart<'_>,
255) -> Result<(), FormError> {
256    for part in mp.parts() {
257        if part.is_file() {
258            builder.file(part.name(), FileValue::Buffered(part))?;
259        } else {
260            builder.text(part.name(), part.text()?)?;
261        }
262    }
263    Ok(())
264}
265
266/// A struct that can be read from a form. Implemented by
267/// [`form!`](crate::form); every method is provided.
268///
269/// # Examples
270///
271/// ```
272/// use form_net::FromForm;
273///
274/// form_net::form! {
275///     pub struct Login {
276///         pub email: String,
277///         pub password: form_net::Secret,
278///         pub remember: bool,
279///     }
280///     ignore = ["csrf_token"];
281/// }
282///
283/// let login = Login::from_body(b"application/x-www-form-urlencoded", b"email=a%40b.example&password=x&csrf_token=t")?;
284/// assert_eq!((login.email.as_str(), login.remember), ("a@b.example", false));
285/// # Ok::<(), form_net::FormError>(())
286/// ```
287pub trait FromForm: Sized {
288    /// The builder that collects the fields.
289    type Builder: FormBuilder<Output = Self> + Default;
290
291    /// From a urlencoded body, with the default limits and policy.
292    ///
293    /// # Errors
294    ///
295    /// The parser's errors; `Extract { .. }`.
296    fn from_urlencoded(input: &[u8]) -> Result<Self, FormError> {
297        Self::from_urlencoded_with(input, &Limits::DEFAULT, &Policy::DEFAULT)
298    }
299
300    /// From a query string; one leading `?` is stripped.
301    ///
302    /// # Errors
303    ///
304    /// As [`from_urlencoded`](Self::from_urlencoded).
305    fn from_query(query: &[u8]) -> Result<Self, FormError> {
306        Self::from_urlencoded(query.strip_prefix(b"?").unwrap_or(query))
307    }
308
309    /// From a buffered `multipart/form-data` body.
310    ///
311    /// # Errors
312    ///
313    /// The parser's errors; `Extract { .. }`.
314    fn from_multipart(content_type: &[u8], body: &[u8]) -> Result<Self, FormError> {
315        let mp = crate::parse_multipart(content_type, body)?;
316        let mut b = Self::Builder::default();
317        feed_multipart(&mut b, &mp)?;
318        b.finish()
319    }
320
321    /// From either encoding, by the `Content-Type`.
322    ///
323    /// # Errors
324    ///
325    /// `UnsupportedMediaType`; the parsers' errors; `Extract { .. }`.
326    fn from_body(content_type: &[u8], body: &[u8]) -> Result<Self, FormError> {
327        Self::from_body_with(content_type, body, &Limits::DEFAULT, &Policy::DEFAULT)
328    }
329
330    /// From a urlencoded body with explicit limits and policy.
331    ///
332    /// # Errors
333    ///
334    /// As [`from_urlencoded`](Self::from_urlencoded); `InvalidLimits`.
335    fn from_urlencoded_with(
336        input: &[u8],
337        limits: &Limits,
338        policy: &Policy,
339    ) -> Result<Self, FormError> {
340        limits.validate()?;
341        let mut b = Self::Builder::default();
342        feed_urlencoded(&mut b, input, limits, policy, &mut NoInspector)?;
343        b.finish()
344    }
345
346    /// From either encoding with explicit limits and policy.
347    ///
348    /// # Errors
349    ///
350    /// As [`from_body`](Self::from_body); `InvalidLimits`.
351    fn from_body_with(
352        content_type: &[u8],
353        body: &[u8],
354        limits: &Limits,
355        policy: &Policy,
356    ) -> Result<Self, FormError> {
357        Self::from_body_inspected(content_type, body, limits, policy, &mut NoInspector)
358    }
359
360    /// From either encoding, with an inspector in the same pass: the
361    /// inspector and the struct see the same bytes.
362    ///
363    /// # Errors
364    ///
365    /// As [`from_body_with`](Self::from_body_with); `Refused`.
366    fn from_body_inspected<I: Inspector>(
367        content_type: &[u8],
368        body: &[u8],
369        limits: &Limits,
370        policy: &Policy,
371        inspector: &mut I,
372    ) -> Result<Self, FormError> {
373        let result = (|| {
374            let mut b = Self::Builder::default();
375            match FormKind::detect(content_type)? {
376                FormKind::Urlencoded => {
377                    limits.validate()?;
378                    feed_urlencoded(&mut b, body, limits, policy, inspector)?;
379                }
380                FormKind::Multipart(_) => {
381                    let mp = crate::multipart::parse_multipart_inspected_raw(
382                        content_type,
383                        body,
384                        limits,
385                        policy,
386                        inspector,
387                    )?;
388                    feed_multipart(&mut b, &mp)?;
389                }
390            }
391            b.finish()
392        })();
393        if let Err(e) = &result {
394            inspector.failed(e);
395        }
396        result
397    }
398
399    /// From a finished upload: text fields as text, files moved into the
400    /// struct as `StoredFile`s.
401    ///
402    /// # Errors
403    ///
404    /// `Extract { .. }`.
405    #[cfg(feature = "fs")]
406    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
407    fn from_upload(upload: crate::store::Upload) -> Result<Self, FormError> {
408        let (fields, files) = upload.into_parts();
409        let mut b = Self::Builder::default();
410        for (name, value) in fields.iter() {
411            b.text(name, value)?;
412        }
413        for file in files {
414            let name = alloc::string::String::from(file.name());
415            b.file(&name, FileValue::Stored(file))?;
416        }
417        b.finish()
418    }
419}
420
421/// Maps a field's [`ValueError`] to the error `form!` returns. Not part of
422/// the API: used by the macro expansion.
423#[doc(hidden)]
424#[must_use]
425pub fn __value_error(e: ValueError, field: &'static str) -> FormError {
426    let reason = match e {
427        ValueError::Missing => ExtractError::Missing,
428        ValueError::Duplicate => ExtractError::Duplicate,
429        ValueError::Invalid(why) => ExtractError::Invalid(why),
430        _ => ExtractError::WrongKind,
431    };
432    FormError::Extract {
433        reason,
434        field: Some(field),
435    }
436}
437
438/// The error for a name the struct does not declare. Not part of the API.
439#[doc(hidden)]
440#[must_use]
441pub const fn __unknown() -> FormError {
442    FormError::Extract {
443        reason: ExtractError::Unknown,
444        field: None,
445    }
446}