Drop 'Data' after sending a response, not before.

This allows responses to be sent to the client even when data is only
partially read, significantly improving the experience for the client
from one with a "connection closed" error to one with a proper response.
The consequence is a lifetime in 'Data'.

Though other non-lifetime-introducing solutions exist, the introduction
of a lifetime to 'Data' is a longstanding desire as it prevents
smuggling 'Data' into a longer-lived context. Use of 'Data' in that
context was unspecified with various runtime consequences. The addition
of a lifetime bound by the request prevents this error statically.

In summary, the changes are:
  * Clients receive responses even when data isn't fully read.
  * 'Data' becomes 'Data<'r>'. 'FromData' changes accordingly.
  * Route 'Outcome's are strictly tied to the request lifetime.

Tangentially, the invalid length form field validation error message has
improved to format length in byte units if it exceeds 1024.
This commit is contained in:
Sergio Benitez 2021-06-08 02:13:02 -07:00
parent 7595450adc
commit 4c6562cd29
41 changed files with 179 additions and 172 deletions

View File

@ -6,7 +6,7 @@ use rocket::{route, config, Request, Data, Route, Config};
use rocket::http::{Method, RawStr, ContentType, Accept, Status}; use rocket::http::{Method, RawStr, ContentType, Accept, Status};
use rocket::local::blocking::{Client, LocalRequest}; use rocket::local::blocking::{Client, LocalRequest};
fn dummy_handler<'r>(req: &'r Request, _: Data) -> route::BoxFuture<'r> { fn dummy_handler<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> {
route::Outcome::from(req, ()).pin() route::Outcome::from(req, ()).pin()
} }

View File

@ -65,7 +65,7 @@ impl Fairing for TemplateFairing {
} }
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
async fn on_request(&self, req: &mut rocket::Request<'_>, _data: &mut rocket::Data) { async fn on_request(&self, req: &mut rocket::Request<'_>, _data: &mut rocket::Data<'_>) {
let cm = req.rocket().state::<ContextManager>() let cm = req.rocket().state::<ContextManager>()
.expect("Template ContextManager registered in on_ignite"); .expect("Template ContextManager registered in on_ignite");

View File

@ -64,10 +64,10 @@ pub fn _catch(
/// Rocket code generated proxy static conversion implementations. /// Rocket code generated proxy static conversion implementations.
impl #user_catcher_fn_name { impl #user_catcher_fn_name {
fn into_info(self) -> #_catcher::StaticInfo { fn into_info(self) -> #_catcher::StaticInfo {
fn monomorphized_function<'_b>( fn monomorphized_function<'__r>(
#__status: #Status, #__status: #Status,
#__req: &'_b #Request<'_> #__req: &'__r #Request<'_>
) -> #_catcher::BoxFuture<'_b> { ) -> #_catcher::BoxFuture<'__r> {
#_Box::pin(async move { #_Box::pin(async move {
let __response = #catcher_response; let __response = #catcher_response;
#Response::build() #Response::build()

View File

@ -331,10 +331,10 @@ fn codegen_route(route: Route) -> Result<TokenStream> {
impl #handler_fn_name { impl #handler_fn_name {
#[allow(non_snake_case, unreachable_patterns, unreachable_code)] #[allow(non_snake_case, unreachable_patterns, unreachable_code)]
fn into_info(self) -> #_route::StaticInfo { fn into_info(self) -> #_route::StaticInfo {
fn monomorphized_function<'_b>( fn monomorphized_function<'__r>(
#__req: &'_b #Request<'_>, #__req: &'__r #Request<'_>,
#__data: #Data #__data: #Data<'__r>
) -> #_route::BoxFuture<'_b> { ) -> #_route::BoxFuture<'__r> {
#_Box::pin(async move { #_Box::pin(async move {
#(#request_guards)* #(#request_guards)*
#(#param_guards)* #(#param_guards)*

View File

@ -19,7 +19,7 @@ struct Simple<'r>(&'r str);
impl<'r> FromData<'r> for Simple<'r> { impl<'r> FromData<'r> for Simple<'r> {
type Error = std::io::Error; type Error = std::io::Error;
async fn from_data(req: &'r Request<'_>, data: Data) -> data::Outcome<Self, Self::Error> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
<&'r str>::from_data(req, data).await.map(Simple) <&'r str>::from_data(req, data).await.map(Simple)
} }
} }

View File

@ -26,7 +26,7 @@ struct Simple(String);
impl<'r> FromData<'r> for Simple { impl<'r> FromData<'r> for Simple {
type Error = std::io::Error; type Error = std::io::Error;
async fn from_data(req: &'r Request<'_>, data: Data) -> data::Outcome<Self, Self::Error> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
String::from_data(req, data).await.map(Simple) String::from_data(req, data).await.map(Simple)
} }
} }
@ -78,7 +78,7 @@ fn post2(
#[allow(dead_code)] #[allow(dead_code)]
#[post("/<_unused_param>?<_unused_query>", data="<_unused_data>")] #[post("/<_unused_param>?<_unused_query>", data="<_unused_data>")]
fn test_unused_params(_unused_param: String, _unused_query: String, _unused_data: Data) { fn test_unused_params(_unused_param: String, _unused_query: String, _unused_data: Data<'_>) {
} }
#[test] #[test]

View File

@ -53,4 +53,4 @@ error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied
28 | fn foo() -> usize { 0 } 28 | fn foo() -> usize { 0 }
| ^^^^^ the trait `Responder<'_, '_>` is not implemented for `usize` | ^^^^^ the trait `Responder<'_, '_>` is not implemented for `usize`
| |
= note: required by `route::handler::<impl Outcome<rocket::Response<'o>, Status, rocket::Data>>::from` = note: required by `route::handler::<impl Outcome<rocket::Response<'o>, Status, rocket::Data<'o>>>::from`

View File

@ -38,7 +38,7 @@ error[E0277]: the trait bound `Q: FromData<'_>` is not satisfied
| |
::: $WORKSPACE/core/lib/src/data/from_data.rs ::: $WORKSPACE/core/lib/src/data/from_data.rs
| |
| async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome<Self, Self::Error>; | async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self>;
| -- required by this bound in `rocket::data::FromData::from_data` | -- required by this bound in `rocket::data::FromData::from_data`
error[E0277]: the trait bound `Q: FromRequest<'_>` is not satisfied error[E0277]: the trait bound `Q: FromRequest<'_>` is not satisfied

View File

@ -53,4 +53,4 @@ error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied
28 | fn foo() -> usize { 0 } 28 | fn foo() -> usize { 0 }
| ^^^^^ the trait `Responder<'_, '_>` is not implemented for `usize` | ^^^^^ the trait `Responder<'_, '_>` is not implemented for `usize`
| |
= note: required by `route::handler::<impl Outcome<rocket::Response<'o>, Status, rocket::Data>>::from` = note: required by `route::handler::<impl Outcome<rocket::Response<'o>, Status, rocket::Data<'o>>>::from`

View File

@ -38,7 +38,7 @@ error[E0277]: the trait bound `Q: FromData<'_>` is not satisfied
| |
::: $WORKSPACE/core/lib/src/data/from_data.rs ::: $WORKSPACE/core/lib/src/data/from_data.rs
| |
| async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome<Self, Self::Error>; | async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self>;
| -- required by this bound in `rocket::data::FromData::from_data` | -- required by this bound in `rocket::data::FromData::from_data`
error[E0277]: the trait bound `Q: FromRequest<'_>` is not satisfied error[E0277]: the trait bound `Q: FromRequest<'_>` is not satisfied

View File

@ -16,10 +16,10 @@ fn f2() {}
// Check if a data argument is used with a usually non-payload bearing method. // Check if a data argument is used with a usually non-payload bearing method.
#[get("/", data = "<_foo>")] #[get("/", data = "<_foo>")]
fn g0(_foo: rocket::Data) {} fn g0(_foo: rocket::Data<'_>) {}
#[head("/", data = "<_foo>")] #[head("/", data = "<_foo>")]
fn g1(_foo: rocket::Data) {} fn g1(_foo: rocket::Data<'_>) {}
fn main() { fn main() {
compile_error!("checking for warnings!") compile_error!("checking for warnings!")

View File

@ -223,21 +223,21 @@ macro_rules! impl_strict_from_form_field_from_capped {
fn from_value(f: ValueField<'v>) -> Result<'v, Self> { fn from_value(f: ValueField<'v>) -> Result<'v, Self> {
let capped = <Capped<$T> as FromFormField<'v>>::from_value(f)?; let capped = <Capped<$T> as FromFormField<'v>>::from_value(f)?;
if capped.is_complete() { if !capped.is_complete() {
Ok(capped.value) Err((None, Some(capped.n.written)))?;
} else {
Err((None, Some(capped.n.written)))?
} }
Ok(capped.value)
} }
async fn from_data(field: DataField<'v, '_>) -> Result<'v, Self> { async fn from_data(field: DataField<'v, '_>) -> Result<'v, Self> {
let capped = <Capped<$T> as FromFormField<'v>>::from_data(field); let capped = <Capped<$T> as FromFormField<'v>>::from_data(field);
let capped = capped.await?; let capped = capped.await?;
if capped.is_complete() { if !capped.is_complete() {
Ok(capped.value) Err((None, Some(capped.n.written)))?;
} else {
Err((None, Some(capped.n.written)))?
} }
Ok(capped.value)
} }
} }
};) };)
@ -251,8 +251,8 @@ macro_rules! impl_strict_from_data_from_capped {
async fn from_data( async fn from_data(
r: &'r $crate::Request<'_>, r: &'r $crate::Request<'_>,
d: $crate::Data d: $crate::Data<'r>
) -> $crate::data::Outcome<Self, Self::Error> { ) -> $crate::data::Outcome<'r, Self> {
use $crate::outcome::Outcome::*; use $crate::outcome::Outcome::*;
use std::io::{Error, ErrorKind::UnexpectedEof}; use std::io::{Error, ErrorKind::UnexpectedEof};

View File

@ -15,7 +15,7 @@ pub const PEEK_BYTES: usize = 512;
/// ///
/// ```rust /// ```rust
/// # #[macro_use] extern crate rocket; /// # #[macro_use] extern crate rocket;
/// # type DataGuard = rocket::data::Data; /// # type DataGuard = String;
/// #[post("/submit", data = "<var>")] /// #[post("/submit", data = "<var>")]
/// fn submit(var: DataGuard) { /* ... */ } /// fn submit(var: DataGuard) { /* ... */ }
/// # fn main() { } /// # fn main() { }
@ -37,15 +37,15 @@ pub const PEEK_BYTES: usize = 512;
/// The `peek` method returns a slice containing at most 512 bytes of buffered /// The `peek` method returns a slice containing at most 512 bytes of buffered
/// body data. This enables partially or fully reading from a `Data` object /// body data. This enables partially or fully reading from a `Data` object
/// without consuming the `Data` object. /// without consuming the `Data` object.
pub struct Data { pub struct Data<'r> {
buffer: Vec<u8>, buffer: Vec<u8>,
is_complete: bool, is_complete: bool,
stream: StreamReader, stream: StreamReader<'r>,
} }
impl Data { impl<'r> Data<'r> {
/// Create a `Data` from a recognized `stream`. /// Create a `Data` from a recognized `stream`.
pub(crate) fn from<S: Into<StreamReader>>(stream: S) -> Data { pub(crate) fn from<S: Into<StreamReader<'r>>>(stream: S) -> Data<'r> {
// TODO.async: This used to also set the read timeout to 5 seconds. // TODO.async: This used to also set the read timeout to 5 seconds.
// Such a short read timeout is likely no longer necessary, but some // Such a short read timeout is likely no longer necessary, but some
// kind of idle timeout should be implemented. // kind of idle timeout should be implemented.
@ -57,7 +57,7 @@ impl Data {
/// This creates a `data` object from a local data source `data`. /// This creates a `data` object from a local data source `data`.
#[inline] #[inline]
pub(crate) fn local(data: Vec<u8>) -> Data { pub(crate) fn local(data: Vec<u8>) -> Data<'r> {
Data { Data {
buffer: data, buffer: data,
stream: StreamReader::empty(), stream: StreamReader::empty(),
@ -78,11 +78,11 @@ impl Data {
/// use rocket::data::{Data, ToByteUnit}; /// use rocket::data::{Data, ToByteUnit};
/// ///
/// # const SIZE_LIMIT: u64 = 2 << 20; // 2MiB /// # const SIZE_LIMIT: u64 = 2 << 20; // 2MiB
/// fn handler(data: Data) { /// fn handler(data: Data<'_>) {
/// let stream = data.open(2.mebibytes()); /// let stream = data.open(2.mebibytes());
/// } /// }
/// ``` /// ```
pub fn open(self, limit: ByteUnit) -> DataStream { pub fn open(self, limit: ByteUnit) -> DataStream<'r> {
DataStream::new(self.buffer, self.stream, limit.into()) DataStream::new(self.buffer, self.stream, limit.into())
} }
@ -101,7 +101,7 @@ impl Data {
/// ///
/// ```rust /// ```rust
/// use rocket::request::{self, Request, FromRequest}; /// use rocket::request::{self, Request, FromRequest};
/// use rocket::data::{self, Data, FromData}; /// use rocket::data::{Data, FromData, Outcome};
/// # struct MyType; /// # struct MyType;
/// # type MyError = String; /// # type MyError = String;
/// ///
@ -109,12 +109,9 @@ impl Data {
/// impl<'r> FromData<'r> for MyType { /// impl<'r> FromData<'r> for MyType {
/// type Error = MyError; /// type Error = MyError;
/// ///
/// async fn from_data( /// async fn from_data(r: &'r Request<'_>, mut data: Data<'r>) -> Outcome<'r, Self> {
/// req: &'r Request<'_>,
/// mut data: Data
/// ) -> data::Outcome<Self, Self::Error> {
/// if data.peek(2).await != b"hi" { /// if data.peek(2).await != b"hi" {
/// return data::Outcome::Forward(data) /// return Outcome::Forward(data)
/// } /// }
/// ///
/// /* .. */ /// /* .. */
@ -139,7 +136,7 @@ impl Data {
/// } /// }
/// } /// }
/// ///
/// async fn on_request(&self, req: &mut Request<'_>, data: &mut Data) { /// async fn on_request(&self, req: &mut Request<'_>, data: &mut Data<'_>) {
/// if data.peek(2).await == b"hi" { /// if data.peek(2).await == b"hi" {
/// /* do something; body data starts with `"hi"` */ /// /* do something; body data starts with `"hi"` */
/// } /// }
@ -179,7 +176,7 @@ impl Data {
/// ```rust /// ```rust
/// use rocket::data::Data; /// use rocket::data::Data;
/// ///
/// async fn handler(mut data: Data) { /// async fn handler(mut data: Data<'_>) {
/// if data.peek_complete() { /// if data.peek_complete() {
/// println!("All of the data: {:?}", data.peek(512).await); /// println!("All of the data: {:?}", data.peek(512).await);
/// } /// }

View File

@ -39,14 +39,14 @@ use crate::data::{Capped, N};
/// ///
/// [`DataStream::stream_to(&mut vec)`]: DataStream::stream_to() /// [`DataStream::stream_to(&mut vec)`]: DataStream::stream_to()
/// [`DataStream::stream_to(&mut file)`]: DataStream::stream_to() /// [`DataStream::stream_to(&mut file)`]: DataStream::stream_to()
pub struct DataStream { pub struct DataStream<'r> {
pub(crate) chain: Take<Chain<Cursor<Vec<u8>>, StreamReader>>, pub(crate) chain: Take<Chain<Cursor<Vec<u8>>, StreamReader<'r>>>,
} }
/// An adapter: turns a `T: Stream` (in `StreamKind`) into a `tokio::AsyncRead`. /// An adapter: turns a `T: Stream` (in `StreamKind`) into a `tokio::AsyncRead`.
pub struct StreamReader { pub struct StreamReader<'r> {
state: State, state: State,
inner: StreamKind, inner: StreamKind<'r>,
} }
/// The current state of `StreamReader` `AsyncRead` adapter. /// The current state of `StreamReader` `AsyncRead` adapter.
@ -57,13 +57,14 @@ enum State {
} }
/// The kinds of streams we accept as `Data`. /// The kinds of streams we accept as `Data`.
enum StreamKind { enum StreamKind<'r> {
Body(hyper::Body), Empty,
Multipart(multer::Field<'static>) Body(&'r mut hyper::Body),
Multipart(multer::Field<'r>)
} }
impl DataStream { impl<'r> DataStream<'r> {
pub(crate) fn new(buf: Vec<u8>, stream: StreamReader, limit: u64) -> Self { pub(crate) fn new(buf: Vec<u8>, stream: StreamReader<'r>, limit: u64) -> Self {
let chain = Chain::new(Cursor::new(buf), stream).take(limit); let chain = Chain::new(Cursor::new(buf), stream).take(limit);
Self { chain } Self { chain }
} }
@ -71,7 +72,7 @@ impl DataStream {
/// Whether a previous read exhausted the set limit _and then some_. /// Whether a previous read exhausted the set limit _and then some_.
async fn limit_exceeded(&mut self) -> io::Result<bool> { async fn limit_exceeded(&mut self) -> io::Result<bool> {
#[cold] #[cold]
async fn _limit_exceeded(stream: &mut DataStream) -> io::Result<bool> { async fn _limit_exceeded(stream: &mut DataStream<'_>) -> io::Result<bool> {
stream.chain.set_limit(1); stream.chain.set_limit(1);
let mut buf = [0u8; 1]; let mut buf = [0u8; 1];
Ok(stream.read(&mut buf).await? != 0) Ok(stream.read(&mut buf).await? != 0)
@ -87,7 +88,7 @@ impl DataStream {
/// ```rust /// ```rust
/// use rocket::data::{Data, ToByteUnit}; /// use rocket::data::{Data, ToByteUnit};
/// ///
/// async fn f(data: Data) { /// async fn f(data: Data<'_>) {
/// let definitely_have_n_bytes = data.open(1.kibibytes()).hint(); /// let definitely_have_n_bytes = data.open(1.kibibytes()).hint();
/// } /// }
/// ``` /// ```
@ -111,7 +112,7 @@ impl DataStream {
/// use std::io; /// use std::io;
/// use rocket::data::{Data, ToByteUnit}; /// use rocket::data::{Data, ToByteUnit};
/// ///
/// async fn data_guard(mut data: Data) -> io::Result<String> { /// async fn data_guard(mut data: Data<'_>) -> io::Result<String> {
/// // write all of the data to stdout /// // write all of the data to stdout
/// let written = data.open(512.kibibytes()) /// let written = data.open(512.kibibytes())
/// .stream_to(tokio::io::stdout()).await?; /// .stream_to(tokio::io::stdout()).await?;
@ -136,7 +137,7 @@ impl DataStream {
/// use std::io; /// use std::io;
/// use rocket::data::{Data, ToByteUnit}; /// use rocket::data::{Data, ToByteUnit};
/// ///
/// async fn data_guard(mut data: Data) -> io::Result<String> { /// async fn data_guard(mut data: Data<'_>) -> io::Result<String> {
/// // write all of the data to stdout /// // write all of the data to stdout
/// let written = data.open(512.kibibytes()) /// let written = data.open(512.kibibytes())
/// .stream_precise_to(tokio::io::stdout()).await?; /// .stream_precise_to(tokio::io::stdout()).await?;
@ -159,7 +160,7 @@ impl DataStream {
/// use std::io; /// use std::io;
/// use rocket::data::{Data, ToByteUnit}; /// use rocket::data::{Data, ToByteUnit};
/// ///
/// async fn data_guard(data: Data) -> io::Result<Vec<u8>> { /// async fn data_guard(data: Data<'_>) -> io::Result<Vec<u8>> {
/// let bytes = data.open(4.kibibytes()).into_bytes().await?; /// let bytes = data.open(4.kibibytes()).into_bytes().await?;
/// if !bytes.is_complete() { /// if !bytes.is_complete() {
/// println!("there are bytes remaining in the stream"); /// println!("there are bytes remaining in the stream");
@ -182,7 +183,7 @@ impl DataStream {
/// use std::io; /// use std::io;
/// use rocket::data::{Data, ToByteUnit}; /// use rocket::data::{Data, ToByteUnit};
/// ///
/// async fn data_guard(data: Data) -> io::Result<String> { /// async fn data_guard(data: Data<'_>) -> io::Result<String> {
/// let string = data.open(10.bytes()).into_string().await?; /// let string = data.open(10.bytes()).into_string().await?;
/// if !string.is_complete() { /// if !string.is_complete() {
/// println!("there are bytes remaining in the stream"); /// println!("there are bytes remaining in the stream");
@ -208,7 +209,7 @@ impl DataStream {
/// use std::io; /// use std::io;
/// use rocket::data::{Data, ToByteUnit}; /// use rocket::data::{Data, ToByteUnit};
/// ///
/// async fn data_guard(mut data: Data) -> io::Result<String> { /// async fn data_guard(mut data: Data<'_>) -> io::Result<String> {
/// let file = data.open(1.megabytes()).into_file("/static/file").await?; /// let file = data.open(1.megabytes()).into_file("/static/file").await?;
/// if !file.is_complete() { /// if !file.is_complete() {
/// println!("there are bytes remaining in the stream"); /// println!("there are bytes remaining in the stream");
@ -226,25 +227,25 @@ impl DataStream {
// TODO.async: Consider implementing `AsyncBufRead`. // TODO.async: Consider implementing `AsyncBufRead`.
impl StreamReader { impl StreamReader<'_> {
pub fn empty() -> Self { pub fn empty() -> Self {
Self { inner: StreamKind::Body(hyper::Body::empty()), state: State::Done } Self { inner: StreamKind::Empty, state: State::Done }
} }
} }
impl From<hyper::Body> for StreamReader { impl<'r> From<&'r mut hyper::Body> for StreamReader<'r> {
fn from(body: hyper::Body) -> Self { fn from(body: &'r mut hyper::Body) -> Self {
Self { inner: StreamKind::Body(body), state: State::Pending } Self { inner: StreamKind::Body(body), state: State::Pending }
} }
} }
impl From<multer::Field<'static>> for StreamReader { impl<'r> From<multer::Field<'r>> for StreamReader<'r> {
fn from(field: multer::Field<'static>) -> Self { fn from(field: multer::Field<'r>) -> Self {
Self { inner: StreamKind::Multipart(field), state: State::Pending } Self { inner: StreamKind::Multipart(field), state: State::Pending }
} }
} }
impl AsyncRead for DataStream { impl AsyncRead for DataStream<'_> {
#[inline(always)] #[inline(always)]
fn poll_read( fn poll_read(
mut self: Pin<&mut Self>, mut self: Pin<&mut Self>,
@ -255,7 +256,7 @@ impl AsyncRead for DataStream {
} }
} }
impl Stream for StreamKind { impl Stream for StreamKind<'_> {
type Item = io::Result<hyper::Bytes>; type Item = io::Result<hyper::Bytes>;
fn poll_next( fn poll_next(
@ -267,6 +268,7 @@ impl Stream for StreamKind {
.map_err_ext(|e| io::Error::new(io::ErrorKind::Other, e)), .map_err_ext(|e| io::Error::new(io::ErrorKind::Other, e)),
StreamKind::Multipart(mp) => Pin::new(mp).poll_next(cx) StreamKind::Multipart(mp) => Pin::new(mp).poll_next(cx)
.map_err_ext(|e| io::Error::new(io::ErrorKind::Other, e)), .map_err_ext(|e| io::Error::new(io::ErrorKind::Other, e)),
StreamKind::Empty => Poll::Ready(None),
} }
} }
@ -274,11 +276,12 @@ impl Stream for StreamKind {
match self { match self {
StreamKind::Body(body) => body.size_hint(), StreamKind::Body(body) => body.size_hint(),
StreamKind::Multipart(mp) => mp.size_hint(), StreamKind::Multipart(mp) => mp.size_hint(),
StreamKind::Empty => (0, Some(0)),
} }
} }
} }
impl AsyncRead for StreamReader { impl AsyncRead for StreamReader<'_> {
fn poll_read( fn poll_read(
mut self: Pin<&mut Self>, mut self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,

View File

@ -5,15 +5,16 @@ use crate::outcome::{self, IntoOutcome, try_outcome, Outcome::*};
/// Type alias for the `Outcome` of [`FromData`]. /// Type alias for the `Outcome` of [`FromData`].
/// ///
/// [`FromData`]: crate::data::FromData /// [`FromData`]: crTte::data::FromData
pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), Data>; pub type Outcome<'r, T, E = <T as FromData<'r>>::Error>
= outcome::Outcome<T, (Status, E), Data<'r>>;
impl<S, E> IntoOutcome<S, (Status, E), Data> for Result<S, E> { impl<'r, S, E> IntoOutcome<S, (Status, E), Data<'r>> for Result<S, E> {
type Failure = Status; type Failure = Status;
type Forward = Data; type Forward = Data<'r>;
#[inline] #[inline]
fn into_outcome(self, status: Status) -> Outcome<S, E> { fn into_outcome(self, status: Status) -> Outcome<'r, S, E> {
match self { match self {
Ok(val) => Success(val), Ok(val) => Success(val),
Err(err) => Failure((status, err)) Err(err) => Failure((status, err))
@ -21,7 +22,7 @@ impl<S, E> IntoOutcome<S, (Status, E), Data> for Result<S, E> {
} }
#[inline] #[inline]
fn or_forward(self, data: Data) -> Outcome<S, E> { fn or_forward(self, data: Data<'r>) -> Outcome<'r, S, E> {
match self { match self {
Ok(val) => Success(val), Ok(val) => Success(val),
Err(_) => Forward(data) Err(_) => Forward(data)
@ -41,7 +42,7 @@ impl<S, E> IntoOutcome<S, (Status, E), Data> for Result<S, E> {
/// ///
/// ```rust /// ```rust
/// # #[macro_use] extern crate rocket; /// # #[macro_use] extern crate rocket;
/// # type DataGuard = rocket::data::Data; /// # type DataGuard = String;
/// #[post("/submit", data = "<var>")] /// #[post("/submit", data = "<var>")]
/// fn submit(var: DataGuard) { /* ... */ } /// fn submit(var: DataGuard) { /* ... */ }
/// ``` /// ```
@ -66,7 +67,7 @@ impl<S, E> IntoOutcome<S, (Status, E), Data> for Result<S, E> {
/// impl<'r> FromData<'r> for MyType { /// impl<'r> FromData<'r> for MyType {
/// type Error = MyError; /// type Error = MyError;
/// ///
/// async fn from_data(req: &'r Request<'_>, data: Data) -> data::Outcome<Self, MyError> { /// async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
/// /* .. */ /// /* .. */
/// # unimplemented!() /// # unimplemented!()
/// } /// }
@ -122,7 +123,7 @@ impl<S, E> IntoOutcome<S, (Status, E), Data> for Result<S, E> {
/// impl<'r> FromData<'r> for Person<'r> { /// impl<'r> FromData<'r> for Person<'r> {
/// type Error = Error; /// type Error = Error;
/// ///
/// async fn from_data(req: &'r Request<'_>, data: Data) -> data::Outcome<Self, Error> { /// async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
/// use Error::*; /// use Error::*;
/// use rocket::outcome::Outcome::*; /// use rocket::outcome::Outcome::*;
/// ///
@ -190,7 +191,7 @@ pub trait FromData<'r>: Sized {
/// If validation and parsing succeeds, an outcome of `Success` is returned. /// If validation and parsing succeeds, an outcome of `Success` is returned.
/// If the data is not appropriate given the type of `Self`, `Forward` is /// If the data is not appropriate given the type of `Self`, `Forward` is
/// returned. If parsing fails, `Failure` is returned. /// returned. If parsing fails, `Failure` is returned.
async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome<Self, Self::Error>; async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self>;
} }
use crate::data::Capped; use crate::data::Capped;
@ -199,7 +200,7 @@ use crate::data::Capped;
impl<'r> FromData<'r> for Capped<String> { impl<'r> FromData<'r> for Capped<String> {
type Error = std::io::Error; type Error = std::io::Error;
async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome<Self, Self::Error> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
let limit = req.limits().get("string").unwrap_or(Limits::STRING); let limit = req.limits().get("string").unwrap_or(Limits::STRING);
data.open(limit).into_string().await.into_outcome(Status::BadRequest) data.open(limit).into_string().await.into_outcome(Status::BadRequest)
} }
@ -211,7 +212,7 @@ impl_strict_from_data_from_capped!(String);
impl<'r> FromData<'r> for Capped<&'r str> { impl<'r> FromData<'r> for Capped<&'r str> {
type Error = std::io::Error; type Error = std::io::Error;
async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome<Self, Self::Error> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
let capped = try_outcome!(<Capped<String>>::from_data(req, data).await); let capped = try_outcome!(<Capped<String>>::from_data(req, data).await);
let string = capped.map(|s| local_cache!(req, s).as_str()); let string = capped.map(|s| local_cache!(req, s).as_str());
Success(string) Success(string)
@ -224,7 +225,7 @@ impl_strict_from_data_from_capped!(&'r str);
impl<'r> FromData<'r> for Capped<&'r RawStr> { impl<'r> FromData<'r> for Capped<&'r RawStr> {
type Error = std::io::Error; type Error = std::io::Error;
async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome<Self, Self::Error> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
let capped = try_outcome!(<Capped<String>>::from_data(req, data).await); let capped = try_outcome!(<Capped<String>>::from_data(req, data).await);
let raw = capped.map(|s| RawStr::new(local_cache!(req, s))); let raw = capped.map(|s| RawStr::new(local_cache!(req, s)));
Success(raw) Success(raw)
@ -237,7 +238,7 @@ impl_strict_from_data_from_capped!(&'r RawStr);
impl<'r> FromData<'r> for Capped<std::borrow::Cow<'_, str>> { impl<'r> FromData<'r> for Capped<std::borrow::Cow<'_, str>> {
type Error = std::io::Error; type Error = std::io::Error;
async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome<Self, Self::Error> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
let capped = try_outcome!(<Capped<String>>::from_data(req, data).await); let capped = try_outcome!(<Capped<String>>::from_data(req, data).await);
Success(capped.map(|s| s.into())) Success(capped.map(|s| s.into()))
} }
@ -249,7 +250,7 @@ impl_strict_from_data_from_capped!(std::borrow::Cow<'_, str>);
impl<'r> FromData<'r> for Capped<&'r [u8]> { impl<'r> FromData<'r> for Capped<&'r [u8]> {
type Error = std::io::Error; type Error = std::io::Error;
async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome<Self, Self::Error> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
let capped = try_outcome!(<Capped<Vec<u8>>>::from_data(req, data).await); let capped = try_outcome!(<Capped<Vec<u8>>>::from_data(req, data).await);
let raw = capped.map(|b| local_cache!(req, b).as_slice()); let raw = capped.map(|b| local_cache!(req, b).as_slice());
Success(raw) Success(raw)
@ -262,7 +263,7 @@ impl_strict_from_data_from_capped!(&'r [u8]);
impl<'r> FromData<'r> for Capped<Vec<u8>> { impl<'r> FromData<'r> for Capped<Vec<u8>> {
type Error = std::io::Error; type Error = std::io::Error;
async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome<Self, Self::Error> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
let limit = req.limits().get("bytes").unwrap_or(Limits::BYTES); let limit = req.limits().get("bytes").unwrap_or(Limits::BYTES);
data.open(limit).into_bytes().await.into_outcome(Status::BadRequest) data.open(limit).into_bytes().await.into_outcome(Status::BadRequest)
} }
@ -271,10 +272,10 @@ impl<'r> FromData<'r> for Capped<Vec<u8>> {
impl_strict_from_data_from_capped!(Vec<u8>); impl_strict_from_data_from_capped!(Vec<u8>);
#[crate::async_trait] #[crate::async_trait]
impl<'r> FromData<'r> for Data { impl<'r> FromData<'r> for Data<'r> {
type Error = std::convert::Infallible; type Error = std::convert::Infallible;
async fn from_data(_: &'r Request<'_>, data: Data) -> Outcome<Self, Self::Error> { async fn from_data(_: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
Success(data) Success(data)
} }
} }
@ -283,10 +284,7 @@ impl<'r> FromData<'r> for Data {
impl<'r, T: FromData<'r> + 'r> FromData<'r> for Result<T, T::Error> { impl<'r, T: FromData<'r> + 'r> FromData<'r> for Result<T, T::Error> {
type Error = std::convert::Infallible; type Error = std::convert::Infallible;
async fn from_data( async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
req: &'r Request<'_>,
data: Data
) -> Outcome<Result<T, <T as FromData<'r>>::Error>, Self::Error> {
match T::from_data(req, data).await { match T::from_data(req, data).await {
Success(v) => Success(Ok(v)), Success(v) => Success(Ok(v)),
Failure((_, e)) => Success(Err(e)), Failure((_, e)) => Success(Err(e)),
@ -299,7 +297,7 @@ impl<'r, T: FromData<'r> + 'r> FromData<'r> for Result<T, T::Error> {
impl<'r, T: FromData<'r>> FromData<'r> for Option<T> { impl<'r, T: FromData<'r>> FromData<'r> for Option<T> {
type Error = std::convert::Infallible; type Error = std::convert::Infallible;
async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome<Self, Self::Error> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
match T::from_data(req, data).await { match T::from_data(req, data).await {
Success(v) => Success(Some(v)), Success(v) => Success(Some(v)),
Failure(..) | Forward(..) => Success(None), Failure(..) | Forward(..) => Success(None),

View File

@ -93,7 +93,7 @@ use crate::http::uncased::Uncased;
/// use rocket::data::{Data, Limits, ToByteUnit}; /// use rocket::data::{Data, Limits, ToByteUnit};
/// ///
/// #[post("/echo", data = "<data>")] /// #[post("/echo", data = "<data>")]
/// async fn echo(data: Data, limits: &Limits) -> io::Result<String> { /// async fn echo(data: Data<'_>, limits: &Limits) -> io::Result<String> {
/// let limit = limits.get("data").unwrap_or(1.mebibytes()); /// let limit = limits.get("data").unwrap_or(1.mebibytes());
/// Ok(data.open(limit).into_string().await?.value) /// Ok(data.open(limit).into_string().await?.value)
/// } /// }
@ -112,7 +112,7 @@ use crate::http::uncased::Uncased;
/// impl<'r> FromData<'r> for MyType { /// impl<'r> FromData<'r> for MyType {
/// type Error = MyError; /// type Error = MyError;
/// ///
/// async fn from_data(req: &'r Request<'_>, data: Data) -> data::Outcome<Self, MyError> { /// async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
/// let limit = req.limits().get("my-data-type"); /// let limit = req.limits().get("my-data-type");
/// /* .. */ /// /* .. */
/// # unimplemented!() /// # unimplemented!()

View File

@ -59,7 +59,7 @@ enum AdHocKind {
Liftoff(Once<dyn for<'a> FnOnce(&'a Rocket<Orbit>) -> BoxFuture<'a, ()> + Send + 'static>), Liftoff(Once<dyn for<'a> FnOnce(&'a Rocket<Orbit>) -> BoxFuture<'a, ()> + Send + 'static>),
/// An ad-hoc **request** fairing. Called when a request is received. /// An ad-hoc **request** fairing. Called when a request is received.
Request(Box<dyn for<'a> Fn(&'a mut Request<'_>, &'a Data) Request(Box<dyn for<'a> Fn(&'a mut Request<'_>, &'a Data<'_>)
-> BoxFuture<'a, ()> + Send + Sync + 'static>), -> BoxFuture<'a, ()> + Send + Sync + 'static>),
/// An ad-hoc **response** fairing. Called when a response is ready to be /// An ad-hoc **response** fairing. Called when a response is ready to be
@ -150,7 +150,7 @@ impl AdHoc {
/// }); /// });
/// ``` /// ```
pub fn on_request<F: Send + Sync + 'static>(name: &'static str, f: F) -> AdHoc pub fn on_request<F: Send + Sync + 'static>(name: &'static str, f: F) -> AdHoc
where F: for<'a> Fn(&'a mut Request<'_>, &'a Data) -> BoxFuture<'a, ()> where F: for<'a> Fn(&'a mut Request<'_>, &'a Data<'_>) -> BoxFuture<'a, ()>
{ {
AdHoc { name, kind: AdHocKind::Request(Box::new(f)) } AdHoc { name, kind: AdHocKind::Request(Box::new(f)) }
} }
@ -247,7 +247,7 @@ impl Fairing for AdHoc {
} }
} }
async fn on_request(&self, req: &mut Request<'_>, data: &mut Data) { async fn on_request(&self, req: &mut Request<'_>, data: &mut Data<'_>) {
if let AdHocKind::Request(ref f) = self.kind { if let AdHocKind::Request(ref f) = self.kind {
f(req, data).await f(req, data).await
} }

View File

@ -140,7 +140,7 @@ impl Fairings {
} }
#[inline(always)] #[inline(always)]
pub async fn handle_request(&self, req: &mut Request<'_>, data: &mut Data) { pub async fn handle_request(&self, req: &mut Request<'_>, data: &mut Data<'_>) {
for fairing in iter!(self.request) { for fairing in iter!(self.request) {
fairing.on_request(req, data).await fairing.on_request(req, data).await
} }

View File

@ -237,7 +237,7 @@ pub type Result<T = Rocket<Build>, E = Rocket<Build>> = std::result::Result<T, E
/// # unimplemented!() /// # unimplemented!()
/// } /// }
/// ///
/// async fn on_request(&self, req: &mut Request<'_>, data: &mut Data) { /// async fn on_request(&self, req: &mut Request<'_>, data: &mut Data<'_>) {
/// /* ... */ /// /* ... */
/// # unimplemented!() /// # unimplemented!()
/// } /// }
@ -286,7 +286,7 @@ pub type Result<T = Rocket<Build>, E = Rocket<Build>> = std::result::Result<T, E
/// } /// }
/// } /// }
/// ///
/// async fn on_request(&self, req: &mut Request<'_>, _: &mut Data) { /// async fn on_request(&self, req: &mut Request<'_>, _: &mut Data<'_>) {
/// if req.method() == Method::Get { /// if req.method() == Method::Get {
/// self.get.fetch_add(1, Ordering::Relaxed); /// self.get.fetch_add(1, Ordering::Relaxed);
/// } else if req.method() == Method::Post { /// } else if req.method() == Method::Post {
@ -349,7 +349,7 @@ pub type Result<T = Rocket<Build>, E = Rocket<Build>> = std::result::Result<T, E
/// } /// }
/// ///
/// /// Stores the start time of the request in request-local state. /// /// Stores the start time of the request in request-local state.
/// async fn on_request(&self, request: &mut Request<'_>, _: &mut Data) { /// async fn on_request(&self, request: &mut Request<'_>, _: &mut Data<'_>) {
/// // Store a `TimerStart` instead of directly storing a `SystemTime` /// // Store a `TimerStart` instead of directly storing a `SystemTime`
/// // to ensure that this usage doesn't conflict with anything else /// // to ensure that this usage doesn't conflict with anything else
/// // that might store a `SystemTime` in request-local cache. /// // that might store a `SystemTime` in request-local cache.
@ -457,7 +457,7 @@ pub trait Fairing: Send + Sync + Any + 'static {
/// ## Default Implementation /// ## Default Implementation
/// ///
/// The default implementation of this method does nothing. /// The default implementation of this method does nothing.
async fn on_request(&self, _req: &mut Request<'_>, _data: &mut Data) {} async fn on_request(&self, _req: &mut Request<'_>, _data: &mut Data<'_>) {}
/// The response callback. /// The response callback.
/// ///
@ -490,7 +490,7 @@ impl<T: Fairing> Fairing for std::sync::Arc<T> {
} }
#[inline] #[inline]
async fn on_request(&self, req: &mut Request<'_>, data: &mut Data) { async fn on_request(&self, req: &mut Request<'_>, data: &mut Data<'_>) {
(self as &T).on_request(req, data).await (self as &T).on_request(req, data).await
} }

View File

@ -804,11 +804,19 @@ impl fmt::Display for ErrorKind<'_> {
match self { match self {
ErrorKind::InvalidLength { min, max } => { ErrorKind::InvalidLength { min, max } => {
match (min, max) { match (min, max) {
(None, None) => write!(f, "unexpected or incomplete")?, (None, None) => write!(f, "invalid length: incomplete")?,
(None, Some(k)) => write!(f, "length cannot exceed {}", k)?, (None, Some(k)) if *k < 1024 => write!(f, "length cannot exceed {}", k)?,
(Some(1), None) => write!(f, "value cannot be empty")?, (None, Some(k)) => write!(f, "size must not exceed {}", ByteUnit::from(*k))?,
(Some(k), None) => write!(f, "length must be at least {}", k)?, (Some(1), None) => write!(f, "cannot be empty")?,
(Some(i), Some(j)) => write!(f, "length must be between {} and {}", i, j)?, (Some(k), None) if *k < 1024 => write!(f, "expected at least {}", k)?,
(Some(k), None) => write!(f, "size must be at least {}", ByteUnit::from(*k))?,
(Some(i), Some(j)) if *i < 1024 && *j < 1024 => {
write!(f, "length must be between {} and {}", i, j)?;
}
(Some(i), Some(j)) => {
let (i, j) = (ByteUnit::from(*i), ByteUnit::from(*j));
write!(f, "size must be between {} and {}", i, j)?;
}
} }
} }
ErrorKind::InvalidChoice { choices } => { ErrorKind::InvalidChoice { choices } => {

View File

@ -33,7 +33,7 @@ pub struct DataField<'r, 'i> {
/// The request in which the form field was submitted. /// The request in which the form field was submitted.
pub request: &'r Request<'i>, pub request: &'r Request<'i>,
/// The raw data stream. /// The raw data stream.
pub data: Data, pub data: Data<'r>,
} }
impl<'v> ValueField<'v> { impl<'v> ValueField<'v> {

View File

@ -299,7 +299,7 @@ impl<T> DerefMut for Form<T> {
impl<'r, T: FromForm<'r>> FromData<'r> for Form<T> { impl<'r, T: FromForm<'r>> FromData<'r> for Form<T> {
type Error = Errors<'r>; type Error = Errors<'r>;
async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome<Self, Self::Error> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
use either::Either; use either::Either;
let mut parser = try_outcome!(Parser::new(req, data).await); let mut parser = try_outcome!(Parser::new(req, data).await);

View File

@ -21,7 +21,7 @@ pub struct Buffer {
pub struct MultipartParser<'r, 'i> { pub struct MultipartParser<'r, 'i> {
request: &'r Request<'i>, request: &'r Request<'i>,
buffer: &'r Buffer, buffer: &'r Buffer,
source: Multipart<'static>, source: Multipart<'r>,
done: bool, done: bool,
} }
@ -36,7 +36,10 @@ pub enum Parser<'r, 'i> {
} }
impl<'r, 'i> Parser<'r, 'i> { impl<'r, 'i> Parser<'r, 'i> {
pub async fn new(req: &'r Request<'i>, data: Data) -> Outcome<Parser<'r, 'i>, Errors<'r>> { pub async fn new(
req: &'r Request<'i>,
data: Data<'r>
) -> Outcome<'r, Parser<'r, 'i>, Errors<'r>> {
let parser = match req.content_type() { let parser = match req.content_type() {
Some(c) if c.is_form() => Self::from_form(req, data).await, Some(c) if c.is_form() => Self::from_form(req, data).await,
Some(c) if c.is_form_data() => Self::from_multipart(req, data).await, Some(c) if c.is_form_data() => Self::from_multipart(req, data).await,
@ -49,11 +52,11 @@ impl<'r, 'i> Parser<'r, 'i> {
} }
} }
async fn from_form(req: &'r Request<'i>, data: Data) -> Result<'r, Parser<'r, 'i>> { async fn from_form(req: &'r Request<'i>, data: Data<'r>) -> Result<'r, Parser<'r, 'i>> {
let limit = req.limits().get("form").unwrap_or(Limits::FORM); let limit = req.limits().get("form").unwrap_or(Limits::FORM);
let string = data.open(limit).into_string().await?; let string = data.open(limit).into_string().await?;
if !string.is_complete() { if !string.is_complete() {
Err((None, Some(limit.as_u64())))? Err((None, Some(limit.as_u64())))?;
} }
Ok(Parser::RawStr(RawStrParser { Ok(Parser::RawStr(RawStrParser {
@ -62,7 +65,7 @@ impl<'r, 'i> Parser<'r, 'i> {
})) }))
} }
async fn from_multipart(req: &'r Request<'i>, data: Data) -> Result<'r, Parser<'r, 'i>> { async fn from_multipart(req: &'r Request<'i>, data: Data<'r>) -> Result<'r, Parser<'r, 'i>> {
let boundary = req.content_type() let boundary = req.content_type()
.ok_or(multer::Error::NoMultipart)? .ok_or(multer::Error::NoMultipart)?
.param("boundary") .param("boundary")

View File

@ -184,7 +184,7 @@ impl Into<Vec<Route>> for FileServer {
#[crate::async_trait] #[crate::async_trait]
impl Handler for FileServer { impl Handler for FileServer {
async fn handle<'r>(&self, req: &'r Request<'_>, data: Data) -> Outcome<'r> { async fn handle<'r>(&self, req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r> {
use crate::http::uri::fmt::Path; use crate::http::uri::fmt::Path;
// Get the segments as a `PathBuf`, allowing dotfiles requested. // Get the segments as a `PathBuf`, allowing dotfiles requested.

View File

@ -3,7 +3,7 @@ use std::path::{PathBuf, Path};
use crate::Request; use crate::Request;
use crate::http::{ContentType, Status}; use crate::http::{ContentType, Status};
use crate::data::{FromData, Data, Capped, N, Limits}; use crate::data::{self, FromData, Data, Capped, N, Limits};
use crate::form::{FromFormField, ValueField, DataField, error::Errors}; use crate::form::{FromFormField, ValueField, DataField, error::Errors};
use crate::outcome::IntoOutcome; use crate::outcome::IntoOutcome;
use crate::fs::FileName; use crate::fs::FileName;
@ -441,7 +441,7 @@ impl<'v> TempFile<'v> {
async fn from<'a>( async fn from<'a>(
req: &Request<'_>, req: &Request<'_>,
data: Data, data: Data<'_>,
file_name: Option<&'a FileName>, file_name: Option<&'a FileName>,
content_type: Option<ContentType>, content_type: Option<ContentType>,
) -> io::Result<Capped<TempFile<'a>>> { ) -> io::Result<Capped<TempFile<'a>>> {
@ -489,10 +489,7 @@ impl<'v> FromFormField<'v> for Capped<TempFile<'v>> {
impl<'r> FromData<'r> for Capped<TempFile<'_>> { impl<'r> FromData<'r> for Capped<TempFile<'_>> {
type Error = io::Error; type Error = io::Error;
async fn from_data( async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
req: &'r crate::Request<'_>,
data: crate::Data
) -> crate::data::Outcome<Self, Self::Error> {
use yansi::Paint; use yansi::Paint;
let has_form = |ty: &ContentType| ty.is_form_data() || ty.is_form(); let has_form = |ty: &ContentType| ty.is_form_data() || ty.is_form();

View File

@ -26,7 +26,7 @@
//! //!
//! ```rust //! ```rust
//! # use rocket::post; //! # use rocket::post;
//! # type S = rocket::data::Data; //! # type S = String;
//! #[post("/", data = "<my_val>")] //! #[post("/", data = "<my_val>")]
//! fn hello(my_val: S) { /* ... */ } //! fn hello(my_val: S) { /* ... */ }
//! ``` //! ```
@ -49,7 +49,7 @@
//! //!
//! ```rust //! ```rust
//! # use rocket::post; //! # use rocket::post;
//! # type S = rocket::data::Data; //! # type S = Option<String>;
//! # type E = std::convert::Infallible; //! # type E = std::convert::Infallible;
//! #[post("/", data = "<my_val>")] //! #[post("/", data = "<my_val>")]
//! fn hello(my_val: Result<S, E>) { /* ... */ } //! fn hello(my_val: Result<S, E>) { /* ... */ }
@ -73,7 +73,7 @@
//! //!
//! ```rust //! ```rust
//! # use rocket::post; //! # use rocket::post;
//! # type S = rocket::data::Data; //! # type S = String;
//! #[post("/", data = "<my_val>")] //! #[post("/", data = "<my_val>")]
//! fn hello(my_val: S) { /* ... */ } //! fn hello(my_val: S) { /* ... */ }
//! ``` //! ```

View File

@ -290,7 +290,7 @@ impl Rocket<Build> {
/// use rocket::{Request, Route, Data, route}; /// use rocket::{Request, Route, Data, route};
/// use rocket::http::Method; /// use rocket::http::Method;
/// ///
/// fn hi<'r>(req: &'r Request, _: Data) -> route::BoxFuture<'r> { /// fn hi<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> {
/// route::Outcome::from(req, "Hello!").pin() /// route::Outcome::from(req, "Hello!").pin()
/// } /// }
/// ///

View File

@ -4,7 +4,7 @@ use crate::http::Status;
/// Type alias for the return type of a [`Route`](crate::Route)'s /// Type alias for the return type of a [`Route`](crate::Route)'s
/// [`Handler::handle()`]. /// [`Handler::handle()`].
pub type Outcome<'r> = crate::outcome::Outcome<Response<'r>, Status, Data>; pub type Outcome<'r> = crate::outcome::Outcome<Response<'r>, Status, Data<'r>>;
/// Type alias for the return type of a _raw_ [`Route`](crate::Route)'s /// Type alias for the return type of a _raw_ [`Route`](crate::Route)'s
/// [`Handler`]. /// [`Handler`].
@ -53,7 +53,7 @@ pub type BoxFuture<'r, T = Outcome<'r>> = futures::future::BoxFuture<'r, T>;
/// ///
/// #[rocket::async_trait] /// #[rocket::async_trait]
/// impl Handler for CustomHandler { /// impl Handler for CustomHandler {
/// async fn handle<'r>(&self, req: &'r Request<'_>, data: Data) -> Outcome<'r> { /// async fn handle<'r>(&self, req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r> {
/// match self.0 { /// match self.0 {
/// Kind::Simple => Outcome::from(req, "simple"), /// Kind::Simple => Outcome::from(req, "simple"),
/// Kind::Intermediate => Outcome::from(req, "intermediate"), /// Kind::Intermediate => Outcome::from(req, "intermediate"),
@ -145,18 +145,18 @@ pub trait Handler: Cloneable + Send + Sync + 'static {
/// generate a response. Otherwise, if the return value is `Forward(Data)`, /// generate a response. Otherwise, if the return value is `Forward(Data)`,
/// the next matching route is attempted. If there are no other matching /// the next matching route is attempted. If there are no other matching
/// routes, the `404` error catcher is invoked. /// routes, the `404` error catcher is invoked.
async fn handle<'r>(&self, request: &'r Request<'_>, data: Data) -> Outcome<'r>; async fn handle<'r>(&self, request: &'r Request<'_>, data: Data<'r>) -> Outcome<'r>;
} }
// We write this manually to avoid double-boxing. // We write this manually to avoid double-boxing.
impl<F: Clone + Sync + Send + 'static> Handler for F impl<F: Clone + Sync + Send + 'static> Handler for F
where for<'x> F: Fn(&'x Request<'_>, Data) -> BoxFuture<'x>, where for<'x> F: Fn(&'x Request<'_>, Data<'x>) -> BoxFuture<'x>,
{ {
#[inline(always)] #[inline(always)]
fn handle<'r, 'life0, 'life1, 'async_trait>( fn handle<'r, 'life0, 'life1, 'async_trait>(
&'life0 self, &'life0 self,
req: &'r Request<'life1>, req: &'r Request<'life1>,
data: Data, data: Data<'r>,
) -> BoxFuture<'r> ) -> BoxFuture<'r>
where 'r: 'async_trait, where 'r: 'async_trait,
'life0: 'async_trait, 'life0: 'async_trait,
@ -167,6 +167,7 @@ impl<F: Clone + Sync + Send + 'static> Handler for F
} }
} }
// FIXME!
impl<'r, 'o: 'r> Outcome<'o> { impl<'r, 'o: 'r> Outcome<'o> {
/// Return the `Outcome` of response to `req` from `responder`. /// Return the `Outcome` of response to `req` from `responder`.
/// ///
@ -179,12 +180,12 @@ impl<'r, 'o: 'r> Outcome<'o> {
/// ```rust /// ```rust
/// use rocket::{Request, Data, route}; /// use rocket::{Request, Data, route};
/// ///
/// fn str_responder<'r>(req: &'r Request, _: Data) -> route::Outcome<'r> { /// fn str_responder<'r>(req: &'r Request, _: Data<'r>) -> route::Outcome<'r> {
/// route::Outcome::from(req, "Hello, world!") /// route::Outcome::from(req, "Hello, world!")
/// } /// }
/// ``` /// ```
#[inline] #[inline]
pub fn from<R: Responder<'r, 'o>>(req: &'r Request<'_>, responder: R) -> Outcome<'o> { pub fn from<R: Responder<'r, 'o>>(req: &'r Request<'_>, responder: R) -> Outcome<'r> {
match responder.respond_to(req) { match responder.respond_to(req) {
Ok(response) => Outcome::Success(response), Ok(response) => Outcome::Success(response),
Err(status) => Outcome::Failure(status) Err(status) => Outcome::Failure(status)
@ -202,12 +203,12 @@ impl<'r, 'o: 'r> Outcome<'o> {
/// ```rust /// ```rust
/// use rocket::{Request, Data, route}; /// use rocket::{Request, Data, route};
/// ///
/// fn str_responder<'r>(req: &'r Request, _: Data) -> route::Outcome<'r> { /// fn str_responder<'r>(req: &'r Request, _: Data<'r>) -> route::Outcome<'r> {
/// route::Outcome::from(req, "Hello, world!") /// route::Outcome::from(req, "Hello, world!")
/// } /// }
/// ``` /// ```
#[inline] #[inline]
pub fn try_from<R, E>(req: &'r Request<'_>, result: Result<R, E>) -> Outcome<'o> pub fn try_from<R, E>(req: &'r Request<'_>, result: Result<R, E>) -> Outcome<'r>
where R: Responder<'r, 'o>, E: std::fmt::Debug where R: Responder<'r, 'o>, E: std::fmt::Debug
{ {
let responder = result.map_err(crate::response::Debug); let responder = result.map_err(crate::response::Debug);
@ -228,12 +229,12 @@ impl<'r, 'o: 'r> Outcome<'o> {
/// ```rust /// ```rust
/// use rocket::{Request, Data, route}; /// use rocket::{Request, Data, route};
/// ///
/// fn str_responder<'r>(req: &'r Request, data: Data) -> route::Outcome<'r> { /// fn str_responder<'r>(req: &'r Request, data: Data<'r>) -> route::Outcome<'r> {
/// route::Outcome::from_or_forward(req, data, "Hello, world!") /// route::Outcome::from_or_forward(req, data, "Hello, world!")
/// } /// }
/// ``` /// ```
#[inline] #[inline]
pub fn from_or_forward<R>(req: &'r Request<'_>, data: Data, responder: R) -> Outcome<'o> pub fn from_or_forward<R>(req: &'r Request<'_>, data: Data<'r>, responder: R) -> Outcome<'r>
where R: Responder<'r, 'o> where R: Responder<'r, 'o>
{ {
match responder.respond_to(req) { match responder.respond_to(req) {
@ -253,12 +254,12 @@ impl<'r, 'o: 'r> Outcome<'o> {
/// use rocket::{Request, Data, route}; /// use rocket::{Request, Data, route};
/// use rocket::http::Status; /// use rocket::http::Status;
/// ///
/// fn bad_req_route<'r>(_: &'r Request, _: Data) -> route::Outcome<'r> { /// fn bad_req_route<'r>(_: &'r Request, _: Data<'r>) -> route::Outcome<'r> {
/// route::Outcome::failure(Status::BadRequest) /// route::Outcome::failure(Status::BadRequest)
/// } /// }
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn failure(code: Status) -> Outcome<'static> { pub fn failure(code: Status) -> Outcome<'r> {
Outcome::Failure(code) Outcome::Failure(code)
} }
@ -272,19 +273,19 @@ impl<'r, 'o: 'r> Outcome<'o> {
/// ```rust /// ```rust
/// use rocket::{Request, Data, route}; /// use rocket::{Request, Data, route};
/// ///
/// fn always_forward<'r>(_: &'r Request, data: Data) -> route::Outcome<'r> { /// fn always_forward<'r>(_: &'r Request, data: Data<'r>) -> route::Outcome<'r> {
/// route::Outcome::forward(data) /// route::Outcome::forward(data)
/// } /// }
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn forward(data: Data) -> Outcome<'static> { pub fn forward(data: Data<'r>) -> Outcome<'r> {
Outcome::Forward(data) Outcome::Forward(data)
} }
} }
// INTERNAL: A handler to use when one is needed temporarily. // INTERNAL: A handler to use when one is needed temporarily.
#[doc(hidden)] #[doc(hidden)]
pub fn dummy_handler<'r>(r: &'r Request<'_>, _: Data) -> BoxFuture<'r> { pub fn dummy_handler<'r>(r: &'r Request<'_>, _: Data<'r>) -> BoxFuture<'r> {
Outcome::from(r, ()).pin() Outcome::from(r, ()).pin()
} }

View File

@ -339,7 +339,7 @@ pub struct StaticInfo {
/// The route's format, if any. /// The route's format, if any.
pub format: Option<MediaType>, pub format: Option<MediaType>,
/// The route's handler, i.e, the annotated function. /// The route's handler, i.e, the annotated function.
pub handler: for<'r> fn(&'r crate::Request<'_>, crate::Data) -> BoxFuture<'r>, pub handler: for<'r> fn(&'r crate::Request<'_>, crate::Data<'r>) -> BoxFuture<'r>,
/// The route's rank, if any. /// The route's rank, if any.
pub rank: Option<isize>, pub rank: Option<isize>,
/// Route-derived sentinels, if any. /// Route-derived sentinels, if any.

View File

@ -158,7 +158,7 @@ impl<'r, T: Deserialize<'r>> Json<T> {
serde_json::from_str(s).map(Json).map_err(|e| Error::Parse(s, e)) serde_json::from_str(s).map(Json).map_err(|e| Error::Parse(s, e))
} }
async fn from_data(req: &'r Request<'_>, data: Data) -> Result<Self, Error<'r>> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Result<Self, Error<'r>> {
let limit = req.limits().get("json").unwrap_or(Limits::JSON); let limit = req.limits().get("json").unwrap_or(Limits::JSON);
let string = match data.open(limit).into_string().await { let string = match data.open(limit).into_string().await {
Ok(s) if s.is_complete() => s.into_inner(), Ok(s) if s.is_complete() => s.into_inner(),
@ -177,7 +177,7 @@ impl<'r, T: Deserialize<'r>> Json<T> {
impl<'r, T: Deserialize<'r>> FromData<'r> for Json<T> { impl<'r, T: Deserialize<'r>> FromData<'r> for Json<T> {
type Error = Error<'r>; type Error = Error<'r>;
async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome<Self, Self::Error> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
match Self::from_data(req, data).await { match Self::from_data(req, data).await {
Ok(value) => Outcome::Success(value), Ok(value) => Outcome::Success(value),
Err(Error::Io(e)) if e.kind() == io::ErrorKind::UnexpectedEof => { Err(Error::Io(e)) if e.kind() == io::ErrorKind::UnexpectedEof => {

View File

@ -146,7 +146,7 @@ impl<'r, T: Deserialize<'r>> MsgPack<T> {
rmp_serde::from_slice(buf).map(MsgPack) rmp_serde::from_slice(buf).map(MsgPack)
} }
async fn from_data(req: &'r Request<'_>, data: Data) -> Result<Self, Error> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Result<Self, Error> {
let limit = req.limits().get("msgpack").unwrap_or(Limits::MESSAGE_PACK); let limit = req.limits().get("msgpack").unwrap_or(Limits::MESSAGE_PACK);
let bytes = match data.open(limit).into_bytes().await { let bytes = match data.open(limit).into_bytes().await {
Ok(buf) if buf.is_complete() => buf.into_inner(), Ok(buf) if buf.is_complete() => buf.into_inner(),
@ -165,7 +165,7 @@ impl<'r, T: Deserialize<'r>> MsgPack<T> {
impl<'r, T: Deserialize<'r>> FromData<'r> for MsgPack<T> { impl<'r, T: Deserialize<'r>> FromData<'r> for MsgPack<T> {
type Error = Error; type Error = Error;
async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome<Self, Self::Error> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
match Self::from_data(req, data).await { match Self::from_data(req, data).await {
Ok(value) => Outcome::Success(value), Ok(value) => Outcome::Success(value),
Err(Error::InvalidDataRead(e)) if e.kind() == io::ErrorKind::UnexpectedEof => { Err(Error::InvalidDataRead(e)) if e.kind() == io::ErrorKind::UnexpectedEof => {

View File

@ -73,7 +73,7 @@ async fn hyper_service_fn(
tokio::spawn(async move { tokio::spawn(async move {
// Convert a Hyper request into a Rocket request. // Convert a Hyper request into a Rocket request.
let (h_parts, h_body) = hyp_req.into_parts(); let (h_parts, mut h_body) = hyp_req.into_parts();
let mut req = match Request::from_hyp(&rocket, &h_parts, addr) { let mut req = match Request::from_hyp(&rocket, &h_parts, addr) {
Ok(req) => req, Ok(req) => req,
Err(e) => { Err(e) => {
@ -89,7 +89,7 @@ async fn hyper_service_fn(
}; };
// Retrieve the data from the hyper body. // Retrieve the data from the hyper body.
let mut data = Data::from(h_body); let mut data = Data::from(&mut h_body);
// Dispatch the request to get a response, then write that response out. // Dispatch the request to get a response, then write that response out.
let token = rocket.preprocess_request(&mut req, &mut data).await; let token = rocket.preprocess_request(&mut req, &mut data).await;
@ -168,7 +168,7 @@ impl Rocket<Orbit> {
pub(crate) async fn preprocess_request( pub(crate) async fn preprocess_request(
&self, &self,
req: &mut Request<'_>, req: &mut Request<'_>,
data: &mut Data data: &mut Data<'_>
) -> RequestToken { ) -> RequestToken {
// Check if this is a form and if the form contains the special _method // Check if this is a form and if the form contains the special _method
// field which we use to reinterpret the request's method. // field which we use to reinterpret the request's method.
@ -198,7 +198,7 @@ impl Rocket<Orbit> {
&'s self, &'s self,
_token: RequestToken, _token: RequestToken,
request: &'r Request<'s>, request: &'r Request<'s>,
data: Data data: Data<'r>
) -> Response<'r> { ) -> Response<'r> {
info!("{}:", request); info!("{}:", request);
@ -228,7 +228,7 @@ impl Rocket<Orbit> {
async fn route_and_process<'s, 'r: 's>( async fn route_and_process<'s, 'r: 's>(
&'s self, &'s self,
request: &'r Request<'s>, request: &'r Request<'s>,
data: Data data: Data<'r>
) -> Response<'r> { ) -> Response<'r> {
let mut response = match self.route(request, data).await { let mut response = match self.route(request, data).await {
Outcome::Success(response) => response, Outcome::Success(response) => response,
@ -266,7 +266,7 @@ impl Rocket<Orbit> {
async fn route<'s, 'r: 's>( async fn route<'s, 'r: 's>(
&'s self, &'s self,
request: &'r Request<'s>, request: &'r Request<'s>,
mut data: Data, mut data: Data<'r>,
) -> route::Outcome<'r> { ) -> route::Outcome<'r> {
// Go through the list of matching routes until we fail or succeed. // Go through the list of matching routes until we fail or succeed.
for route in self.router.route(request) { for route in self.router.route(request) {

View File

@ -21,7 +21,7 @@ use rocket::data::{self, FromData};
impl<'r> FromData<'r> for HasContentType { impl<'r> FromData<'r> for HasContentType {
type Error = (); type Error = ();
async fn from_data(req: &'r Request<'_>, data: Data) -> data::Outcome<Self, ()> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
req.content_type().map(|_| HasContentType).or_forward(data) req.content_type().map(|_| HasContentType).or_forward(data)
} }
} }

View File

@ -20,7 +20,7 @@ fn ise() -> &'static str {
"Hey, sorry! :(" "Hey, sorry! :("
} }
fn pre_future_route<'r>(_: &'r Request<'_>, _: Data) -> route::BoxFuture<'r> { fn pre_future_route<'r>(_: &'r Request<'_>, _: Data<'r>) -> route::BoxFuture<'r> {
panic!("hey now..."); panic!("hey now...");
} }

View File

@ -2,10 +2,10 @@ use rocket::{Rocket, Build};
use rocket::{fairing::AdHoc, http::ContentType, local::blocking::Client}; use rocket::{fairing::AdHoc, http::ContentType, local::blocking::Client};
#[rocket::post("/", data = "<_data>", format = "json")] #[rocket::post("/", data = "<_data>", format = "json")]
fn index(_data: rocket::Data) -> &'static str { "json" } fn index(_data: rocket::Data<'_>) -> &'static str { "json" }
#[rocket::post("/", data = "<_data>", rank = 2)] #[rocket::post("/", data = "<_data>", rank = 2)]
fn other_index(_data: rocket::Data) -> &'static str { "other" } fn other_index(_data: rocket::Data<'_>) -> &'static str { "other" }
fn rocket() -> Rocket<Build> { fn rocket() -> Rocket<Build> {
rocket::build() rocket::build()

View File

@ -38,7 +38,7 @@ impl Fairing for Counter {
Ok(rocket.manage(self.clone()).mount("/", routes![counts])) Ok(rocket.manage(self.clone()).mount("/", routes![counts]))
} }
async fn on_request(&self, request: &mut Request<'_>, _: &mut Data) { async fn on_request(&self, request: &mut Request<'_>, _: &mut Data<'_>) {
if request.method() == Method::Get { if request.method() == Method::Get {
self.get.fetch_add(1, Ordering::Relaxed); self.get.fetch_add(1, Ordering::Relaxed);
} else if request.method() == Method::Post { } else if request.method() == Method::Post {

View File

@ -10,15 +10,15 @@ use rocket::response::{Responder, status::Custom};
use rocket::outcome::{try_outcome, IntoOutcome}; use rocket::outcome::{try_outcome, IntoOutcome};
use rocket::tokio::fs::File; use rocket::tokio::fs::File;
fn forward<'r>(_req: &'r Request, data: Data) -> route::BoxFuture<'r> { fn forward<'r>(_req: &'r Request, data: Data<'r>) -> route::BoxFuture<'r> {
Box::pin(async move { route::Outcome::forward(data) }) Box::pin(async move { route::Outcome::forward(data) })
} }
fn hi<'r>(req: &'r Request, _: Data) -> route::BoxFuture<'r> { fn hi<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> {
route::Outcome::from(req, "Hello!").pin() route::Outcome::from(req, "Hello!").pin()
} }
fn name<'a>(req: &'a Request, _: Data) -> route::BoxFuture<'a> { fn name<'a>(req: &'a Request, _: Data<'r>) -> route::BoxFuture<'a> {
let param = req.param::<&'a str>(0) let param = req.param::<&'a str>(0)
.and_then(|res| res.ok()) .and_then(|res| res.ok())
.unwrap_or("unnamed".into()); .unwrap_or("unnamed".into());
@ -26,7 +26,7 @@ fn name<'a>(req: &'a Request, _: Data) -> route::BoxFuture<'a> {
route::Outcome::from(req, param).pin() route::Outcome::from(req, param).pin()
} }
fn echo_url<'r>(req: &'r Request, _: Data) -> route::BoxFuture<'r> { fn echo_url<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> {
let param_outcome = req.param::<&str>(1) let param_outcome = req.param::<&str>(1)
.and_then(|res| res.ok()) .and_then(|res| res.ok())
.into_outcome(Status::BadRequest); .into_outcome(Status::BadRequest);
@ -36,7 +36,7 @@ fn echo_url<'r>(req: &'r Request, _: Data) -> route::BoxFuture<'r> {
}) })
} }
fn upload<'r>(req: &'r Request, data: Data) -> route::BoxFuture<'r> { fn upload<'r>(req: &'r Request, data: Data<'r>) -> route::BoxFuture<'r> {
Box::pin(async move { Box::pin(async move {
if !req.content_type().map_or(false, |ct| ct.is_plain()) { if !req.content_type().map_or(false, |ct| ct.is_plain()) {
println!(" => Content-Type of upload must be text/plain. Ignoring."); println!(" => Content-Type of upload must be text/plain. Ignoring.");
@ -58,7 +58,7 @@ fn upload<'r>(req: &'r Request, data: Data) -> route::BoxFuture<'r> {
}) })
} }
fn get_upload<'r>(req: &'r Request, _: Data) -> route::BoxFuture<'r> { fn get_upload<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> {
route::Outcome::from(req, std::fs::File::open(env::temp_dir().join("upload.txt")).ok()).pin() route::Outcome::from(req, std::fs::File::open(env::temp_dir().join("upload.txt")).ok()).pin()
} }
@ -80,7 +80,7 @@ impl CustomHandler {
#[rocket::async_trait] #[rocket::async_trait]
impl route::Handler for CustomHandler { impl route::Handler for CustomHandler {
async fn handle<'r>(&self, req: &'r Request<'_>, data: Data) -> route::Outcome<'r> { async fn handle<'r>(&self, req: &'r Request<'_>, data: Data<'r>) -> route::Outcome<'r> {
let self_data = self.data; let self_data = self.data;
let id = req.param::<&str>(0) let id = req.param::<&str>(0)
.and_then(|res| res.ok()) .and_then(|res| res.ok())

View File

@ -17,7 +17,7 @@ const HOST: Absolute<'static> = uri!("http://localhost:8000");
const ID_LENGTH: usize = 3; const ID_LENGTH: usize = 3;
#[post("/", data = "<paste>")] #[post("/", data = "<paste>")]
async fn upload(paste: Data) -> io::Result<String> { async fn upload(paste: Data<'_>) -> io::Result<String> {
let id = PasteId::new(ID_LENGTH); let id = PasteId::new(ID_LENGTH);
paste.open(128.kibibytes()).into_file(id.file_path()).await?; paste.open(128.kibibytes()).into_file(id.file_path()).await?;
Ok(uri!(HOST, retrieve(id)).to_string()) Ok(uri!(HOST, retrieve(id)).to_string())

View File

@ -224,7 +224,7 @@ use rocket::Data;
use rocket::response::Debug; use rocket::response::Debug;
#[post("/", data = "<paste>")] #[post("/", data = "<paste>")]
fn upload(paste: Data) -> std::io::Result<String> { fn upload(paste: Data<'_>) -> std::io::Result<String> {
# unimplemented!() # unimplemented!()
/* .. */ /* .. */
} }
@ -255,7 +255,7 @@ use rocket::response::Debug;
use rocket::data::{Data, ToByteUnit}; use rocket::data::{Data, ToByteUnit};
#[post("/", data = "<paste>")] #[post("/", data = "<paste>")]
async fn upload(paste: Data) -> Result<String, Debug<std::io::Error>> { async fn upload(paste: Data<'_>) -> Result<String, Debug<std::io::Error>> {
let id = PasteId::new(3); let id = PasteId::new(3);
let filename = format!("upload/{id}", id = id); let filename = format!("upload/{id}", id = id);
let url = format!("{host}/{id}\n", host = "http://localhost:8000", id = id); let url = format!("{host}/{id}\n", host = "http://localhost:8000", id = id);

View File

@ -598,7 +598,7 @@ As an example, consider the following route:
# #[macro_use] extern crate rocket; # #[macro_use] extern crate rocket;
# fn main() {} # fn main() {}
# type User = rocket::data::Data; # type User = String;
#[post("/user", format = "application/json", data = "<user>")] #[post("/user", format = "application/json", data = "<user>")]
fn new_user(user: User) { /* ... */ } fn new_user(user: User) { /* ... */ }
@ -647,7 +647,7 @@ trait. It looks like this, where `T` is assumed to implement `FromData`:
```rust ```rust
# #[macro_use] extern crate rocket; # #[macro_use] extern crate rocket;
# type T = rocket::data::Data; # type T = String;
#[post("/", data = "<input>")] #[post("/", data = "<input>")]
fn new(input: T) { /* .. */ } fn new(input: T) { /* .. */ }
@ -715,7 +715,7 @@ use rocket::tokio;
use rocket::data::{Data, ToByteUnit}; use rocket::data::{Data, ToByteUnit};
#[post("/debug", data = "<data>")] #[post("/debug", data = "<data>")]
async fn debug(data: Data) -> std::io::Result<()> { async fn debug(data: Data<'_>) -> std::io::Result<()> {
// Stream at most 512KiB all of the body data to stdout. // Stream at most 512KiB all of the body data to stdout.
data.open(512.kibibytes()) data.open(512.kibibytes())
.stream_to(tokio::io::stdout()) .stream_to(tokio::io::stdout())

View File

@ -171,7 +171,7 @@ impl Fairing for Counter {
} }
// Increment the counter for `GET` and `POST` requests. // Increment the counter for `GET` and `POST` requests.
async fn on_request(&self, request: &mut Request<'_>, _: &mut Data) { async fn on_request(&self, request: &mut Request<'_>, _: &mut Data<'_>) {
match request.method() { match request.method() {
Method::Get => self.get.fetch_add(1, Ordering::Relaxed), Method::Get => self.get.fetch_add(1, Ordering::Relaxed),
Method::Post => self.post.fetch_add(1, Ordering::Relaxed), Method::Post => self.post.fetch_add(1, Ordering::Relaxed),