rocket_community/data/
from_data.rs

1use crate::data::{Data, Limits};
2use crate::http::{RawStr, Status};
3use crate::outcome::{self, try_outcome, IntoOutcome, Outcome::*};
4use crate::request::{local_cache, Request};
5
6/// Type alias for the `Outcome` of [`FromData`].
7///
8/// [`FromData`]: crate::data::FromData
9pub type Outcome<'r, T, E = <T as FromData<'r>>::Error> =
10    outcome::Outcome<T, (Status, E), (Data<'r>, Status)>;
11
12/// Trait implemented by data guards to derive a value from request body data.
13///
14/// # Data Guards
15///
16/// A data guard is a guard that operates on a request's body data. Data guards
17/// validate and parse request body data via implementations of `FromData`. In
18/// other words, a type is a data guard _iff_ it implements `FromData`.
19///
20/// Data guards are the target of the `data` route attribute parameter:
21///
22/// ```rust
23/// # #[macro_use] extern crate rocket_community as rocket;
24/// # type DataGuard = String;
25/// #[post("/submit", data = "<var>")]
26/// fn submit(var: DataGuard) { /* ... */ }
27/// ```
28///
29/// A route can have at most one data guard. Above, `var` is used as the
30/// argument name for the data guard type `DataGuard`. When the `submit` route
31/// matches, Rocket will call the `FromData` implementation for the type `T`.
32/// The handler will only be called if the guard returns successfully.
33///
34/// ## Build-In Guards
35///
36/// Rocket provides implementations for `FromData` for many types. Their
37/// behavior is documented here:
38///
39///   * `Data`: Returns the untouched `Data`.
40///
41///     - **Fails:** Never.
42///
43///     - **Succeeds:** Always.
44///
45///     - **Forwards:** Never.
46///
47///   * Strings: `Cow<str>`, `&str`, `&RawStr`, `String`
48///
49///     _Limited by the `string` [data limit]._
50///
51///     Reads the body data into a string via [`DataStream::into_string()`].
52///
53///     - **Fails:** If the body data is not valid UTF-8 or on I/O errors while
54///     reading. The error type is [`io::Error`].
55///
56///     - **Succeeds:** If the body data _is_ valid UTF-8. If the limit is
57///     exceeded, the string is truncated to the limit.
58///
59///     - **Forwards:** Never.
60///
61///   * Bytes: `&[u8]`, `Vec<u8>`
62///
63///     _Limited by the `bytes` [data limit]._
64///
65///     Reads the body data into a byte vector via [`DataStream::into_bytes()`].
66///
67///     - **Fails:** On I/O errors while reading. The error type is
68///     [`io::Error`].
69///
70///     - **Succeeds:** As long as no I/O error occurs. If the limit is
71///     exceeded, the slice is truncated to the limit.
72///
73///     - **Forwards:** Never.
74///
75///   * [`TempFile`](crate::fs::TempFile)
76///
77///     _Limited by the `file` and/or `file/$ext` [data limit]._
78///
79///     Streams the body data directly into a temporary file. The data is never
80///     buffered in memory.
81///
82///     - **Fails:** On I/O errors while reading data or creating the temporary
83///     file. The error type is [`io::Error`].
84///
85///     - **Succeeds:** As long as no I/O error occurs and the temporary file
86///     could be created. If the limit is exceeded, only data up to the limit is
87///     read and subsequently written.
88///
89///     - **Forwards:** Never.
90///
91///   * Deserializers: [`Json<T>`], [`MsgPack<T>`]
92///
93///     _Limited by the `json`, `msgpack` [data limit], respectively._
94///
95///     Reads up to the configured limit and deserializes the read data into `T`
96///     using the respective format's parser.
97///
98///     - **Fails:** On I/O errors while reading the data, or if the data fails
99///     to parse as a `T` according to the deserializer. The error type for
100///     `Json` is [`json::Error`](crate::serde::json::Error) and the error type
101///     for `MsgPack` is [`msgpack::Error`](crate::serde::msgpack::Error).
102///
103///     - **Succeeds:** As long as no I/O error occurs and the (limited) body
104///     data was successfully deserialized as a `T`.
105///
106///     - **Forwards:** Never.
107///
108///   * Forms: [`Form<T>`]
109///
110///     _Limited by the `form` or `data-form` [data limit]._
111///
112///     Parses the incoming data stream into fields according to Rocket's [field
113///     wire format], pushes each field to `T`'s [`FromForm`] [push parser], and
114///     finalizes the form. Parsing is done on the stream without reading the
115///     data into memory. If the request has as a [`ContentType::Form`], the
116///     `form` limit is applied, otherwise if the request has a
117///     [`ContentType::FormData`], the `data-form` limit is applied.
118///
119///     - **Fails:** On I/O errors while reading the data, or if the data fails
120///     to parse as a `T` according to its `FromForm` implementation. The errors
121///     are collected into an [`Errors`](crate::form::Errors), the error type.
122///
123///     - **Succeeds:** As long as no I/O error occurs and the (limited) body
124///     data was successfully parsed as a `T`.
125///
126///     - **Forwards:** If the request's `Content-Type` is neither
127///     [`ContentType::Form`] nor [`ContentType::FormData`].
128///
129///   * `Option<T>`
130///
131///     Forwards to `T`'s `FromData` implementation, capturing the outcome.
132///
133///     - **Fails:** Never.
134///
135///     - **Succeeds:** Always. If `T`'s `FromData` implementation succeeds, the
136///     parsed value is returned in `Some`. If its implementation forwards or
137///     fails, `None` is returned.
138///
139///     - **Forwards:** Never.
140///
141///   * `Result<T, T::Error>`
142///
143///     Forwards to `T`'s `FromData` implementation, capturing the outcome.
144///
145///     - **Fails:** Never.
146///
147///     - **Succeeds:** If `T`'s `FromData` implementation succeeds or fails. If
148///     it succeeds, the value is returned in `Ok`. If it fails, the error value
149///     is returned in `Err`.
150///
151///     - **Forwards:** If `T`'s implementation forwards.
152///
153///   * [`Capped<T>`]
154///
155///     Forwards to `T`'s `FromData` implementation, recording whether the data
156///     was truncated (a.k.a. capped) due to `T`'s limit being exceeded.
157///
158///     - **Fails:** If `T`'s implementation fails.
159///     - **Succeeds:** If `T`'s implementation succeeds.
160///     - **Forwards:** If `T`'s implementation forwards.
161///
162/// [data limit]: crate::data::Limits#built-in-limits
163/// [`DataStream::into_string()`]: crate::data::DataStream::into_string()
164/// [`DataStream::into_bytes()`]: crate::data::DataStream::into_bytes()
165/// [`io::Error`]: std::io::Error
166/// [`Json<T>`]: crate::serde::json::Json
167/// [`MsgPack<T>`]: crate::serde::msgpack::MsgPack
168/// [`Form<T>`]: crate::form::Form
169/// [field wire format]: crate::form#field-wire-format
170/// [`FromForm`]: crate::form::FromForm
171/// [push parser]: crate::form::FromForm#push-parsing
172/// [`ContentType::Form`]: crate::http::ContentType::Form
173/// [`ContentType::FormData`]: crate::http::ContentType::FormData
174///
175/// ## Async Trait
176///
177/// [`FromData`] is an _async_ trait. Implementations of `FromData` must be
178/// decorated with an attribute of `#[rocket::async_trait]`:
179///
180/// ```rust
181/// # extern crate rocket_community as rocket;
182/// use rocket::request::Request;
183/// use rocket::data::{self, Data, FromData};
184/// # struct MyType;
185/// # type MyError = String;
186///
187/// #[rocket::async_trait]
188/// impl<'r> FromData<'r> for MyType {
189///     type Error = MyError;
190///
191///     async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
192///         /* .. */
193///         # unimplemented!()
194///     }
195/// }
196/// ```
197///
198/// # Example
199///
200/// Say that you have a custom type, `Person`:
201///
202/// ```rust
203/// struct Person<'r> {
204///     name: &'r str,
205///     age: u16
206/// }
207/// ```
208///
209/// `Person` has a custom serialization format, so the built-in `Json` type
210/// doesn't suffice. The format is `<name>:<age>` with `Content-Type:
211/// application/x-person`. You'd like to use `Person` as a data guard, so that
212/// you can retrieve it directly from a client's request body:
213///
214/// ```rust
215/// # extern crate rocket_community as rocket;
216/// # use rocket::post;
217/// # type Person<'r> = &'r rocket::http::RawStr;
218/// #[post("/person", data = "<person>")]
219/// fn person(person: Person<'_>) -> &'static str {
220///     "Saved the new person to the database!"
221/// }
222/// ```
223///
224/// A `FromData` implementation for such a type might look like:
225///
226/// ```rust
227/// # #[macro_use] extern crate rocket_community as rocket;
228/// #
229/// # #[derive(Debug)]
230/// # struct Person<'r> { name: &'r str, age: u16 }
231/// #
232/// use rocket::request::{self, Request};
233/// use rocket::data::{self, Data, FromData, ToByteUnit};
234/// use rocket::http::{Status, ContentType};
235/// use rocket::outcome::Outcome;
236///
237/// #[derive(Debug)]
238/// enum Error {
239///     TooLarge,
240///     NoColon,
241///     InvalidAge,
242///     Io(std::io::Error),
243/// }
244///
245/// #[rocket::async_trait]
246/// impl<'r> FromData<'r> for Person<'r> {
247///     type Error = Error;
248///
249///     async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
250///         use Error::*;
251///
252///         // Ensure the content type is correct before opening the data.
253///         let person_ct = ContentType::new("application", "x-person");
254///         if req.content_type() != Some(&person_ct) {
255///             return Outcome::Forward((data, Status::UnsupportedMediaType));
256///         }
257///
258///         // Use a configured limit with name 'person' or fallback to default.
259///         let limit = req.limits().get("person").unwrap_or(256.bytes());
260///
261///         // Read the data into a string.
262///         let string = match data.open(limit).into_string().await {
263///             Ok(string) if string.is_complete() => string.into_inner(),
264///             Ok(_) => return Outcome::Error((Status::PayloadTooLarge, TooLarge)),
265///             Err(e) => return Outcome::Error((Status::InternalServerError, Io(e))),
266///         };
267///
268///         // We store `string` in request-local cache for long-lived borrows.
269///         let string = request::local_cache!(req, string);
270///
271///         // Split the string into two pieces at ':'.
272///         let (name, age) = match string.find(':') {
273///             Some(i) => (&string[..i], &string[(i + 1)..]),
274///             None => return Outcome::Error((Status::UnprocessableEntity, NoColon)),
275///         };
276///
277///         // Parse the age.
278///         let age: u16 = match age.parse() {
279///             Ok(age) => age,
280///             Err(_) => return Outcome::Error((Status::UnprocessableEntity, InvalidAge)),
281///         };
282///
283///         Outcome::Success(Person { name, age })
284///     }
285/// }
286///
287/// // The following routes now typecheck...
288///
289/// #[post("/person", data = "<person>")]
290/// fn person(person: Person<'_>) { /* .. */ }
291///
292/// #[post("/person", data = "<person>")]
293/// fn person2(person: Result<Person<'_>, Error>) { /* .. */ }
294///
295/// #[post("/person", data = "<person>")]
296/// fn person3(person: Option<Person<'_>>) { /* .. */ }
297///
298/// #[post("/person", data = "<person>")]
299/// fn person4(person: Person<'_>) -> &str {
300///     // Note that this is only possible because the data in `person` live
301///     // as long as the request through request-local cache.
302///     person.name
303/// }
304/// ```
305#[crate::async_trait]
306pub trait FromData<'r>: Sized {
307    /// The associated error to be returned when the guard fails.
308    type Error: Send + std::fmt::Debug;
309
310    /// Asynchronously validates, parses, and converts an instance of `Self`
311    /// from the incoming request body data.
312    ///
313    /// If validation and parsing succeeds, an outcome of `Success` is returned.
314    /// If the data is not appropriate given the type of `Self`, `Forward` is
315    /// returned. If parsing fails, `Error` is returned.
316    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self>;
317}
318
319use crate::data::Capped;
320
321#[crate::async_trait]
322impl<'r> FromData<'r> for Capped<String> {
323    type Error = std::io::Error;
324
325    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
326        let limit = req.limits().get("string").unwrap_or(Limits::STRING);
327        data.open(limit)
328            .into_string()
329            .await
330            .or_error(Status::BadRequest)
331    }
332}
333
334impl_strict_from_data_from_capped!(String);
335
336#[crate::async_trait]
337impl<'r> FromData<'r> for Capped<&'r str> {
338    type Error = std::io::Error;
339
340    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
341        let capped = try_outcome!(<Capped<String>>::from_data(req, data).await);
342        let string = capped.map(|s| local_cache!(req, s));
343        Success(string)
344    }
345}
346
347impl_strict_from_data_from_capped!(&'r str);
348
349#[crate::async_trait]
350impl<'r> FromData<'r> for Capped<&'r RawStr> {
351    type Error = std::io::Error;
352
353    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
354        let capped = try_outcome!(<Capped<String>>::from_data(req, data).await);
355        let raw = capped.map(|s| RawStr::new(local_cache!(req, s)));
356        Success(raw)
357    }
358}
359
360impl_strict_from_data_from_capped!(&'r RawStr);
361
362#[crate::async_trait]
363impl<'r> FromData<'r> for Capped<std::borrow::Cow<'_, str>> {
364    type Error = std::io::Error;
365
366    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
367        let capped = try_outcome!(<Capped<String>>::from_data(req, data).await);
368        Success(capped.map(|s| s.into()))
369    }
370}
371
372impl_strict_from_data_from_capped!(std::borrow::Cow<'_, str>);
373
374#[crate::async_trait]
375impl<'r> FromData<'r> for Capped<&'r [u8]> {
376    type Error = std::io::Error;
377
378    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
379        let capped = try_outcome!(<Capped<Vec<u8>>>::from_data(req, data).await);
380        let raw = capped.map(|b| local_cache!(req, b));
381        Success(raw)
382    }
383}
384
385impl_strict_from_data_from_capped!(&'r [u8]);
386
387#[crate::async_trait]
388impl<'r> FromData<'r> for Capped<Vec<u8>> {
389    type Error = std::io::Error;
390
391    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
392        let limit = req.limits().get("bytes").unwrap_or(Limits::BYTES);
393        data.open(limit)
394            .into_bytes()
395            .await
396            .or_error(Status::BadRequest)
397    }
398}
399
400impl_strict_from_data_from_capped!(Vec<u8>);
401
402#[crate::async_trait]
403impl<'r> FromData<'r> for Data<'r> {
404    type Error = std::convert::Infallible;
405
406    async fn from_data(_: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
407        Success(data)
408    }
409}
410
411#[crate::async_trait]
412impl<'r, T: FromData<'r> + 'r> FromData<'r> for Result<T, T::Error> {
413    type Error = std::convert::Infallible;
414
415    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
416        match T::from_data(req, data).await {
417            Success(v) => Success(Ok(v)),
418            Error((_, e)) => Success(Err(e)),
419            Forward(d) => Forward(d),
420        }
421    }
422}
423
424#[crate::async_trait]
425impl<'r, T: FromData<'r>> FromData<'r> for Option<T> {
426    type Error = std::convert::Infallible;
427
428    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
429        match T::from_data(req, data).await {
430            Success(v) => Success(Some(v)),
431            Error(..) | Forward(..) => Success(None),
432        }
433    }
434}