From 4c6562cd29150b399ef67f33b77e92434afa2338 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Tue, 8 Jun 2021 02:13:02 -0700 Subject: [PATCH] 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. --- benchmarks/src/routing.rs | 2 +- contrib/dyn_templates/src/fairing.rs | 2 +- core/codegen/src/attribute/catch/mod.rs | 6 +-- core/codegen/src/attribute/route/mod.rs | 8 +-- core/codegen/tests/route-data.rs | 2 +- core/codegen/tests/route.rs | 4 +- .../ui-fail-nightly/responder-types.stderr | 2 +- .../ui-fail-nightly/route-type-errors.stderr | 2 +- .../ui-fail-stable/responder-types.stderr | 2 +- .../ui-fail-stable/route-type-errors.stderr | 2 +- core/codegen/tests/ui-fail/route-warnings.rs | 4 +- core/lib/src/data/capped.rs | 20 +++---- core/lib/src/data/data.rs | 29 +++++----- core/lib/src/data/data_stream.rs | 53 ++++++++++--------- core/lib/src/data/from_data.rs | 44 ++++++++------- core/lib/src/data/limits.rs | 4 +- core/lib/src/fairing/ad_hoc.rs | 6 +-- core/lib/src/fairing/fairings.rs | 2 +- core/lib/src/fairing/mod.rs | 10 ++-- core/lib/src/form/error.rs | 18 +++++-- core/lib/src/form/field.rs | 2 +- core/lib/src/form/form.rs | 2 +- core/lib/src/form/parser.rs | 13 +++-- core/lib/src/fs/server.rs | 2 +- core/lib/src/fs/temp_file.rs | 9 ++-- core/lib/src/outcome.rs | 6 +-- core/lib/src/rocket.rs | 2 +- core/lib/src/route/handler.rs | 33 ++++++------ core/lib/src/route/route.rs | 2 +- core/lib/src/serde/json.rs | 4 +- core/lib/src/serde/msgpack.rs | 4 +- core/lib/src/server.rs | 12 ++--- .../local-request-content-type-issue-505.rs | 2 +- core/lib/tests/panic-handling.rs | 2 +- core/lib/tests/replace-content-type-518.rs | 4 +- examples/fairings/src/main.rs | 2 +- examples/manual-routing/src/main.rs | 14 ++--- examples/pastebin/src/main.rs | 2 +- site/guide/10-pastebin.md | 4 +- site/guide/4-requests.md | 6 +-- site/guide/7-fairings.md | 2 +- 41 files changed, 179 insertions(+), 172 deletions(-) diff --git a/benchmarks/src/routing.rs b/benchmarks/src/routing.rs index 64d68491..c4fb8238 100644 --- a/benchmarks/src/routing.rs +++ b/benchmarks/src/routing.rs @@ -6,7 +6,7 @@ use rocket::{route, config, Request, Data, Route, Config}; use rocket::http::{Method, RawStr, ContentType, Accept, Status}; 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() } diff --git a/contrib/dyn_templates/src/fairing.rs b/contrib/dyn_templates/src/fairing.rs index 52aa2798..08e1c768 100644 --- a/contrib/dyn_templates/src/fairing.rs +++ b/contrib/dyn_templates/src/fairing.rs @@ -65,7 +65,7 @@ impl Fairing for TemplateFairing { } #[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::() .expect("Template ContextManager registered in on_ignite"); diff --git a/core/codegen/src/attribute/catch/mod.rs b/core/codegen/src/attribute/catch/mod.rs index f504f1bf..6f4bf61c 100644 --- a/core/codegen/src/attribute/catch/mod.rs +++ b/core/codegen/src/attribute/catch/mod.rs @@ -64,10 +64,10 @@ pub fn _catch( /// Rocket code generated proxy static conversion implementations. impl #user_catcher_fn_name { fn into_info(self) -> #_catcher::StaticInfo { - fn monomorphized_function<'_b>( + fn monomorphized_function<'__r>( #__status: #Status, - #__req: &'_b #Request<'_> - ) -> #_catcher::BoxFuture<'_b> { + #__req: &'__r #Request<'_> + ) -> #_catcher::BoxFuture<'__r> { #_Box::pin(async move { let __response = #catcher_response; #Response::build() diff --git a/core/codegen/src/attribute/route/mod.rs b/core/codegen/src/attribute/route/mod.rs index 4c9c5789..c9eae2bb 100644 --- a/core/codegen/src/attribute/route/mod.rs +++ b/core/codegen/src/attribute/route/mod.rs @@ -331,10 +331,10 @@ fn codegen_route(route: Route) -> Result { impl #handler_fn_name { #[allow(non_snake_case, unreachable_patterns, unreachable_code)] fn into_info(self) -> #_route::StaticInfo { - fn monomorphized_function<'_b>( - #__req: &'_b #Request<'_>, - #__data: #Data - ) -> #_route::BoxFuture<'_b> { + fn monomorphized_function<'__r>( + #__req: &'__r #Request<'_>, + #__data: #Data<'__r> + ) -> #_route::BoxFuture<'__r> { #_Box::pin(async move { #(#request_guards)* #(#param_guards)* diff --git a/core/codegen/tests/route-data.rs b/core/codegen/tests/route-data.rs index 9296b6e4..1ecd0452 100644 --- a/core/codegen/tests/route-data.rs +++ b/core/codegen/tests/route-data.rs @@ -19,7 +19,7 @@ struct Simple<'r>(&'r str); impl<'r> FromData<'r> for Simple<'r> { type Error = std::io::Error; - async fn from_data(req: &'r Request<'_>, data: Data) -> data::Outcome { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> { <&'r str>::from_data(req, data).await.map(Simple) } } diff --git a/core/codegen/tests/route.rs b/core/codegen/tests/route.rs index 36517406..db74a45b 100644 --- a/core/codegen/tests/route.rs +++ b/core/codegen/tests/route.rs @@ -26,7 +26,7 @@ struct Simple(String); impl<'r> FromData<'r> for Simple { type Error = std::io::Error; - async fn from_data(req: &'r Request<'_>, data: Data) -> data::Outcome { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> { String::from_data(req, data).await.map(Simple) } } @@ -78,7 +78,7 @@ fn post2( #[allow(dead_code)] #[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] diff --git a/core/codegen/tests/ui-fail-nightly/responder-types.stderr b/core/codegen/tests/ui-fail-nightly/responder-types.stderr index ca586d84..5853d181 100644 --- a/core/codegen/tests/ui-fail-nightly/responder-types.stderr +++ b/core/codegen/tests/ui-fail-nightly/responder-types.stderr @@ -53,4 +53,4 @@ error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied 28 | fn foo() -> usize { 0 } | ^^^^^ the trait `Responder<'_, '_>` is not implemented for `usize` | - = note: required by `route::handler::, Status, rocket::Data>>::from` + = note: required by `route::handler::, Status, rocket::Data<'o>>>::from` diff --git a/core/codegen/tests/ui-fail-nightly/route-type-errors.stderr b/core/codegen/tests/ui-fail-nightly/route-type-errors.stderr index 45c6c90f..e9f53ff9 100644 --- a/core/codegen/tests/ui-fail-nightly/route-type-errors.stderr +++ b/core/codegen/tests/ui-fail-nightly/route-type-errors.stderr @@ -38,7 +38,7 @@ error[E0277]: the trait bound `Q: FromData<'_>` is not satisfied | ::: $WORKSPACE/core/lib/src/data/from_data.rs | - | async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome; + | async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self>; | -- required by this bound in `rocket::data::FromData::from_data` error[E0277]: the trait bound `Q: FromRequest<'_>` is not satisfied diff --git a/core/codegen/tests/ui-fail-stable/responder-types.stderr b/core/codegen/tests/ui-fail-stable/responder-types.stderr index a71752a2..66b9d3a6 100644 --- a/core/codegen/tests/ui-fail-stable/responder-types.stderr +++ b/core/codegen/tests/ui-fail-stable/responder-types.stderr @@ -53,4 +53,4 @@ error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied 28 | fn foo() -> usize { 0 } | ^^^^^ the trait `Responder<'_, '_>` is not implemented for `usize` | - = note: required by `route::handler::, Status, rocket::Data>>::from` + = note: required by `route::handler::, Status, rocket::Data<'o>>>::from` diff --git a/core/codegen/tests/ui-fail-stable/route-type-errors.stderr b/core/codegen/tests/ui-fail-stable/route-type-errors.stderr index 45c6c90f..e9f53ff9 100644 --- a/core/codegen/tests/ui-fail-stable/route-type-errors.stderr +++ b/core/codegen/tests/ui-fail-stable/route-type-errors.stderr @@ -38,7 +38,7 @@ error[E0277]: the trait bound `Q: FromData<'_>` is not satisfied | ::: $WORKSPACE/core/lib/src/data/from_data.rs | - | async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome; + | async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self>; | -- required by this bound in `rocket::data::FromData::from_data` error[E0277]: the trait bound `Q: FromRequest<'_>` is not satisfied diff --git a/core/codegen/tests/ui-fail/route-warnings.rs b/core/codegen/tests/ui-fail/route-warnings.rs index 3be14d75..95e8c8c8 100644 --- a/core/codegen/tests/ui-fail/route-warnings.rs +++ b/core/codegen/tests/ui-fail/route-warnings.rs @@ -16,10 +16,10 @@ fn f2() {} // Check if a data argument is used with a usually non-payload bearing method. #[get("/", data = "<_foo>")] -fn g0(_foo: rocket::Data) {} +fn g0(_foo: rocket::Data<'_>) {} #[head("/", data = "<_foo>")] -fn g1(_foo: rocket::Data) {} +fn g1(_foo: rocket::Data<'_>) {} fn main() { compile_error!("checking for warnings!") diff --git a/core/lib/src/data/capped.rs b/core/lib/src/data/capped.rs index e1e2aa2a..335c980f 100644 --- a/core/lib/src/data/capped.rs +++ b/core/lib/src/data/capped.rs @@ -223,21 +223,21 @@ macro_rules! impl_strict_from_form_field_from_capped { fn from_value(f: ValueField<'v>) -> Result<'v, Self> { let capped = as FromFormField<'v>>::from_value(f)?; - if capped.is_complete() { - Ok(capped.value) - } else { - Err((None, Some(capped.n.written)))? + if !capped.is_complete() { + Err((None, Some(capped.n.written)))?; } + + Ok(capped.value) } async fn from_data(field: DataField<'v, '_>) -> Result<'v, Self> { let capped = as FromFormField<'v>>::from_data(field); let capped = capped.await?; - if capped.is_complete() { - Ok(capped.value) - } else { - Err((None, Some(capped.n.written)))? + if !capped.is_complete() { + 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( r: &'r $crate::Request<'_>, - d: $crate::Data - ) -> $crate::data::Outcome { + d: $crate::Data<'r> + ) -> $crate::data::Outcome<'r, Self> { use $crate::outcome::Outcome::*; use std::io::{Error, ErrorKind::UnexpectedEof}; diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index bf09bebb..52898b49 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -15,7 +15,7 @@ pub const PEEK_BYTES: usize = 512; /// /// ```rust /// # #[macro_use] extern crate rocket; -/// # type DataGuard = rocket::data::Data; +/// # type DataGuard = String; /// #[post("/submit", data = "")] /// fn submit(var: DataGuard) { /* ... */ } /// # 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 /// body data. This enables partially or fully reading from a `Data` object /// without consuming the `Data` object. -pub struct Data { +pub struct Data<'r> { buffer: Vec, is_complete: bool, - stream: StreamReader, + stream: StreamReader<'r>, } -impl Data { +impl<'r> Data<'r> { /// Create a `Data` from a recognized `stream`. - pub(crate) fn from>(stream: S) -> Data { + pub(crate) fn from>>(stream: S) -> Data<'r> { // 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 // kind of idle timeout should be implemented. @@ -57,7 +57,7 @@ impl Data { /// This creates a `data` object from a local data source `data`. #[inline] - pub(crate) fn local(data: Vec) -> Data { + pub(crate) fn local(data: Vec) -> Data<'r> { Data { buffer: data, stream: StreamReader::empty(), @@ -78,11 +78,11 @@ impl Data { /// use rocket::data::{Data, ToByteUnit}; /// /// # const SIZE_LIMIT: u64 = 2 << 20; // 2MiB - /// fn handler(data: Data) { + /// fn handler(data: Data<'_>) { /// 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()) } @@ -101,7 +101,7 @@ impl Data { /// /// ```rust /// use rocket::request::{self, Request, FromRequest}; - /// use rocket::data::{self, Data, FromData}; + /// use rocket::data::{Data, FromData, Outcome}; /// # struct MyType; /// # type MyError = String; /// @@ -109,12 +109,9 @@ impl Data { /// impl<'r> FromData<'r> for MyType { /// type Error = MyError; /// - /// async fn from_data( - /// req: &'r Request<'_>, - /// mut data: Data - /// ) -> data::Outcome { + /// async fn from_data(r: &'r Request<'_>, mut data: Data<'r>) -> Outcome<'r, Self> { /// 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" { /// /* do something; body data starts with `"hi"` */ /// } @@ -179,7 +176,7 @@ impl Data { /// ```rust /// use rocket::data::Data; /// - /// async fn handler(mut data: Data) { + /// async fn handler(mut data: Data<'_>) { /// if data.peek_complete() { /// println!("All of the data: {:?}", data.peek(512).await); /// } diff --git a/core/lib/src/data/data_stream.rs b/core/lib/src/data/data_stream.rs index d09f51ad..c87fde74 100644 --- a/core/lib/src/data/data_stream.rs +++ b/core/lib/src/data/data_stream.rs @@ -39,14 +39,14 @@ use crate::data::{Capped, N}; /// /// [`DataStream::stream_to(&mut vec)`]: DataStream::stream_to() /// [`DataStream::stream_to(&mut file)`]: DataStream::stream_to() -pub struct DataStream { - pub(crate) chain: Take>, StreamReader>>, +pub struct DataStream<'r> { + pub(crate) chain: Take>, StreamReader<'r>>>, } /// An adapter: turns a `T: Stream` (in `StreamKind`) into a `tokio::AsyncRead`. -pub struct StreamReader { +pub struct StreamReader<'r> { state: State, - inner: StreamKind, + inner: StreamKind<'r>, } /// The current state of `StreamReader` `AsyncRead` adapter. @@ -57,13 +57,14 @@ enum State { } /// The kinds of streams we accept as `Data`. -enum StreamKind { - Body(hyper::Body), - Multipart(multer::Field<'static>) +enum StreamKind<'r> { + Empty, + Body(&'r mut hyper::Body), + Multipart(multer::Field<'r>) } -impl DataStream { - pub(crate) fn new(buf: Vec, stream: StreamReader, limit: u64) -> Self { +impl<'r> DataStream<'r> { + pub(crate) fn new(buf: Vec, stream: StreamReader<'r>, limit: u64) -> Self { let chain = Chain::new(Cursor::new(buf), stream).take(limit); Self { chain } } @@ -71,7 +72,7 @@ impl DataStream { /// Whether a previous read exhausted the set limit _and then some_. async fn limit_exceeded(&mut self) -> io::Result { #[cold] - async fn _limit_exceeded(stream: &mut DataStream) -> io::Result { + async fn _limit_exceeded(stream: &mut DataStream<'_>) -> io::Result { stream.chain.set_limit(1); let mut buf = [0u8; 1]; Ok(stream.read(&mut buf).await? != 0) @@ -87,7 +88,7 @@ impl DataStream { /// ```rust /// 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(); /// } /// ``` @@ -111,7 +112,7 @@ impl DataStream { /// use std::io; /// use rocket::data::{Data, ToByteUnit}; /// - /// async fn data_guard(mut data: Data) -> io::Result { + /// async fn data_guard(mut data: Data<'_>) -> io::Result { /// // write all of the data to stdout /// let written = data.open(512.kibibytes()) /// .stream_to(tokio::io::stdout()).await?; @@ -136,7 +137,7 @@ impl DataStream { /// use std::io; /// use rocket::data::{Data, ToByteUnit}; /// - /// async fn data_guard(mut data: Data) -> io::Result { + /// async fn data_guard(mut data: Data<'_>) -> io::Result { /// // write all of the data to stdout /// let written = data.open(512.kibibytes()) /// .stream_precise_to(tokio::io::stdout()).await?; @@ -159,7 +160,7 @@ impl DataStream { /// use std::io; /// use rocket::data::{Data, ToByteUnit}; /// - /// async fn data_guard(data: Data) -> io::Result> { + /// async fn data_guard(data: Data<'_>) -> io::Result> { /// let bytes = data.open(4.kibibytes()).into_bytes().await?; /// if !bytes.is_complete() { /// println!("there are bytes remaining in the stream"); @@ -182,7 +183,7 @@ impl DataStream { /// use std::io; /// use rocket::data::{Data, ToByteUnit}; /// - /// async fn data_guard(data: Data) -> io::Result { + /// async fn data_guard(data: Data<'_>) -> io::Result { /// let string = data.open(10.bytes()).into_string().await?; /// if !string.is_complete() { /// println!("there are bytes remaining in the stream"); @@ -208,7 +209,7 @@ impl DataStream { /// use std::io; /// use rocket::data::{Data, ToByteUnit}; /// - /// async fn data_guard(mut data: Data) -> io::Result { + /// async fn data_guard(mut data: Data<'_>) -> io::Result { /// let file = data.open(1.megabytes()).into_file("/static/file").await?; /// if !file.is_complete() { /// println!("there are bytes remaining in the stream"); @@ -226,25 +227,25 @@ impl DataStream { // TODO.async: Consider implementing `AsyncBufRead`. -impl StreamReader { +impl StreamReader<'_> { pub fn empty() -> Self { - Self { inner: StreamKind::Body(hyper::Body::empty()), state: State::Done } + Self { inner: StreamKind::Empty, state: State::Done } } } -impl From for StreamReader { - fn from(body: hyper::Body) -> Self { +impl<'r> From<&'r mut hyper::Body> for StreamReader<'r> { + fn from(body: &'r mut hyper::Body) -> Self { Self { inner: StreamKind::Body(body), state: State::Pending } } } -impl From> for StreamReader { - fn from(field: multer::Field<'static>) -> Self { +impl<'r> From> for StreamReader<'r> { + fn from(field: multer::Field<'r>) -> Self { Self { inner: StreamKind::Multipart(field), state: State::Pending } } } -impl AsyncRead for DataStream { +impl AsyncRead for DataStream<'_> { #[inline(always)] fn poll_read( 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; fn poll_next( @@ -267,6 +268,7 @@ impl Stream for StreamKind { .map_err_ext(|e| io::Error::new(io::ErrorKind::Other, e)), StreamKind::Multipart(mp) => Pin::new(mp).poll_next(cx) .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 { StreamKind::Body(body) => body.size_hint(), StreamKind::Multipart(mp) => mp.size_hint(), + StreamKind::Empty => (0, Some(0)), } } } -impl AsyncRead for StreamReader { +impl AsyncRead for StreamReader<'_> { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/core/lib/src/data/from_data.rs b/core/lib/src/data/from_data.rs index aa7558d2..68a00c82 100644 --- a/core/lib/src/data/from_data.rs +++ b/core/lib/src/data/from_data.rs @@ -5,15 +5,16 @@ use crate::outcome::{self, IntoOutcome, try_outcome, Outcome::*}; /// Type alias for the `Outcome` of [`FromData`]. /// -/// [`FromData`]: crate::data::FromData -pub type Outcome = outcome::Outcome; +/// [`FromData`]: crTte::data::FromData +pub type Outcome<'r, T, E = >::Error> + = outcome::Outcome>; -impl IntoOutcome for Result { +impl<'r, S, E> IntoOutcome> for Result { type Failure = Status; - type Forward = Data; + type Forward = Data<'r>; #[inline] - fn into_outcome(self, status: Status) -> Outcome { + fn into_outcome(self, status: Status) -> Outcome<'r, S, E> { match self { Ok(val) => Success(val), Err(err) => Failure((status, err)) @@ -21,7 +22,7 @@ impl IntoOutcome for Result { } #[inline] - fn or_forward(self, data: Data) -> Outcome { + fn or_forward(self, data: Data<'r>) -> Outcome<'r, S, E> { match self { Ok(val) => Success(val), Err(_) => Forward(data) @@ -41,7 +42,7 @@ impl IntoOutcome for Result { /// /// ```rust /// # #[macro_use] extern crate rocket; -/// # type DataGuard = rocket::data::Data; +/// # type DataGuard = String; /// #[post("/submit", data = "")] /// fn submit(var: DataGuard) { /* ... */ } /// ``` @@ -66,7 +67,7 @@ impl IntoOutcome for Result { /// impl<'r> FromData<'r> for MyType { /// type Error = MyError; /// -/// async fn from_data(req: &'r Request<'_>, data: Data) -> data::Outcome { +/// async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> { /// /* .. */ /// # unimplemented!() /// } @@ -122,7 +123,7 @@ impl IntoOutcome for Result { /// impl<'r> FromData<'r> for Person<'r> { /// type Error = Error; /// -/// async fn from_data(req: &'r Request<'_>, data: Data) -> data::Outcome { +/// async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> { /// use Error::*; /// 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 the data is not appropriate given the type of `Self`, `Forward` is /// returned. If parsing fails, `Failure` is returned. - async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome; + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self>; } use crate::data::Capped; @@ -199,7 +200,7 @@ use crate::data::Capped; impl<'r> FromData<'r> for Capped { type Error = std::io::Error; - async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { let limit = req.limits().get("string").unwrap_or(Limits::STRING); 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> { type Error = std::io::Error; - async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { let capped = try_outcome!(>::from_data(req, data).await); let string = capped.map(|s| local_cache!(req, s).as_str()); Success(string) @@ -224,7 +225,7 @@ impl_strict_from_data_from_capped!(&'r str); impl<'r> FromData<'r> for Capped<&'r RawStr> { type Error = std::io::Error; - async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { let capped = try_outcome!(>::from_data(req, data).await); let raw = capped.map(|s| RawStr::new(local_cache!(req, s))); Success(raw) @@ -237,7 +238,7 @@ impl_strict_from_data_from_capped!(&'r RawStr); impl<'r> FromData<'r> for Capped> { type Error = std::io::Error; - async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { let capped = try_outcome!(>::from_data(req, data).await); 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]> { type Error = std::io::Error; - async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { let capped = try_outcome!(>>::from_data(req, data).await); let raw = capped.map(|b| local_cache!(req, b).as_slice()); Success(raw) @@ -262,7 +263,7 @@ impl_strict_from_data_from_capped!(&'r [u8]); impl<'r> FromData<'r> for Capped> { type Error = std::io::Error; - async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { let limit = req.limits().get("bytes").unwrap_or(Limits::BYTES); data.open(limit).into_bytes().await.into_outcome(Status::BadRequest) } @@ -271,10 +272,10 @@ impl<'r> FromData<'r> for Capped> { impl_strict_from_data_from_capped!(Vec); #[crate::async_trait] -impl<'r> FromData<'r> for Data { +impl<'r> FromData<'r> for Data<'r> { type Error = std::convert::Infallible; - async fn from_data(_: &'r Request<'_>, data: Data) -> Outcome { + async fn from_data(_: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { Success(data) } } @@ -283,10 +284,7 @@ impl<'r> FromData<'r> for Data { impl<'r, T: FromData<'r> + 'r> FromData<'r> for Result { type Error = std::convert::Infallible; - async fn from_data( - req: &'r Request<'_>, - data: Data - ) -> Outcome>::Error>, Self::Error> { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { match T::from_data(req, data).await { Success(v) => Success(Ok(v)), Failure((_, e)) => Success(Err(e)), @@ -299,7 +297,7 @@ impl<'r, T: FromData<'r> + 'r> FromData<'r> for Result { impl<'r, T: FromData<'r>> FromData<'r> for Option { type Error = std::convert::Infallible; - async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { match T::from_data(req, data).await { Success(v) => Success(Some(v)), Failure(..) | Forward(..) => Success(None), diff --git a/core/lib/src/data/limits.rs b/core/lib/src/data/limits.rs index ecae0ff6..e942a89c 100644 --- a/core/lib/src/data/limits.rs +++ b/core/lib/src/data/limits.rs @@ -93,7 +93,7 @@ use crate::http::uncased::Uncased; /// use rocket::data::{Data, Limits, ToByteUnit}; /// /// #[post("/echo", data = "")] -/// async fn echo(data: Data, limits: &Limits) -> io::Result { +/// async fn echo(data: Data<'_>, limits: &Limits) -> io::Result { /// let limit = limits.get("data").unwrap_or(1.mebibytes()); /// Ok(data.open(limit).into_string().await?.value) /// } @@ -112,7 +112,7 @@ use crate::http::uncased::Uncased; /// impl<'r> FromData<'r> for MyType { /// type Error = MyError; /// -/// async fn from_data(req: &'r Request<'_>, data: Data) -> data::Outcome { +/// async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> { /// let limit = req.limits().get("my-data-type"); /// /* .. */ /// # unimplemented!() diff --git a/core/lib/src/fairing/ad_hoc.rs b/core/lib/src/fairing/ad_hoc.rs index 5d9659b6..915e1754 100644 --- a/core/lib/src/fairing/ad_hoc.rs +++ b/core/lib/src/fairing/ad_hoc.rs @@ -59,7 +59,7 @@ enum AdHocKind { Liftoff(Once FnOnce(&'a Rocket) -> BoxFuture<'a, ()> + Send + 'static>), /// An ad-hoc **request** fairing. Called when a request is received. - Request(Box Fn(&'a mut Request<'_>, &'a Data) + Request(Box Fn(&'a mut Request<'_>, &'a Data<'_>) -> BoxFuture<'a, ()> + Send + Sync + 'static>), /// An ad-hoc **response** fairing. Called when a response is ready to be @@ -150,7 +150,7 @@ impl AdHoc { /// }); /// ``` pub fn on_request(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)) } } @@ -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 { f(req, data).await } diff --git a/core/lib/src/fairing/fairings.rs b/core/lib/src/fairing/fairings.rs index afbbfc57..594afb5a 100644 --- a/core/lib/src/fairing/fairings.rs +++ b/core/lib/src/fairing/fairings.rs @@ -140,7 +140,7 @@ impl Fairings { } #[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) { fairing.on_request(req, data).await } diff --git a/core/lib/src/fairing/mod.rs b/core/lib/src/fairing/mod.rs index e73d5ce2..cdde553a 100644 --- a/core/lib/src/fairing/mod.rs +++ b/core/lib/src/fairing/mod.rs @@ -237,7 +237,7 @@ pub type Result, E = Rocket> = std::result::Result, data: &mut Data) { +/// async fn on_request(&self, req: &mut Request<'_>, data: &mut Data<'_>) { /// /* ... */ /// # unimplemented!() /// } @@ -286,7 +286,7 @@ pub type Result, E = Rocket> = std::result::Result, _: &mut Data) { +/// async fn on_request(&self, req: &mut Request<'_>, _: &mut Data<'_>) { /// if req.method() == Method::Get { /// self.get.fetch_add(1, Ordering::Relaxed); /// } else if req.method() == Method::Post { @@ -349,7 +349,7 @@ pub type Result, E = Rocket> = std::result::Result, _: &mut Data) { +/// async fn on_request(&self, request: &mut Request<'_>, _: &mut Data<'_>) { /// // Store a `TimerStart` instead of directly storing a `SystemTime` /// // to ensure that this usage doesn't conflict with anything else /// // that might store a `SystemTime` in request-local cache. @@ -457,7 +457,7 @@ pub trait Fairing: Send + Sync + Any + 'static { /// ## Default Implementation /// /// 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. /// @@ -490,7 +490,7 @@ impl Fairing for std::sync::Arc { } #[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 } diff --git a/core/lib/src/form/error.rs b/core/lib/src/form/error.rs index d7a23310..23c05751 100644 --- a/core/lib/src/form/error.rs +++ b/core/lib/src/form/error.rs @@ -804,11 +804,19 @@ impl fmt::Display for ErrorKind<'_> { match self { ErrorKind::InvalidLength { min, max } => { match (min, max) { - (None, None) => write!(f, "unexpected or incomplete")?, - (None, Some(k)) => write!(f, "length cannot exceed {}", k)?, - (Some(1), None) => write!(f, "value cannot be empty")?, - (Some(k), None) => write!(f, "length must be at least {}", k)?, - (Some(i), Some(j)) => write!(f, "length must be between {} and {}", i, j)?, + (None, None) => write!(f, "invalid length: incomplete")?, + (None, Some(k)) if *k < 1024 => write!(f, "length cannot exceed {}", k)?, + (None, Some(k)) => write!(f, "size must not exceed {}", ByteUnit::from(*k))?, + (Some(1), None) => write!(f, "cannot be empty")?, + (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 } => { diff --git a/core/lib/src/form/field.rs b/core/lib/src/form/field.rs index 9bd80876..f241b709 100644 --- a/core/lib/src/form/field.rs +++ b/core/lib/src/form/field.rs @@ -33,7 +33,7 @@ pub struct DataField<'r, 'i> { /// The request in which the form field was submitted. pub request: &'r Request<'i>, /// The raw data stream. - pub data: Data, + pub data: Data<'r>, } impl<'v> ValueField<'v> { diff --git a/core/lib/src/form/form.rs b/core/lib/src/form/form.rs index e40a69b5..0ed43f92 100644 --- a/core/lib/src/form/form.rs +++ b/core/lib/src/form/form.rs @@ -299,7 +299,7 @@ impl DerefMut for Form { impl<'r, T: FromForm<'r>> FromData<'r> for Form { type Error = Errors<'r>; - async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { use either::Either; let mut parser = try_outcome!(Parser::new(req, data).await); diff --git a/core/lib/src/form/parser.rs b/core/lib/src/form/parser.rs index eb57b4ed..20bd8430 100644 --- a/core/lib/src/form/parser.rs +++ b/core/lib/src/form/parser.rs @@ -21,7 +21,7 @@ pub struct Buffer { pub struct MultipartParser<'r, 'i> { request: &'r Request<'i>, buffer: &'r Buffer, - source: Multipart<'static>, + source: Multipart<'r>, done: bool, } @@ -36,7 +36,10 @@ pub enum Parser<'r, 'i> { } impl<'r, 'i> Parser<'r, 'i> { - pub async fn new(req: &'r Request<'i>, data: Data) -> Outcome, 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() { 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, @@ -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 string = data.open(limit).into_string().await?; if !string.is_complete() { - Err((None, Some(limit.as_u64())))? + Err((None, Some(limit.as_u64())))?; } 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() .ok_or(multer::Error::NoMultipart)? .param("boundary") diff --git a/core/lib/src/fs/server.rs b/core/lib/src/fs/server.rs index 8c421f78..029a49df 100644 --- a/core/lib/src/fs/server.rs +++ b/core/lib/src/fs/server.rs @@ -184,7 +184,7 @@ impl Into> for FileServer { #[crate::async_trait] 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; // Get the segments as a `PathBuf`, allowing dotfiles requested. diff --git a/core/lib/src/fs/temp_file.rs b/core/lib/src/fs/temp_file.rs index c504ccb4..ec575466 100644 --- a/core/lib/src/fs/temp_file.rs +++ b/core/lib/src/fs/temp_file.rs @@ -3,7 +3,7 @@ use std::path::{PathBuf, Path}; use crate::Request; 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::outcome::IntoOutcome; use crate::fs::FileName; @@ -441,7 +441,7 @@ impl<'v> TempFile<'v> { async fn from<'a>( req: &Request<'_>, - data: Data, + data: Data<'_>, file_name: Option<&'a FileName>, content_type: Option, ) -> io::Result>> { @@ -489,10 +489,7 @@ impl<'v> FromFormField<'v> for Capped> { impl<'r> FromData<'r> for Capped> { type Error = io::Error; - async fn from_data( - req: &'r crate::Request<'_>, - data: crate::Data - ) -> crate::data::Outcome { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> { use yansi::Paint; let has_form = |ty: &ContentType| ty.is_form_data() || ty.is_form(); diff --git a/core/lib/src/outcome.rs b/core/lib/src/outcome.rs index 5b08eca9..df3dbc1a 100644 --- a/core/lib/src/outcome.rs +++ b/core/lib/src/outcome.rs @@ -26,7 +26,7 @@ //! //! ```rust //! # use rocket::post; -//! # type S = rocket::data::Data; +//! # type S = String; //! #[post("/", data = "")] //! fn hello(my_val: S) { /* ... */ } //! ``` @@ -49,7 +49,7 @@ //! //! ```rust //! # use rocket::post; -//! # type S = rocket::data::Data; +//! # type S = Option; //! # type E = std::convert::Infallible; //! #[post("/", data = "")] //! fn hello(my_val: Result) { /* ... */ } @@ -73,7 +73,7 @@ //! //! ```rust //! # use rocket::post; -//! # type S = rocket::data::Data; +//! # type S = String; //! #[post("/", data = "")] //! fn hello(my_val: S) { /* ... */ } //! ``` diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 1104c441..260f694a 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -290,7 +290,7 @@ impl Rocket { /// use rocket::{Request, Route, Data, route}; /// 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() /// } /// diff --git a/core/lib/src/route/handler.rs b/core/lib/src/route/handler.rs index 1d8c33d4..5e4e62ff 100644 --- a/core/lib/src/route/handler.rs +++ b/core/lib/src/route/handler.rs @@ -4,7 +4,7 @@ use crate::http::Status; /// Type alias for the return type of a [`Route`](crate::Route)'s /// [`Handler::handle()`]. -pub type Outcome<'r> = crate::outcome::Outcome, Status, Data>; +pub type Outcome<'r> = crate::outcome::Outcome, Status, Data<'r>>; /// Type alias for the return type of a _raw_ [`Route`](crate::Route)'s /// [`Handler`]. @@ -53,7 +53,7 @@ pub type BoxFuture<'r, T = Outcome<'r>> = futures::future::BoxFuture<'r, T>; /// /// #[rocket::async_trait] /// 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 { /// Kind::Simple => Outcome::from(req, "simple"), /// 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)`, /// the next matching route is attempted. If there are no other matching /// 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. impl 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)] fn handle<'r, 'life0, 'life1, 'async_trait>( &'life0 self, req: &'r Request<'life1>, - data: Data, + data: Data<'r>, ) -> BoxFuture<'r> where 'r: 'async_trait, 'life0: 'async_trait, @@ -167,6 +167,7 @@ impl Handler for F } } +// FIXME! impl<'r, 'o: 'r> Outcome<'o> { /// Return the `Outcome` of response to `req` from `responder`. /// @@ -179,12 +180,12 @@ impl<'r, 'o: 'r> Outcome<'o> { /// ```rust /// 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!") /// } /// ``` #[inline] - pub fn from>(req: &'r Request<'_>, responder: R) -> Outcome<'o> { + pub fn from>(req: &'r Request<'_>, responder: R) -> Outcome<'r> { match responder.respond_to(req) { Ok(response) => Outcome::Success(response), Err(status) => Outcome::Failure(status) @@ -202,12 +203,12 @@ impl<'r, 'o: 'r> Outcome<'o> { /// ```rust /// 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!") /// } /// ``` #[inline] - pub fn try_from(req: &'r Request<'_>, result: Result) -> Outcome<'o> + pub fn try_from(req: &'r Request<'_>, result: Result) -> Outcome<'r> where R: Responder<'r, 'o>, E: std::fmt::Debug { let responder = result.map_err(crate::response::Debug); @@ -228,12 +229,12 @@ impl<'r, 'o: 'r> Outcome<'o> { /// ```rust /// 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!") /// } /// ``` #[inline] - pub fn from_or_forward(req: &'r Request<'_>, data: Data, responder: R) -> Outcome<'o> + pub fn from_or_forward(req: &'r Request<'_>, data: Data<'r>, responder: R) -> Outcome<'r> where R: Responder<'r, 'o> { match responder.respond_to(req) { @@ -253,12 +254,12 @@ impl<'r, 'o: 'r> Outcome<'o> { /// use rocket::{Request, Data, route}; /// 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) /// } /// ``` #[inline(always)] - pub fn failure(code: Status) -> Outcome<'static> { + pub fn failure(code: Status) -> Outcome<'r> { Outcome::Failure(code) } @@ -272,19 +273,19 @@ impl<'r, 'o: 'r> Outcome<'o> { /// ```rust /// 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) /// } /// ``` #[inline(always)] - pub fn forward(data: Data) -> Outcome<'static> { + pub fn forward(data: Data<'r>) -> Outcome<'r> { Outcome::Forward(data) } } // INTERNAL: A handler to use when one is needed temporarily. #[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() } diff --git a/core/lib/src/route/route.rs b/core/lib/src/route/route.rs index b136b82b..8c0d7edd 100644 --- a/core/lib/src/route/route.rs +++ b/core/lib/src/route/route.rs @@ -339,7 +339,7 @@ pub struct StaticInfo { /// The route's format, if any. pub format: Option, /// 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. pub rank: Option, /// Route-derived sentinels, if any. diff --git a/core/lib/src/serde/json.rs b/core/lib/src/serde/json.rs index 2d5b2d12..86344554 100644 --- a/core/lib/src/serde/json.rs +++ b/core/lib/src/serde/json.rs @@ -158,7 +158,7 @@ impl<'r, T: Deserialize<'r>> Json { serde_json::from_str(s).map(Json).map_err(|e| Error::Parse(s, e)) } - async fn from_data(req: &'r Request<'_>, data: Data) -> Result> { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Result> { let limit = req.limits().get("json").unwrap_or(Limits::JSON); let string = match data.open(limit).into_string().await { Ok(s) if s.is_complete() => s.into_inner(), @@ -177,7 +177,7 @@ impl<'r, T: Deserialize<'r>> Json { impl<'r, T: Deserialize<'r>> FromData<'r> for Json { type Error = Error<'r>; - async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { match Self::from_data(req, data).await { Ok(value) => Outcome::Success(value), Err(Error::Io(e)) if e.kind() == io::ErrorKind::UnexpectedEof => { diff --git a/core/lib/src/serde/msgpack.rs b/core/lib/src/serde/msgpack.rs index b9a55d63..348c8729 100644 --- a/core/lib/src/serde/msgpack.rs +++ b/core/lib/src/serde/msgpack.rs @@ -146,7 +146,7 @@ impl<'r, T: Deserialize<'r>> MsgPack { rmp_serde::from_slice(buf).map(MsgPack) } - async fn from_data(req: &'r Request<'_>, data: Data) -> Result { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Result { let limit = req.limits().get("msgpack").unwrap_or(Limits::MESSAGE_PACK); let bytes = match data.open(limit).into_bytes().await { Ok(buf) if buf.is_complete() => buf.into_inner(), @@ -165,7 +165,7 @@ impl<'r, T: Deserialize<'r>> MsgPack { impl<'r, T: Deserialize<'r>> FromData<'r> for MsgPack { type Error = Error; - async fn from_data(req: &'r Request<'_>, data: Data) -> Outcome { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { match Self::from_data(req, data).await { Ok(value) => Outcome::Success(value), Err(Error::InvalidDataRead(e)) if e.kind() == io::ErrorKind::UnexpectedEof => { diff --git a/core/lib/src/server.rs b/core/lib/src/server.rs index a3b7446a..3578f310 100644 --- a/core/lib/src/server.rs +++ b/core/lib/src/server.rs @@ -73,7 +73,7 @@ async fn hyper_service_fn( tokio::spawn(async move { // 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) { Ok(req) => req, Err(e) => { @@ -89,7 +89,7 @@ async fn hyper_service_fn( }; // 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. let token = rocket.preprocess_request(&mut req, &mut data).await; @@ -168,7 +168,7 @@ impl Rocket { pub(crate) async fn preprocess_request( &self, req: &mut Request<'_>, - data: &mut Data + data: &mut Data<'_> ) -> RequestToken { // Check if this is a form and if the form contains the special _method // field which we use to reinterpret the request's method. @@ -198,7 +198,7 @@ impl Rocket { &'s self, _token: RequestToken, request: &'r Request<'s>, - data: Data + data: Data<'r> ) -> Response<'r> { info!("{}:", request); @@ -228,7 +228,7 @@ impl Rocket { async fn route_and_process<'s, 'r: 's>( &'s self, request: &'r Request<'s>, - data: Data + data: Data<'r> ) -> Response<'r> { let mut response = match self.route(request, data).await { Outcome::Success(response) => response, @@ -266,7 +266,7 @@ impl Rocket { async fn route<'s, 'r: 's>( &'s self, request: &'r Request<'s>, - mut data: Data, + mut data: Data<'r>, ) -> route::Outcome<'r> { // Go through the list of matching routes until we fail or succeed. for route in self.router.route(request) { diff --git a/core/lib/tests/local-request-content-type-issue-505.rs b/core/lib/tests/local-request-content-type-issue-505.rs index cb78599b..b95f8985 100644 --- a/core/lib/tests/local-request-content-type-issue-505.rs +++ b/core/lib/tests/local-request-content-type-issue-505.rs @@ -21,7 +21,7 @@ use rocket::data::{self, FromData}; impl<'r> FromData<'r> for HasContentType { type Error = (); - async fn from_data(req: &'r Request<'_>, data: Data) -> data::Outcome { + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> { req.content_type().map(|_| HasContentType).or_forward(data) } } diff --git a/core/lib/tests/panic-handling.rs b/core/lib/tests/panic-handling.rs index 75a23931..e2e8e134 100644 --- a/core/lib/tests/panic-handling.rs +++ b/core/lib/tests/panic-handling.rs @@ -20,7 +20,7 @@ fn ise() -> &'static str { "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..."); } diff --git a/core/lib/tests/replace-content-type-518.rs b/core/lib/tests/replace-content-type-518.rs index 48463363..b7e9d8c9 100644 --- a/core/lib/tests/replace-content-type-518.rs +++ b/core/lib/tests/replace-content-type-518.rs @@ -2,10 +2,10 @@ use rocket::{Rocket, Build}; use rocket::{fairing::AdHoc, http::ContentType, local::blocking::Client}; #[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)] -fn other_index(_data: rocket::Data) -> &'static str { "other" } +fn other_index(_data: rocket::Data<'_>) -> &'static str { "other" } fn rocket() -> Rocket { rocket::build() diff --git a/examples/fairings/src/main.rs b/examples/fairings/src/main.rs index 48ce44da..6a092cad 100644 --- a/examples/fairings/src/main.rs +++ b/examples/fairings/src/main.rs @@ -38,7 +38,7 @@ impl Fairing for Counter { 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 { self.get.fetch_add(1, Ordering::Relaxed); } else if request.method() == Method::Post { diff --git a/examples/manual-routing/src/main.rs b/examples/manual-routing/src/main.rs index 79c5685e..b2f88525 100644 --- a/examples/manual-routing/src/main.rs +++ b/examples/manual-routing/src/main.rs @@ -10,15 +10,15 @@ use rocket::response::{Responder, status::Custom}; use rocket::outcome::{try_outcome, IntoOutcome}; 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) }) } -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() } -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) .and_then(|res| res.ok()) .unwrap_or("unnamed".into()); @@ -26,7 +26,7 @@ fn name<'a>(req: &'a Request, _: Data) -> route::BoxFuture<'a> { 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) .and_then(|res| res.ok()) .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 { if !req.content_type().map_or(false, |ct| ct.is_plain()) { 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() } @@ -80,7 +80,7 @@ impl CustomHandler { #[rocket::async_trait] 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 id = req.param::<&str>(0) .and_then(|res| res.ok()) diff --git a/examples/pastebin/src/main.rs b/examples/pastebin/src/main.rs index 6e0c67b4..0b990310 100644 --- a/examples/pastebin/src/main.rs +++ b/examples/pastebin/src/main.rs @@ -17,7 +17,7 @@ const HOST: Absolute<'static> = uri!("http://localhost:8000"); const ID_LENGTH: usize = 3; #[post("/", data = "")] -async fn upload(paste: Data) -> io::Result { +async fn upload(paste: Data<'_>) -> io::Result { let id = PasteId::new(ID_LENGTH); paste.open(128.kibibytes()).into_file(id.file_path()).await?; Ok(uri!(HOST, retrieve(id)).to_string()) diff --git a/site/guide/10-pastebin.md b/site/guide/10-pastebin.md index a3004943..12267b92 100644 --- a/site/guide/10-pastebin.md +++ b/site/guide/10-pastebin.md @@ -224,7 +224,7 @@ use rocket::Data; use rocket::response::Debug; #[post("/", data = "")] -fn upload(paste: Data) -> std::io::Result { +fn upload(paste: Data<'_>) -> std::io::Result { # unimplemented!() /* .. */ } @@ -255,7 +255,7 @@ use rocket::response::Debug; use rocket::data::{Data, ToByteUnit}; #[post("/", data = "")] -async fn upload(paste: Data) -> Result> { +async fn upload(paste: Data<'_>) -> Result> { let id = PasteId::new(3); let filename = format!("upload/{id}", id = id); let url = format!("{host}/{id}\n", host = "http://localhost:8000", id = id); diff --git a/site/guide/4-requests.md b/site/guide/4-requests.md index 16f6ef21..98a858df 100644 --- a/site/guide/4-requests.md +++ b/site/guide/4-requests.md @@ -598,7 +598,7 @@ As an example, consider the following route: # #[macro_use] extern crate rocket; # fn main() {} -# type User = rocket::data::Data; +# type User = String; #[post("/user", format = "application/json", data = "")] fn new_user(user: User) { /* ... */ } @@ -647,7 +647,7 @@ trait. It looks like this, where `T` is assumed to implement `FromData`: ```rust # #[macro_use] extern crate rocket; -# type T = rocket::data::Data; +# type T = String; #[post("/", data = "")] fn new(input: T) { /* .. */ } @@ -715,7 +715,7 @@ use rocket::tokio; use rocket::data::{Data, ToByteUnit}; #[post("/debug", 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. data.open(512.kibibytes()) .stream_to(tokio::io::stdout()) diff --git a/site/guide/7-fairings.md b/site/guide/7-fairings.md index 55e6dbd5..360f1092 100644 --- a/site/guide/7-fairings.md +++ b/site/guide/7-fairings.md @@ -171,7 +171,7 @@ impl Fairing for Counter { } // 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() { Method::Get => self.get.fetch_add(1, Ordering::Relaxed), Method::Post => self.post.fetch_add(1, Ordering::Relaxed),