diff --git a/CHANGELOG.md b/CHANGELOG.md index 57480246..0166905a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -958,7 +958,7 @@ applications. * **The [`Responder`] trait has changed.** `Responder::respond(self)` was removed in favor of - `Responder::respond_to(self, &Request)`. Responders may dynamically adjust + `Responder::respond_to(self, &'r Request)`. Responders may dynamically adjust their response based on the incoming request. * **`Outcome::of(Responder)` was removed while `Outcome::from(&Request, diff --git a/contrib/lib/src/compression/responder.rs b/contrib/lib/src/compression/responder.rs index 05a6bfa8..bd0c0d07 100644 --- a/contrib/lib/src/compression/responder.rs +++ b/contrib/lib/src/compression/responder.rs @@ -34,9 +34,9 @@ use super::CompressionUtils; #[derive(Debug)] pub struct Compress(pub R); -impl<'r, R: Responder<'r>> Responder<'r> for Compress { +impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Compress { #[inline(always)] - fn respond_to(self, request: &Request<'_>) -> response::Result<'r> { + fn respond_to(self, request: &'r Request<'_>) -> response::Result<'o> { let mut response = Response::build() .merge(self.0.respond_to(request)?) .finalize(); diff --git a/contrib/lib/src/json.rs b/contrib/lib/src/json.rs index 6d2c5f07..2d3d2246 100644 --- a/contrib/lib/src/json.rs +++ b/contrib/lib/src/json.rs @@ -19,7 +19,6 @@ use std::io; use std::iter::FromIterator; use tokio::io::AsyncReadExt; -use rocket::futures::future::BoxFuture; use rocket::request::Request; use rocket::outcome::Outcome::*; @@ -169,20 +168,15 @@ impl<'a, T: Deserialize<'a>> FromData<'a> for Json { /// Serializes the wrapped value into JSON. Returns a response with Content-Type /// JSON and a fixed-size body with the serialized value. If serialization /// fails, an `Err` of `Status::InternalServerError` is returned. -impl<'r, T: Serialize> Responder<'r> for Json { - fn respond_to<'a, 'x>(self, req: &'r Request<'a>) -> BoxFuture<'x, response::Result<'r>> - where 'a: 'x, 'r: 'x, Self: 'x - { - let json_string = serde_json::to_string(&self.0); - Box::pin(async move { - match json_string { - Ok(string) => Ok(content::Json(string).respond_to(req).await.unwrap()), - Err(e) => { - error_!("JSON failed to serialize: {:?}", e); - Err(Status::InternalServerError) - } - } - }) +impl<'r, T: Serialize> Responder<'r, 'static> for Json { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { + let string = serde_json::to_string(&self.0) + .map_err(|e| { + error_!("JSON failed to serialize: {:?}", e); + Status::InternalServerError + })?; + + content::Json(string).respond_to(req) } } @@ -297,10 +291,9 @@ impl FromIterator for JsonValue where serde_json::Value: FromIterator { /// Serializes the value into JSON. Returns a response with Content-Type JSON /// and a fixed-size body with the serialized value. -#[rocket::async_trait] -impl<'r> Responder<'r> for JsonValue { - async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> { - content::Json(self.0.to_string()).respond_to(req).await +impl<'r> Responder<'r, 'static> for JsonValue { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { + content::Json(self.0.to_string()).respond_to(req) } } diff --git a/contrib/lib/src/msgpack.rs b/contrib/lib/src/msgpack.rs index bfb73283..6c7c79a0 100644 --- a/contrib/lib/src/msgpack.rs +++ b/contrib/lib/src/msgpack.rs @@ -23,7 +23,6 @@ use rocket::outcome::Outcome::*; use rocket::data::{Data, FromData, FromDataFuture, Transform::*, TransformFuture, Transformed}; use rocket::http::Status; use rocket::response::{self, content, Responder}; -use rocket::futures::future::BoxFuture; use serde::Serialize; use serde::de::Deserialize; @@ -157,17 +156,15 @@ impl<'a, T: Deserialize<'a>> FromData<'a> for MsgPack { /// Serializes the wrapped value into MessagePack. Returns a response with /// Content-Type `MsgPack` and a fixed-size body with the serialization. If /// serialization fails, an `Err` of `Status::InternalServerError` is returned. -impl<'r, T: Serialize> Responder<'r> for MsgPack { - fn respond_to<'a, 'x>(self, req: &'r Request<'a>) -> BoxFuture<'x, response::Result<'r>> - where 'a: 'x, 'r: 'x, Self: 'x - { - match rmp_serde::to_vec(&self.0) { - Ok(buf) => content::MsgPack(buf).respond_to(req), - Err(e) => { +impl<'r, T: Serialize> Responder<'r, 'static> for MsgPack { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { + let buf = rmp_serde::to_vec(&self.0) + .map_err(|e| { error_!("MsgPack failed to serialize: {:?}", e); - Box::pin(async { Err(Status::InternalServerError) }) - } - } + Status::InternalServerError + })?; + + content::MsgPack(buf).respond_to(req) } } diff --git a/contrib/lib/src/serve.rs b/contrib/lib/src/serve.rs index c643d2ba..73bac08a 100644 --- a/contrib/lib/src/serve.rs +++ b/contrib/lib/src/serve.rs @@ -278,19 +278,17 @@ impl Into> for StaticFiles { impl Handler for StaticFiles { fn handle<'r>(&self, req: &'r Request<'_>, data: Data) -> HandlerFuture<'r> { - fn handle_dir<'r>(opt: Options, r: &'r Request<'_>, d: Data, path: &Path) -> HandlerFuture<'r> { + fn handle_dir<'r>(opt: Options, r: &'r Request<'_>, d: Data, path: &Path) -> Outcome<'r> { if opt.contains(Options::NormalizeDirs) && !r.uri().path().ends_with('/') { let new_path = r.uri().map_path(|p| p.to_owned() + "/") .expect("adding a trailing slash to a known good path results in a valid path") .into_owned(); - return Box::pin(async move { - Outcome::from_or_forward(r, d, Redirect::permanent(new_path)) - }); + return Outcome::from_or_forward(r, d, Redirect::permanent(new_path)); } if !opt.contains(Options::Index) { - return Box::pin(async move { Outcome::forward(d) }); + return Outcome::forward(d); } let file = NamedFile::open(path.join("index.html")).ok(); @@ -302,7 +300,7 @@ impl Handler for StaticFiles { let current_route = req.route().expect("route while handling"); let is_segments_route = current_route.uri.path().ends_with(">"); if !is_segments_route { - return handle_dir(self.options, req, data, &self.root); + return handle_dir(self.options, req, data, &self.root).pin(); } // Otherwise, we're handling segments. Get the segments as a `PathBuf`, @@ -316,7 +314,7 @@ impl Handler for StaticFiles { match &path { Some(path) if path.is_dir() => handle_dir(self.options, req, data, path), Some(path) => Outcome::from_or_forward(req, data, NamedFile::open(path).ok()), - None => Box::pin(async move { Outcome::forward(data) }), - } + None => Outcome::forward(data), + }.pin() } } diff --git a/contrib/lib/src/templates/mod.rs b/contrib/lib/src/templates/mod.rs index f428bd9e..4cd2c8c8 100644 --- a/contrib/lib/src/templates/mod.rs +++ b/contrib/lib/src/templates/mod.rs @@ -141,7 +141,7 @@ use serde_json::{Value, to_value}; use std::borrow::Cow; use std::path::PathBuf; -use rocket::{Manifest, State}; +use rocket::Manifest; use rocket::request::Request; use rocket::fairing::Fairing; use rocket::response::{self, Content, Responder}; @@ -389,20 +389,19 @@ impl Template { /// Returns a response with the Content-Type derived from the template's /// extension and a fixed-size body containing the rendered template. If /// rendering fails, an `Err` of `Status::InternalServerError` is returned. -#[rocket::async_trait] -impl<'r> Responder<'r> for Template { - async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> { +impl<'r> Responder<'r, 'static> for Template { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { let (render, content_type) = { - let ctxt = req.guard::>().await.succeeded().ok_or_else(|| { + let ctxt = req.managed_state::().ok_or_else(|| { error_!("Uninitialized template context: missing fairing."); info_!("To use templates, you must attach `Template::fairing()`."); info_!("See the `Template` documentation for more information."); Status::InternalServerError - })?.inner().context(); + })?.context(); self.finalize(&ctxt)? }; - Content(content_type, render).respond_to(req).await + Content(content_type, render).respond_to(req) } } diff --git a/core/codegen/src/attribute/catch.rs b/core/codegen/src/attribute/catch.rs index ec8846ba..e8dd7666 100644 --- a/core/codegen/src/attribute/catch.rs +++ b/core/codegen/src/attribute/catch.rs @@ -87,7 +87,7 @@ pub fn _catch(args: TokenStream, input: TokenStream) -> Result { // let #catcher: #fn_sig = #user_catcher_fn_name; let #catcher = #user_catcher_fn_name; #responder_stmt - ::rocket::response::Responder::respond_to(___responder, #req).await? + ::rocket::response::Responder::respond_to(___responder, #req)? }); // Generate the catcher, keeping the user's input around. diff --git a/core/codegen/src/attribute/route.rs b/core/codegen/src/attribute/route.rs index 1bb7d1d3..879fa298 100644 --- a/core/codegen/src/attribute/route.rs +++ b/core/codegen/src/attribute/route.rs @@ -369,19 +369,14 @@ fn generate_respond_expr(route: &Route) -> TokenStream2 { let parameter_names = route.inputs.iter() .map(|(_, rocket_ident, _)| rocket_ident); - let responder_stmt = if route.function.sig.asyncness.is_some() { - quote_spanned! { ret_span => - let ___responder = #user_handler_fn_name(#(#parameter_names),*).await; - } - } else { - quote_spanned! { ret_span => - let ___responder = #user_handler_fn_name(#(#parameter_names),*); - } + let _await = route.function.sig.asyncness.map(|a| quote_spanned!(a.span().into() => .await)); + let responder_stmt = quote_spanned! { ret_span => + let ___responder = #user_handler_fn_name(#(#parameter_names),*) #_await; }; quote_spanned! { ret_span => #responder_stmt - #handler::Outcome::from(#req, ___responder).await + #handler::Outcome::from(#req, ___responder) } } diff --git a/core/codegen/src/derive/responder.rs b/core/codegen/src/derive/responder.rs index 0067f8e8..3c7bae76 100644 --- a/core/codegen/src/derive/responder.rs +++ b/core/codegen/src/derive/responder.rs @@ -17,7 +17,11 @@ struct FieldAttr { } pub fn derive_responder(input: TokenStream) -> TokenStream { - DeriveGenerator::build_for(input, quote!(impl<'__r> ::rocket::response::Responder<'__r>)) + // NOTE: Due to a bug in devise, we can't do the more correct: + // quote!(impl<'__r, '__o: '__r> ::rocket::response::Responder<'__r, '__o>)) + // replace_generic(1, 0) + // A bugfix (on devise master) fixes this so the above works. Hack. + DeriveGenerator::build_for(input, quote!(impl<'__r> ::rocket::response::Responder<'__r, '__r>)) .generic_support(GenericSupport::Lifetime) .data_support(DataSupport::Struct | DataSupport::Enum) .replace_generic(0, 0) @@ -30,17 +34,15 @@ pub fn derive_responder(input: TokenStream) -> TokenStream { false => Ok(()) }) .function(|_, inner| quote! { - fn respond_to<'__i, '__x>( + fn respond_to( self, - __req: &'__r ::rocket::request::Request<'__i> - ) -> ::rocket::futures::future::BoxFuture<'__x, ::rocket::response::Result<'__r>> - where '__i: '__x, '__r: '__x, Self: '__x - { + __req: &'__r ::rocket::request::Request + ) -> ::rocket::response::Result<'__r> { #inner } }) .try_map_fields(|_, fields| { - define_vars_and_mods!(_Ok, _Box); + define_vars_and_mods!(_Ok); fn set_header_tokens(item: T) -> TokenStream2 { quote_spanned!(item.span().into() => __res.set_header(#item);) } @@ -53,7 +55,7 @@ pub fn derive_responder(input: TokenStream) -> TokenStream { quote_spanned! { f.span().into() => let mut __res = <#ty as ::rocket::response::Responder>::respond_to( #accessor, __req - ).await?; + )?; } }).expect("have at least one field"); @@ -73,13 +75,11 @@ pub fn derive_responder(input: TokenStream) -> TokenStream { }); Ok(quote! { - #_Box::pin(async move { - #responder - #(#headers)* - #content_type - #status - #_Ok(__res) - }) + #responder + #(#headers)* + #content_type + #status + #_Ok(__res) }) }) .to_tokens() diff --git a/core/codegen/tests/responder.rs b/core/codegen/tests/responder.rs index a4b24da8..e6469dc6 100644 --- a/core/codegen/tests/responder.rs +++ b/core/codegen/tests/responder.rs @@ -28,7 +28,7 @@ async fn responder_foo() { let req = local_req.inner(); let mut response = Foo::First("hello".into()) - .respond_to(req).await + .respond_to(req) .expect("response okay"); assert_eq!(response.status(), Status::Ok); @@ -36,7 +36,7 @@ async fn responder_foo() { assert_eq!(response.body_string().await, Some("hello".into())); let mut response = Foo::Second("just a test".into()) - .respond_to(req).await + .respond_to(req) .expect("response okay"); assert_eq!(response.status(), Status::InternalServerError); @@ -44,7 +44,7 @@ async fn responder_foo() { assert_eq!(response.body_string().await, Some("just a test".into())); let mut response = Foo::Third { responder: "well, hi", ct: ContentType::JSON } - .respond_to(req).await + .respond_to(req) .expect("response okay"); assert_eq!(response.status(), Status::NotFound); @@ -52,7 +52,7 @@ async fn responder_foo() { assert_eq!(response.body_string().await, Some("well, hi".into())); let mut response = Foo::Fourth { string: "goodbye", ct: ContentType::JSON } - .respond_to(req).await + .respond_to(req) .expect("response okay"); assert_eq!(response.status(), Status::raw(105)); @@ -81,7 +81,7 @@ async fn responder_bar() { other: ContentType::HTML, third: Cookie::new("cookie", "here!"), _yet_another: "uh..hi?".into() - }.respond_to(req).await.expect("response okay"); + }.respond_to(req).expect("response okay"); assert_eq!(response.status(), Status::InternalServerError); assert_eq!(response.content_type(), Some(ContentType::Plain)); @@ -102,7 +102,7 @@ async fn responder_baz() { let req = local_req.inner(); let mut response = Baz { responder: "just a custom" } - .respond_to(req).await + .respond_to(req) .expect("response okay"); assert_eq!(response.status(), Status::Ok); diff --git a/core/lib/benches/format-routing.rs b/core/lib/benches/format-routing.rs index c12a536b..808ad642 100644 --- a/core/lib/benches/format-routing.rs +++ b/core/lib/benches/format-routing.rs @@ -16,6 +16,7 @@ fn rocket() -> rocket::Rocket { rocket::custom(config.unwrap()).mount("/", routes![get, post]) } +#[allow(unused_must_use)] mod benches { extern crate test; @@ -24,30 +25,34 @@ mod benches { use rocket::local::Client; use rocket::http::{Accept, ContentType}; + fn client(_rocket: rocket::Rocket) -> Option { + unimplemented!("waiting for sync-client"); + } + #[bench] fn accept_format(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = client(rocket()).unwrap(); let mut request = client.get("/").header(Accept::JSON); b.iter(|| { request.mut_dispatch(); }); } #[bench] fn wrong_accept_format(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = client(rocket()).unwrap(); let mut request = client.get("/").header(Accept::HTML); b.iter(|| { request.mut_dispatch(); }); } #[bench] fn content_type_format(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = client(rocket()).unwrap(); let mut request = client.post("/").header(ContentType::JSON); b.iter(|| { request.mut_dispatch(); }); } #[bench] fn wrong_content_type_format(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = client(rocket()).unwrap(); let mut request = client.post("/").header(ContentType::Plain); b.iter(|| { request.mut_dispatch(); }); } diff --git a/core/lib/benches/ranked-routing.rs b/core/lib/benches/ranked-routing.rs index 7ef93980..aba5e4c4 100644 --- a/core/lib/benches/ranked-routing.rs +++ b/core/lib/benches/ranked-routing.rs @@ -30,6 +30,7 @@ fn rocket() -> rocket::Rocket { .mount("/", routes![post, post2, post3]) } +#[allow(unused_must_use)] mod benches { extern crate test; @@ -38,9 +39,13 @@ mod benches { use rocket::local::Client; use rocket::http::{Accept, ContentType}; + fn client(_rocket: rocket::Rocket) -> Option { + unimplemented!("waiting for sync-client"); + } + #[bench] fn accept_format(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = client(rocket()).unwrap(); let mut requests = vec![]; requests.push(client.get("/").header(Accept::JSON)); requests.push(client.get("/").header(Accept::HTML)); @@ -55,7 +60,7 @@ mod benches { #[bench] fn content_type_format(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = client(rocket()).unwrap(); let mut requests = vec![]; requests.push(client.post("/").header(ContentType::JSON)); requests.push(client.post("/").header(ContentType::HTML)); diff --git a/core/lib/benches/simple-routing.rs b/core/lib/benches/simple-routing.rs index 804dfcdd..b63474ed 100644 --- a/core/lib/benches/simple-routing.rs +++ b/core/lib/benches/simple-routing.rs @@ -42,6 +42,7 @@ fn rocket() -> rocket::Rocket { index_b, index_c, index_dyn_a]) } +#[allow(unused_must_use)] mod benches { extern crate test; @@ -49,9 +50,13 @@ mod benches { use self::test::Bencher; use rocket::local::Client; + fn client(_rocket: rocket::Rocket) -> Option { + unimplemented!("waiting for sync-client"); + } + #[bench] fn bench_hello_world(b: &mut Bencher) { - let client = Client::new(hello_world_rocket()).unwrap(); + let client = client(hello_world_rocket()).unwrap(); let mut request = client.get("/"); b.iter(|| { @@ -61,7 +66,7 @@ mod benches { #[bench] fn bench_single_get_index(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = client(rocket()).unwrap(); let mut request = client.get("/"); b.iter(|| { @@ -71,7 +76,7 @@ mod benches { #[bench] fn bench_get_put_post_index(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = client(rocket()).unwrap(); // Hold all of the requests we're going to make during the benchmark. let mut requests = vec![]; @@ -88,7 +93,7 @@ mod benches { #[bench] fn bench_dynamic(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = client(rocket()).unwrap(); // Hold all of the requests we're going to make during the benchmark. let mut requests = vec![]; @@ -105,7 +110,7 @@ mod benches { #[bench] fn bench_simple_routing(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = client(rocket()).unwrap(); // Hold all of the requests we're going to make during the benchmark. let mut requests = vec![]; diff --git a/core/lib/src/catcher.rs b/core/lib/src/catcher.rs index 40061d72..89e2da08 100644 --- a/core/lib/src/catcher.rs +++ b/core/lib/src/catcher.rs @@ -153,10 +153,10 @@ macro_rules! default_catchers { let mut map = HashMap::new(); $( - fn $fn_name<'r>(req: &'r Request<'_>) -> futures::future::BoxFuture<'r, response::Result<'r>> { - status::Custom(Status::from_code($code).unwrap(), - content::Html(error_page_template!($code, $name, $description)) - ).respond_to(req) + fn $fn_name<'r>(req: &'r Request<'_>) -> crate::handler::ErrorHandlerFuture<'r> { + let status = Status::from_code($code).unwrap(); + let html = content::Html(error_page_template!($code, $name, $description)); + Box::pin(async move { status::Custom(status, html).respond_to(req) }) } map.insert($code, Catcher::new_default($code, $fn_name)); @@ -172,7 +172,7 @@ pub mod defaults { use std::collections::HashMap; use crate::request::Request; - use crate::response::{self, content, status, Responder}; + use crate::response::{content, status, Responder}; use crate::http::Status; pub fn get() -> HashMap { @@ -243,4 +243,3 @@ pub mod defaults { } } } - diff --git a/core/lib/src/fairing/ad_hoc.rs b/core/lib/src/fairing/ad_hoc.rs index 59041013..4d085673 100644 --- a/core/lib/src/fairing/ad_hoc.rs +++ b/core/lib/src/fairing/ad_hoc.rs @@ -44,16 +44,39 @@ pub struct AdHoc { kind: AdHocKind, } +// macro_rules! Async { +// ($kind:ident <$l:lifetime> ($($param:ty),*) -> $r:ty) => ( +// dyn for<$l> $kind($($param),*) -> futures::future::BoxFuture<$l, $r> +// + Send + 'static +// ); +// ($kind:ident ($($param:ty),*) -> $r:ty) => ( +// dyn $kind($($param),*) -> futures::future::BoxFuture<'static, $r> +// + Send + Sync + 'static +// ); +// ($kind:ident <$l:lifetime> ($($param:ty),*)) => ( +// Async!($kind <$l> ($($param),*) -> ()) +// ); +// ($kind:ident ($($param:ty),*)) => ( +// Async!($kind ($($param),*) -> ()) +// ); +// } + enum AdHocKind { /// An ad-hoc **attach** fairing. Called when the fairing is attached. - Attach(Mutex BoxFuture<'static, Result> + Send + 'static>>>), + Attach(Mutex BoxFuture<'static, Result> + Send + 'static>>>), + /// An ad-hoc **launch** fairing. Called just before Rocket launches. Launch(Mutex>>), + /// An ad-hoc **request** fairing. Called when a request is received. - Request(Box Fn(&'a mut Request<'_>, &'a Data) -> BoxFuture<'a, ()> + Send + Sync + 'static>), + 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 /// sent to a client. - Response(Box Fn(&'a Request<'_>, &'a mut Response<'_>) -> BoxFuture<'a, ()> + Send + Sync + 'static>), + Response(Box Fn(&'a Request<'_>, &'a mut Response<'_>) + -> BoxFuture<'a, ()> + Send + Sync + 'static>), } impl AdHoc { @@ -73,7 +96,10 @@ impl AdHoc { F: FnOnce(Rocket) -> Fut + Send + 'static, Fut: Future> + Send + 'static, { - AdHoc { name, kind: AdHocKind::Attach(Mutex::new(Some(Box::new(|rocket| Box::pin(f(rocket)))))) } + AdHoc { + name, + kind: AdHocKind::Attach(Mutex::new(Some(Box::new(|rocket| Box::pin(f(rocket)))))) + } } /// Constructs an `AdHoc` launch fairing named `name`. The function `f` will @@ -117,6 +143,17 @@ impl AdHoc { { AdHoc { name, kind: AdHocKind::Request(Box::new(f)) } } + // // FIXME: Can the generated future hold references to the request with this? + // pub fn on_request(name: &'static str, f: F) -> AdHoc + // where + // F: for<'a> Fn(&'a mut Request<'_>, &'a Data) -> Fut + Send + Sync + 'static, + // Fut: Future + Send + 'static, + // { + // AdHoc { + // name, + // kind: AdHocKind::Request(Box::new(|req, data| Box::pin(f(req, data)))) + // } + // } /// Constructs an `AdHoc` response fairing named `name`. The function `f` /// will be called and the returned `Future` will be `await`ed by Rocket diff --git a/core/lib/src/fairing/mod.rs b/core/lib/src/fairing/mod.rs index 0dbd66ec..728a597e 100644 --- a/core/lib/src/fairing/mod.rs +++ b/core/lib/src/fairing/mod.rs @@ -287,7 +287,7 @@ pub use self::info_kind::{Info, Kind}; /// let body = format!("Get: {}\nPost: {}", get_count, post_count); /// res.set_status(Status::Ok); /// res.set_header(ContentType::Plain); -/// res.set_sized_body(Cursor::new(body)); +/// res.set_sized_body(body.len(), Cursor::new(body)); /// } /// } /// } diff --git a/core/lib/src/handler.rs b/core/lib/src/handler.rs index 8d346270..c0eadd92 100644 --- a/core/lib/src/handler.rs +++ b/core/lib/src/handler.rs @@ -44,13 +44,13 @@ pub type HandlerFuture<'r> = BoxFuture<'r, Outcome<'r>>; /// ```rust,no_run /// # #[derive(Copy, Clone)] enum Kind { Simple, Intermediate, Complex, } /// use rocket::{Request, Data, Route, http::Method}; -/// use rocket::handler::{self, Handler, Outcome, HandlerFuture}; +/// use rocket::handler::{self, Handler, Outcome}; /// /// #[derive(Clone)] /// struct CustomHandler(Kind); /// /// impl Handler for CustomHandler { -/// fn handle<'r>(&self, req: &'r Request, data: Data) -> HandlerFuture<'r> { +/// fn handle<'r>(&self, req: &'r Request, data: Data) -> Outcome<'r> { /// match self.0 { /// Kind::Simple => Outcome::from(req, "simple"), /// Kind::Intermediate => Outcome::from(req, "intermediate"), @@ -184,7 +184,7 @@ pub type ErrorHandler = for<'r> fn(&'r Request<'_>) -> ErrorHandlerFuture<'r>; /// Type type of `Future` returned by an error handler. pub type ErrorHandlerFuture<'r> = BoxFuture<'r, response::Result<'r>>; -impl<'r> Outcome<'r> { +impl<'r, 'o: 'r> Outcome<'o> { /// Return the `Outcome` of response to `req` from `responder`. /// /// If the responder returns `Ok`, an outcome of `Success` is @@ -195,20 +195,18 @@ impl<'r> Outcome<'r> { /// /// ```rust /// use rocket::{Request, Data}; - /// use rocket::handler::{Outcome, HandlerFuture}; + /// use rocket::handler::Outcome; /// - /// fn str_responder<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> { + /// fn str_responder<'r>(req: &'r Request, _: Data) -> Outcome<'r> { /// Outcome::from(req, "Hello, world!") /// } /// ``` #[inline] - pub fn from + Send + 'r>(req: &'r Request<'_>, responder: T) -> HandlerFuture<'r> { - Box::pin(async move { - match responder.respond_to(req).await { - Ok(response) => outcome::Outcome::Success(response), - Err(status) => outcome::Outcome::Failure(status) - } - }) + pub fn from>(req: &'r Request<'_>, responder: R) -> Outcome<'o> { + match responder.respond_to(req) { + Ok(response) => outcome::Outcome::Success(response), + Err(status) => outcome::Outcome::Failure(status) + } } /// Return the `Outcome` of response to `req` from `responder`. @@ -221,23 +219,21 @@ impl<'r> Outcome<'r> { /// /// ```rust /// use rocket::{Request, Data}; - /// use rocket::handler::{Outcome, HandlerFuture}; + /// use rocket::handler::Outcome; /// - /// fn str_responder<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> { + /// fn str_responder<'r>(req: &'r Request, _: Data) -> Outcome<'r> { /// Outcome::from(req, "Hello, world!") /// } /// ``` #[inline] - pub fn try_from(req: &'r Request<'_>, result: Result) -> HandlerFuture<'r> - where T: Responder<'r> + Send + 'r, E: std::fmt::Debug + Send + 'r + pub fn try_from(req: &'r Request<'_>, result: Result) -> Outcome<'o> + where R: Responder<'r, 'o>, E: std::fmt::Debug { - Box::pin(async move { - let responder = result.map_err(crate::response::Debug); - match responder.respond_to(req).await { - Ok(response) => outcome::Outcome::Success(response), - Err(status) => outcome::Outcome::Failure(status) - } - }) + let responder = result.map_err(crate::response::Debug); + match responder.respond_to(req) { + Ok(response) => outcome::Outcome::Success(response), + Err(status) => outcome::Outcome::Failure(status) + } } /// Return the `Outcome` of response to `req` from `responder`. @@ -250,22 +246,20 @@ impl<'r> Outcome<'r> { /// /// ```rust /// use rocket::{Request, Data}; - /// use rocket::handler::{Outcome, HandlerFuture}; + /// use rocket::handler::Outcome; /// - /// fn str_responder<'r>(req: &'r Request, data: Data) -> HandlerFuture<'r> { + /// fn str_responder<'r>(req: &'r Request, data: Data) -> Outcome<'r> { /// Outcome::from_or_forward(req, data, "Hello, world!") /// } /// ``` #[inline] - pub fn from_or_forward(req: &'r Request<'_>, data: Data, responder: T) -> HandlerFuture<'r> - where T: Responder<'r> + Send + pub fn from_or_forward(req: &'r Request<'_>, data: Data, responder: R) -> Outcome<'o> + where R: Responder<'r, 'o> { - Box::pin(async move { - match responder.respond_to(req).await { - Ok(response) => outcome::Outcome::Success(response), - Err(_) => outcome::Outcome::Forward(data) - } - }) + match responder.respond_to(req) { + Ok(response) => outcome::Outcome::Success(response), + Err(_) => outcome::Outcome::Forward(data) + } } /// Return an `Outcome` of `Failure` with the status code `code`. This is @@ -278,13 +272,11 @@ impl<'r> Outcome<'r> { /// /// ```rust /// use rocket::{Request, Data}; - /// use rocket::handler::{Outcome, HandlerFuture}; + /// use rocket::handler::Outcome; /// use rocket::http::Status; /// - /// fn bad_req_route<'r>(_: &'r Request, _: Data) -> HandlerFuture<'r> { - /// Box::pin(async move { - /// Outcome::failure(Status::BadRequest) - /// }) + /// fn bad_req_route<'r>(_: &'r Request, _: Data) -> Outcome<'r> { + /// Outcome::failure(Status::BadRequest) /// } /// ``` #[inline(always)] @@ -302,12 +294,10 @@ impl<'r> Outcome<'r> { /// /// ```rust /// use rocket::{Request, Data}; - /// use rocket::handler::{Outcome, HandlerFuture}; + /// use rocket::handler::Outcome; /// - /// fn always_forward<'r>(_: &'r Request, data: Data) -> HandlerFuture<'r> { - /// Box::pin(async move { - /// Outcome::forward(data) - /// }) + /// fn always_forward<'r>(_: &'r Request, data: Data) -> Outcome<'r> { + /// Outcome::forward(data) /// } /// ``` #[inline(always)] diff --git a/core/lib/src/outcome.rs b/core/lib/src/outcome.rs index d56cd0eb..642afdf9 100644 --- a/core/lib/src/outcome.rs +++ b/core/lib/src/outcome.rs @@ -601,6 +601,12 @@ impl Outcome { } } } +impl<'a, S: Send + 'a, E: Send + 'a, F: Send + 'a> Outcome { + #[inline] + pub fn pin(self) -> futures::future::BoxFuture<'a, Self> { + Box::pin(async move { self }) + } +} /// Unwraps an [`Outcome`] to its success value, otherwise propagating the /// forward or failure. diff --git a/core/lib/src/request/request.rs b/core/lib/src/request/request.rs index ba9396ef..6f24c363 100644 --- a/core/lib/src/request/request.rs +++ b/core/lib/src/request/request.rs @@ -294,7 +294,11 @@ impl<'r> Request<'r> { match guard.take() { Some(jar) => { let mutex = &self.state.cookies; - Cookies::new(jar, self.state.config.secret_key(), move |jar| *mutex.lock().expect("cookies lock") = Some(jar)) + let on_drop = move |jar| { + *mutex.lock().expect("cookies lock") = Some(jar); + }; + + Cookies::new(jar, self.state.config.secret_key(), on_drop) } None => { error_!("Multiple `Cookies` instances are active at once."); @@ -537,6 +541,13 @@ impl<'r> Request<'r> { T::from_request(self) } + #[inline(always)] + pub fn managed_state(&self) -> Option<&'r T> + where T: Send + Sync + 'static + { + self.state.managed.try_get::() + } + /// Retrieves the cached value for type `T` from the request-local cached /// state of `self`. If no such value has previously been cached for this /// request, `f` is called to produce the value which is subsequently diff --git a/core/lib/src/response/content.rs b/core/lib/src/response/content.rs index 1f20de26..94eed797 100644 --- a/core/lib/src/response/content.rs +++ b/core/lib/src/response/content.rs @@ -46,12 +46,10 @@ pub struct Content(pub ContentType, pub R); /// Overrides the Content-Type of the response to the wrapped `ContentType` then /// delegates the remainder of the response to the wrapped responder. -#[crate::async_trait] -impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Content { - #[inline(always)] - async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> { +impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Content { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { Response::build() - .merge(self.1.respond_to(req).await?) + .merge(self.1.respond_to(req)?) .header(self.0) .ok() } @@ -59,8 +57,6 @@ impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Content { macro_rules! ctrs { ($($name:ident: $ct:ident, $name_str:expr, $ct_str:expr),+) => { - use futures::future::BoxFuture; - $( #[doc="Override the `Content-Type` of the response to "] #[doc=$name_str] @@ -74,10 +70,8 @@ macro_rules! ctrs { /// Sets the Content-Type of the response then delegates the /// remainder of the response to the wrapped responder. - impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for $name { - fn respond_to<'a, 'x>(self, req: &'r Request<'a>) -> BoxFuture<'x, response::Result<'r>> - where 'a: 'x, 'r: 'x, Self: 'x - { + impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for $name { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { Content(ContentType::$ct, self.0).respond_to(req) } } diff --git a/core/lib/src/response/debug.rs b/core/lib/src/response/debug.rs index 2e51bc34..47a8e13d 100644 --- a/core/lib/src/response/debug.rs +++ b/core/lib/src/response/debug.rs @@ -63,9 +63,8 @@ impl From for Debug { } } -#[crate::async_trait] -impl<'r, E: std::fmt::Debug + Send + 'r> Responder<'r> for Debug { - async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> { +impl<'r, E: std::fmt::Debug> Responder<'r, 'static> for Debug { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { warn_!("Debug: {:?}", Paint::default(self.0)); warn_!("Debug always responds with {}.", Status::InternalServerError); Response::build().status(Status::InternalServerError).ok() diff --git a/core/lib/src/response/flash.rs b/core/lib/src/response/flash.rs index 686c3d21..666edd0f 100644 --- a/core/lib/src/response/flash.rs +++ b/core/lib/src/response/flash.rs @@ -99,7 +99,7 @@ pub struct Flash { /// [`msg()`]: Flash::msg() pub type FlashMessage<'a, 'r> = crate::response::Flash<&'a Request<'r>>; -impl<'r, R: Responder<'r>> Flash { +impl Flash { /// Constructs a new `Flash` message with the given `name`, `msg`, and /// underlying `responder`. /// @@ -192,12 +192,11 @@ impl<'r, R: Responder<'r>> Flash { /// response. In other words, simply sets a cookie and delegates the rest of the /// response handling to the wrapped responder. As a result, the `Outcome` of /// the response is the `Outcome` of the wrapped `Responder`. -#[crate::async_trait] -impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Flash { - async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> { +impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Flash { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { trace_!("Flash: setting message: {}:{}", self.name, self.message); req.cookies().add(self.cookie()); - self.inner.respond_to(req).await + self.inner.respond_to(req) } } diff --git a/core/lib/src/response/mod.rs b/core/lib/src/response/mod.rs index ceedf04e..8688d3da 100644 --- a/core/lib/src/response/mod.rs +++ b/core/lib/src/response/mod.rs @@ -37,7 +37,8 @@ pub mod status; #[doc(hidden)] pub use rocket_codegen::Responder; -pub use self::response::{Response, ResponseBuilder, Body, DEFAULT_CHUNK_SIZE}; +pub use self::response::DEFAULT_CHUNK_SIZE; +pub use self::response::{Response, ResponseBody, ResponseBuilder, Body}; pub use self::responder::Responder; pub use self::redirect::Redirect; pub use self::flash::Flash; @@ -47,4 +48,4 @@ pub use self::debug::Debug; #[doc(inline)] pub use self::content::Content; /// Type alias for the `Result` of a [`Responder::respond_to()`] call. -pub type Result<'r> = std::result::Result, crate::http::Status>; +pub type Result<'r> = std::result::Result, crate::http::Status>; diff --git a/core/lib/src/response/named_file.rs b/core/lib/src/response/named_file.rs index ed993c43..90ffa546 100644 --- a/core/lib/src/response/named_file.rs +++ b/core/lib/src/response/named_file.rs @@ -78,10 +78,9 @@ impl NamedFile { /// recognized. See [`ContentType::from_extension()`] for more information. If /// you would like to stream a file with a different Content-Type than that /// implied by its extension, use a [`File`] directly. -#[crate::async_trait] -impl<'r> Responder<'r> for NamedFile { - async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> { - let mut response = self.1.respond_to(req).await?; +impl<'r> Responder<'r, 'static> for NamedFile { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { + let mut response = self.1.respond_to(req)?; if let Some(ext) = self.0.extension() { if let Some(ct) = ContentType::from_extension(&ext.to_string_lossy()) { response.set_header(ct); diff --git a/core/lib/src/response/redirect.rs b/core/lib/src/response/redirect.rs index 01ad5924..730fd01b 100644 --- a/core/lib/src/response/redirect.rs +++ b/core/lib/src/response/redirect.rs @@ -147,9 +147,8 @@ impl Redirect { /// the `Location` header field. The body of the response is empty. If the URI /// value used to create the `Responder` is an invalid URI, an error of /// `Status::InternalServerError` is returned. -#[crate::async_trait] -impl<'r> Responder<'r> for Redirect { - async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> { +impl<'r> Responder<'r, 'static> for Redirect { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { if let Some(uri) = self.1 { Response::build() .status(self.0) diff --git a/core/lib/src/response/responder.rs b/core/lib/src/response/responder.rs index 350137d0..6058f783 100644 --- a/core/lib/src/response/responder.rs +++ b/core/lib/src/response/responder.rs @@ -1,11 +1,8 @@ use std::fs::File; use std::io::Cursor; -use tokio::io::BufReader; -use futures::future::BoxFuture; - use crate::http::{Status, ContentType, StatusClass}; -use crate::response::{self, Response, Body}; +use crate::response::{self, Response}; use crate::request::Request; /// Trait implemented by types that generate responses for clients. @@ -113,44 +110,15 @@ use crate::request::Request; /// regardless of the incoming request. Thus, knowing the type is sufficient to /// fully determine its functionality. /// -/// # Async Trait +/// ## Lifetimes /// -/// [`Responder`] is an _async_ trait. Implementations of `Responder` may be -/// decorated with an attribute of `#[rocket::async_trait]`, allowing the -/// `respond_to` method to be implemented as an `async fn`: -/// -/// ```rust -/// use rocket::response::{self, Responder}; -/// use rocket::request::Request; -/// # struct MyType; -/// -/// #[rocket::async_trait] -/// impl<'r> Responder<'r> for MyType { -/// async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> { -/// /* .. */ -/// # unimplemented!() -/// } -/// } -/// ``` -/// -/// In certain cases, including when implementing a _wrapping_ responder, it may -/// be desirable to perform work before entering an `async` section, or to -/// return a future directly. In this case, `Responder` can be implemented as: -/// -/// ```rust -/// use rocket::response::{self, Responder}; -/// use rocket::futures::future::BoxFuture; -/// use rocket::request::Request; -/// # struct MyType { inner: R }; -/// -/// impl<'r, R: Responder<'r>> Responder<'r> for MyType { -/// fn respond_to<'a, 'x>(self, req: &'r Request<'a>) -> BoxFuture<'x, response::Result<'r>> -/// where 'a: 'x, 'r: 'x, Self: 'x -/// { -/// self.inner.respond_to(req) -/// } -/// } -/// ``` +/// `Responder` has two lifetimes: `Responder<'r, 'o: 'r>`. The first lifetime, +/// `'r`, refers to the reference to the `&'r Request`, while the second +/// lifetime refers to the returned `Response<'o>`. The bound `'o: 'r` allows +/// `'o` to be any lifetime that lives at least as long as the `Request`. In +/// particular, this includes borrows from the `Request` itself (where `'o` would +/// be `'r` as in `impl<'r> Responder<'r, 'r>`) as well as `'static` data (where +/// `'o` would be `'static` as in `impl<'r> Responder<'r, 'static>`). /// /// # Example /// @@ -196,12 +164,11 @@ use crate::request::Request; /// use rocket::response::{self, Response, Responder}; /// use rocket::http::ContentType; /// -/// #[rocket::async_trait] -/// impl<'r> Responder<'r> for Person { -/// async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> { +/// impl<'r> Responder<'r, 'static> for Person { +/// fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { /// let person_string = format!("{}:{}", self.name, self.age); /// Response::build() -/// .sized_body(Cursor::new(person_string)).await +/// .sized_body(person_string.len(), Cursor::new(person_string)) /// .raw_header("X-Person-Name", self.name) /// .raw_header("X-Person-Age", self.age.to_string()) /// .header(ContentType::new("application", "x-person")) @@ -213,8 +180,7 @@ use crate::request::Request; /// # fn person() -> Person { Person { name: "a".to_string(), age: 20 } } /// # fn main() { } /// ``` -#[crate::async_trait] -pub trait Responder<'r> { +pub trait Responder<'r, 'o: 'r> { /// Returns `Ok` if a `Response` could be generated successfully. Otherwise, /// returns an `Err` with a failing `Status`. /// @@ -226,97 +192,83 @@ pub trait Responder<'r> { /// returned, the error catcher for the given status is retrieved and called /// to generate a final error response, which is then written out to the /// client. - async fn respond_to(self, request: &'r Request<'_>) -> response::Result<'r>; + fn respond_to(self, request: &'r Request<'_>) -> response::Result<'o>; } /// Returns a response with Content-Type `text/plain` and a fixed-size body /// containing the string `self`. Always returns `Ok`. -#[crate::async_trait] -impl<'r> Responder<'r> for &'r str { - async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> { +impl<'r, 'o: 'r> Responder<'r, 'o> for &'o str { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> { Response::build() .header(ContentType::Plain) - .sized_body(Cursor::new(self)).await + .sized_body(self.len(), Cursor::new(self)) .ok() } } /// Returns a response with Content-Type `text/plain` and a fixed-size body /// containing the string `self`. Always returns `Ok`. -#[crate::async_trait] -impl<'r> Responder<'r> for String { - async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> { +impl<'r> Responder<'r, 'static> for String { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { Response::build() .header(ContentType::Plain) - .sized_body(Cursor::new(self)).await + .sized_body(self.len(), Cursor::new(self)) .ok() } } /// Returns a response with Content-Type `application/octet-stream` and a /// fixed-size body containing the data in `self`. Always returns `Ok`. -#[crate::async_trait] -impl<'r> Responder<'r> for &'r [u8] { - async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> { +impl<'r, 'o: 'r> Responder<'r, 'o> for &'o [u8] { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> { Response::build() .header(ContentType::Binary) - .sized_body(Cursor::new(self)).await + .sized_body(self.len(), Cursor::new(self)) .ok() } } /// Returns a response with Content-Type `application/octet-stream` and a /// fixed-size body containing the data in `self`. Always returns `Ok`. -#[crate::async_trait] -impl<'r> Responder<'r> for Vec { - async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> { +impl<'r> Responder<'r, 'static> for Vec { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { Response::build() .header(ContentType::Binary) - .sized_body(Cursor::new(self)).await + .sized_body(self.len(), Cursor::new(self)) .ok() } } /// Returns a response with a sized body for the file. Always returns `Ok`. -#[crate::async_trait] -impl<'r> Responder<'r> for File { - async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> { - tokio::fs::File::from(self).respond_to(req).await +impl<'r> Responder<'r, 'static> for File { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { + tokio::fs::File::from(self).respond_to(req) } } /// Returns a response with a sized body for the file. Always returns `Ok`. -#[crate::async_trait] -impl<'r> Responder<'r> for tokio::fs::File { - async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> { - let metadata = self.metadata().await; - let stream = BufReader::new(self); - match metadata { - Ok(md) => Response::build().raw_body(Body::Sized(stream, md.len())).ok(), - Err(_) => Response::build().streamed_body(stream).ok() - } +impl<'r> Responder<'r, 'static> for tokio::fs::File { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { + Response::build().sized_body(None, self).ok() } } /// Returns an empty, default `Response`. Always returns `Ok`. -#[crate::async_trait] -impl<'r> Responder<'r> for () { - async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> { +impl<'r> Responder<'r, 'static> for () { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { Ok(Response::new()) } } /// If `self` is `Some`, responds with the wrapped `Responder`. Otherwise prints /// a warning message and returns an `Err` of `Status::NotFound`. -impl<'r, R: Responder<'r>> Responder<'r> for Option { - fn respond_to<'a, 'x>(self, req: &'r Request<'a>) -> BoxFuture<'x, response::Result<'r>> - where 'a: 'x, 'r: 'x, Self: 'x - { +impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Option { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { match self { Some(r) => r.respond_to(req), None => { warn_!("Response was `None`."); - Box::pin(async { Err(Status::NotFound) }) + Err(Status::NotFound) }, } } @@ -324,10 +276,10 @@ impl<'r, R: Responder<'r>> Responder<'r> for Option { // Responds with the wrapped `Responder` in `self`, whether it is `Ok` or /// `Err`. -impl<'r, R: Responder<'r>, E: Responder<'r>> Responder<'r> for Result { - fn respond_to<'a, 'x>(self, req: &'r Request<'a>) -> BoxFuture<'x, response::Result<'r>> - where 'a: 'x, 'r: 'x, Self: 'x - { +impl<'r, 'o: 'r, 't: 'o, 'e: 'o, T, E> Responder<'r, 'o> for Result + where T: Responder<'r, 't>, E: Responder<'r, 'e> +{ + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { match self { Ok(responder) => responder.respond_to(req), Err(responder) => responder.respond_to(req), @@ -349,9 +301,8 @@ impl<'r, R: Responder<'r>, E: Responder<'r>> Responder<'r> for Result { /// `100` responds with any empty body and the given status code, and all other /// status code emit an error message and forward to the `500` (internal server /// error) catcher. -#[crate::async_trait] -impl<'r> Responder<'r> for Status { - async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> { +impl<'r> Responder<'r, 'static> for Status { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { match self.class() { StatusClass::ClientError | StatusClass::ServerError => Err(self), StatusClass::Success if self.code < 206 => { diff --git a/core/lib/src/response/response.rs b/core/lib/src/response/response.rs index 2606f3aa..77b81e09 100644 --- a/core/lib/src/response/response.rs +++ b/core/lib/src/response/response.rs @@ -8,40 +8,33 @@ use crate::response::{self, Responder}; use crate::http::{Header, HeaderMap, Status, ContentType, Cookie}; /// The default size, in bytes, of a chunk for streamed responses. -pub const DEFAULT_CHUNK_SIZE: u64 = 4096; +pub const DEFAULT_CHUNK_SIZE: usize = 4096; -#[derive(PartialEq, Clone, Hash)] /// The body of a response: can be sized or streamed/chunked. -pub enum Body { +pub enum Body { /// A fixed-size body. - Sized(T, u64), + Sized(A, Option), /// A streamed/chunked body, akin to `Transfer-Encoding: chunked`. - Chunked(T, u64) + Chunked(B, usize) } -impl Body { +impl Body { /// Returns a new `Body` with a mutable borrow to `self`'s inner type. - pub fn as_mut(&mut self) -> Body<&mut T> { + pub fn as_mut(&mut self) -> Body<&mut A, &mut B> { match *self { - Body::Sized(ref mut b, n) => Body::Sized(b, n), + Body::Sized(ref mut a, n) => Body::Sized(a, n), Body::Chunked(ref mut b, n) => Body::Chunked(b, n) } } - /// Consumes `self`. Passes the inner type as a parameter to `f` and - /// constructs a new body with the size of `self` and the return value of - /// the call to `f`. - pub fn map U>(self, f: F) -> Body { + /// Consumes `self`. Passes the inner types as parameter to `f1` and `f2` + /// and constructs a new body with the values returned from calls to the + /// functions. The size or chunk size of the body is copied into the new + /// `Body`. + pub fn map U, F2: FnOnce(B) -> U>(self, f1: F1, f2: F2) -> Body { match self { - Body::Sized(b, n) => Body::Sized(f(b), n), - Body::Chunked(b, n) => Body::Chunked(f(b), n) - } - } - - /// Consumes `self` and returns the inner body. - pub fn into_inner(self) -> T { - match self { - Body::Sized(b, _) | Body::Chunked(b, _) => b + Body::Sized(a, n) => Body::Sized(f1(a), n), + Body::Chunked(b, n) => Body::Chunked(f2(b), n) } } @@ -62,13 +55,50 @@ impl Body { } } -impl Body { +impl Body { + /// Consumes `self` and returns the inner body. + pub fn into_inner(self) -> T { + match self { + Body::Sized(b, _) | Body::Chunked(b, _) => b + } + } +} + +impl Body + where A: AsyncRead + AsyncSeek + Send + Unpin, + B: AsyncRead + Send + Unpin +{ + /// Attempts to compute the size of `self` if it is `Body::Sized`. If it is + /// not, simply returned `None`. Also returned `None` if determining the + /// body's size failed. + pub async fn size(&mut self) -> Option { + if let Body::Sized(body, size) = self { + match *size { + Some(size) => Some(size), + None => async { + let pos = body.seek(io::SeekFrom::Current(0)).await.ok()?; + let end = body.seek(io::SeekFrom::End(0)).await.ok()?; + body.seek(io::SeekFrom::Start(pos)).await.ok()?; + Some(end as usize - pos as usize) + }.await + } + } else { + None + } + } + + /// Monomorphizes the internal readers into a single `&mut (dyn AsyncRead + + /// Send + Unpin)`. + pub fn as_reader(&mut self) -> &mut (dyn AsyncRead + Send + Unpin) { + type Reader<'a> = &'a mut (dyn AsyncRead + Send + Unpin); + self.as_mut().map(|a| a as Reader<'_>, |b| b as Reader<'_>).into_inner() + } + /// Attempts to read `self` into a `Vec` and returns it. If reading fails, /// returns `None`. - pub async fn into_bytes(self) -> Option> { + pub async fn into_bytes(mut self) -> Option> { let mut vec = Vec::new(); - let mut body = self.into_inner(); - if let Err(e) = body.read_to_end(&mut vec).await { + if let Err(e) = self.as_reader().read_to_end(&mut vec).await { error_!("Error reading body: {:?}", e); return None; } @@ -89,10 +119,10 @@ impl Body { } } -impl fmt::Debug for Body { +impl fmt::Debug for Body { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - Body::Sized(_, n) => writeln!(f, "Sized Body [{} bytes]", n), + Body::Sized(_, n) => writeln!(f, "Sized Body [{:?} bytes]", n), Body::Chunked(_, n) => writeln!(f, "Chunked Body [{} bytes]", n), } } @@ -147,16 +177,15 @@ impl fmt::Debug for Body { /// use rocket::response::Response; /// use rocket::http::{Status, ContentType}; /// -/// # rocket::async_test(async { +/// let body = "Brewing the best coffee!"; /// let response = Response::build() /// .status(Status::ImATeapot) /// .header(ContentType::Plain) /// .raw_header("X-Teapot-Make", "Rocket") /// .raw_header("X-Teapot-Model", "Utopia") /// .raw_header_adjoin("X-Teapot-Model", "Series 1") -/// .sized_body(Cursor::new("Brewing the best coffee!")).await +/// .sized_body(body.len(), Cursor::new(body)) /// .finalize(); -/// # }); /// ``` pub struct ResponseBuilder<'r> { response: Response<'r>, @@ -332,7 +361,10 @@ impl<'r> ResponseBuilder<'r> { self } - /// Sets the body of the `Response` to be the fixed-sized `body`. + /// Sets the body of the `Response` to be the fixed-sized `body` with size + /// `size`, which may be `None`. If `size` is `None`, the body's size will + /// be computing with calls to `seek` just before being written out in a + /// response. /// /// # Example /// @@ -340,16 +372,16 @@ impl<'r> ResponseBuilder<'r> { /// use std::io::Cursor; /// use rocket::Response; /// - /// # rocket::async_test(async { + /// let body = "Hello, world!"; /// let response = Response::build() - /// .sized_body(Cursor::new("Hello, world!")).await + /// .sized_body(body.len(), Cursor::new(body)) /// .finalize(); - /// # }) /// ``` - pub async fn sized_body(&mut self, body: B) -> &mut ResponseBuilder<'r> - where B: AsyncRead + AsyncSeek + Send + Unpin + 'r + pub fn sized_body(&mut self, size: S, body: B) -> &mut ResponseBuilder<'r> + where B: AsyncRead + AsyncSeek + Send + Unpin + 'r, + S: Into> { - self.response.set_sized_body(body).await; + self.response.set_sized_body(size, body); self } @@ -391,7 +423,7 @@ impl<'r> ResponseBuilder<'r> { /// # } /// ``` #[inline(always)] - pub fn chunked_body(&mut self, body: B, chunk_size: u64) -> &mut ResponseBuilder<'r> + pub fn chunked_body(&mut self, body: B, chunk_size: usize) -> &mut ResponseBuilder<'r> where B: AsyncRead + Send + 'r { self.response.set_chunked_body(body, chunk_size); @@ -408,15 +440,16 @@ impl<'r> ResponseBuilder<'r> { /// use std::io::Cursor; /// use rocket::response::{Response, Body}; /// - /// # rocket::async_test(async { + /// let s = "Hello!"; + /// let body = Body::Sized(Cursor::new(s), Some(s.len())); /// let response = Response::build() - /// .raw_body(Body::Sized(Cursor::new("Hello!"), 6)) + /// .raw_body::, Cursor<&'static str>>(body) /// .finalize(); - /// # }) /// ``` #[inline(always)] - pub fn raw_body(&mut self, body: Body) -> &mut ResponseBuilder<'r> - where T: AsyncRead + Send + Unpin + 'r + pub fn raw_body(&mut self, body: Body) -> &mut ResponseBuilder<'r> + where S: AsyncRead + AsyncSeek + Send + Unpin + 'r, + C: AsyncRead + Send + Unpin + 'r { self.response.set_raw_body(body); self @@ -513,13 +546,12 @@ impl<'r> ResponseBuilder<'r> { /// use rocket::Response; /// use rocket::http::Status; /// - /// # rocket::async_test(async { + /// let body = "Brewing the best coffee!"; /// let response = Response::build() /// .status(Status::ImATeapot) - /// .sized_body(Cursor::new("Brewing the best coffee!")).await + /// .sized_body(body.len(), Cursor::new(body)) /// .raw_header("X-Custom", "value 2") /// .finalize(); - /// # }) /// ``` pub fn finalize(&mut self) -> Response<'r> { std::mem::replace(&mut self.response, Response::new()) @@ -545,12 +577,20 @@ impl<'r> ResponseBuilder<'r> { } } +pub trait AsyncReadSeek: AsyncRead + AsyncSeek { } +impl AsyncReadSeek for T { } + +pub type ResponseBody<'r> = Body< + Pin>, + Pin> +>; + /// A response, as returned by types implementing [`Responder`]. #[derive(Default)] pub struct Response<'r> { status: Option, headers: HeaderMap<'r>, - body: Option>>>, + body: Option>, } impl<'r> Response<'r> { @@ -877,20 +917,14 @@ impl<'r> Response<'r> { /// let mut response = Response::new(); /// assert!(response.body().is_none()); /// - /// response.set_sized_body(Cursor::new("Hello, world!")).await; + /// let string = "Hello, world!"; + /// response.set_sized_body(string.len(), Cursor::new(string)); /// assert_eq!(response.body_string().await, Some("Hello, world!".to_string())); /// # }) /// ``` #[inline(always)] - pub fn body(&mut self) -> Option> { - // Looks crazy, right? Needed so Rust infers lifetime correctly. Weird. - match self.body.as_mut() { - Some(body) => Some(match body.as_mut() { - Body::Sized(b, size) => Body::Sized(b, size), - Body::Chunked(b, chunk_size) => Body::Chunked(b, chunk_size), - }), - None => None - } + pub fn body(&mut self) -> Option<&mut ResponseBody<'r>> { + self.body.as_mut() } /// Consumes `self's` body and reads it into a string. If `self` doesn't @@ -908,7 +942,8 @@ impl<'r> Response<'r> { /// let mut response = Response::new(); /// assert!(response.body().is_none()); /// - /// response.set_sized_body(Cursor::new("Hello, world!")).await; + /// let string = "Hello, world!"; + /// response.set_sized_body(string.len(), Cursor::new(string)); /// assert_eq!(response.body_string().await, Some("Hello, world!".to_string())); /// assert!(response.body().is_none()); /// # }) @@ -935,7 +970,8 @@ impl<'r> Response<'r> { /// let mut response = Response::new(); /// assert!(response.body().is_none()); /// - /// response.set_sized_body(Cursor::new("hi!")).await; + /// let string = "hi!"; + /// response.set_sized_body(string.len(), Cursor::new(string)); /// assert_eq!(response.body_bytes().await, Some(vec![0x68, 0x69, 0x21])); /// assert!(response.body().is_none()); /// # }) @@ -961,7 +997,8 @@ impl<'r> Response<'r> { /// let mut response = Response::new(); /// assert!(response.body().is_none()); /// - /// response.set_sized_body(Cursor::new("Hello, world!")).await; + /// let string = "Hello, world!"; + /// response.set_sized_body(string.len(), Cursor::new(string)); /// assert!(response.body().is_some()); /// /// let body = response.take_body(); @@ -974,7 +1011,7 @@ impl<'r> Response<'r> { /// # }) /// ``` #[inline(always)] - pub fn take_body(&mut self) -> Option>>> { + pub fn take_body(&mut self) -> Option> { self.body.take() } @@ -990,16 +1027,10 @@ impl<'r> Response<'r> { } } - /// Sets the body of `self` to be the fixed-sized `body`. The size of the - /// body is obtained by `seek`ing to the end and then `seek`ing back to the - /// start. Since this is an asynchronous operation, it returns a future - /// and should be `await`-ed on. - /// - /// # Panics - /// - /// If either seek fails, this method panics. If you believe it is possible - /// for `seek` to panic for `B`, use [set_raw_body](#method.set_raw_body) - /// instead. + /// Sets the body of `self` to be the fixed-sized `body` with size + /// `size`, which may be `None`. If `size` is `None`, the body's size will + /// be computing with calls to `seek` just before being written out in a + /// response. /// /// # Example /// @@ -1008,19 +1039,18 @@ impl<'r> Response<'r> { /// use rocket::Response; /// /// # rocket::async_test(async { + /// let string = "Hello, world!"; + /// /// let mut response = Response::new(); - /// response.set_sized_body(Cursor::new("Hello, world!")).await; + /// response.set_sized_body(string.len(), Cursor::new(string)); /// assert_eq!(response.body_string().await, Some("Hello, world!".to_string())); /// # }) /// ``` - pub async fn set_sized_body(&mut self, mut body: B) - where B: AsyncRead + AsyncSeek + Send + Unpin + 'r + pub fn set_sized_body(&mut self, size: S, body: B) + where B: AsyncRead + AsyncSeek + Send + Unpin + 'r, + S: Into> { - let size = body.seek(io::SeekFrom::End(0)).await - .expect("Attempted to retrieve size by seeking, but failed."); - body.seek(io::SeekFrom::Start(0)).await - .expect("Attempted to reset body by seeking after getting size."); - self.body = Some(Body::Sized(Box::pin(body.take(size)), size)); + self.body = Some(Body::Sized(Box::pin(body), size.into())); } /// Sets the body of `self` to be `body`, which will be streamed. The chunk @@ -1061,7 +1091,7 @@ impl<'r> Response<'r> { /// # }) /// ``` #[inline(always)] - pub fn set_chunked_body(&mut self, body: B, chunk_size: u64) + pub fn set_chunked_body(&mut self, body: B, chunk_size: usize) where B: AsyncRead + Send + 'r { self.body = Some(Body::Chunked(Box::pin(body), chunk_size)); @@ -1078,19 +1108,22 @@ impl<'r> Response<'r> { /// use rocket::response::{Response, Body}; /// /// # rocket::async_test(async { - /// let body = Body::Sized(Cursor::new("Hello!"), 6); + /// let string = "Hello!"; /// /// let mut response = Response::new(); - /// response.set_raw_body(body); + /// let body = Body::Sized(Cursor::new(string), Some(string.len())); + /// response.set_raw_body::, Cursor<&'static str>>(body); /// /// assert_eq!(response.body_string().await, Some("Hello!".to_string())); /// # }) /// ``` #[inline(always)] - pub fn set_raw_body(&mut self, body: Body) - where T: AsyncRead + Send + Unpin + 'r { + pub fn set_raw_body(&mut self, body: Body) + where S: AsyncRead + AsyncSeek + Send + Unpin + 'r, + C: AsyncRead + Send + Unpin + 'r + { self.body = Some(match body { - Body::Sized(b, n) => Body::Sized(Box::pin(b.take(n)), n), + Body::Sized(a, n) => Body::Sized(Box::pin(a), n), Body::Chunked(b, n) => Body::Chunked(Box::pin(b), n), }); } @@ -1203,10 +1236,9 @@ impl fmt::Debug for Response<'_> { use crate::request::Request; -#[crate::async_trait] -impl<'r> Responder<'r> for Response<'r> { +impl<'r, 'o: 'r> Responder<'r, 'o> for Response<'o> { /// This is the identity implementation. It simply returns `Ok(self)`. - async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> { Ok(self) } } diff --git a/core/lib/src/response/status.rs b/core/lib/src/response/status.rs index df8d4df4..ccdc0d29 100644 --- a/core/lib/src/response/status.rs +++ b/core/lib/src/response/status.rs @@ -97,9 +97,7 @@ impl<'r, R> Created { /// assert_eq!(etag, None); /// # }); /// ``` - pub fn body(mut self, responder: R) -> Self - where R: Responder<'r> - { + pub fn body(mut self, responder: R) -> Self { self.1 = Some(responder); self } @@ -135,9 +133,7 @@ impl<'r, R> Created { /// assert_eq!(etag, Some(r#""13046220615156895040""#)); /// # }); /// ``` - pub fn tagged_body(mut self, responder: R) -> Self - where R: Responder<'r> + Hash - { + pub fn tagged_body(mut self, responder: R) -> Self where R: Hash { let mut hasher = &mut DefaultHasher::default(); responder.hash(&mut hasher); let hash = hasher.finish(); @@ -160,12 +156,11 @@ impl<'r, R> Created { /// the response with the `Responder`, the `ETag` header is set conditionally if /// a hashable `Responder` is provided via [`Created::tagged_body()`]. The `ETag` /// header is set to a hash value of the responder. -#[crate::async_trait] -impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Created { - async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> { +impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Created { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { let mut response = Response::build(); if let Some(responder) = self.1 { - response.merge(responder.respond_to(req).await?); + response.merge(responder.respond_to(req)?); } if let Some(hash) = self.2 { @@ -207,12 +202,11 @@ pub struct Accepted(pub Option); /// Sets the status code of the response to 202 Accepted. If the responder is /// `Some`, it is used to finalize the response. -#[crate::async_trait] -impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Accepted { - async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> { +impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Accepted { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { let mut build = Response::build(); if let Some(responder) = self.0 { - build.merge(responder.respond_to(req).await?); + build.merge(responder.respond_to(req)?); } build.status(Status::Accepted).ok() @@ -237,10 +231,9 @@ impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Accepted { pub struct NoContent; /// Sets the status code of the response to 204 No Content. -impl<'r> Responder<'r> for NoContent { - fn respond_to(self, _: &Request<'_>) -> Result, Status> { - let mut build = Response::build(); - build.status(Status::NoContent).ok() +impl<'r> Responder<'r, 'static> for NoContent { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { + Response::build().status(Status::NoContent).ok() } } @@ -273,12 +266,11 @@ pub struct BadRequest(pub Option); /// Sets the status code of the response to 400 Bad Request. If the responder is /// `Some`, it is used to finalize the response. -#[crate::async_trait] -impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for BadRequest { - async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> { +impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for BadRequest { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { let mut build = Response::build(); if let Some(responder) = self.0 { - build.merge(responder.respond_to(req).await?); + build.merge(responder.respond_to(req)?); } build.status(Status::BadRequest).ok() @@ -314,8 +306,8 @@ pub struct Unauthorized(pub Option); /// Sets the status code of the response to 401 Unauthorized. If the responder is /// `Some`, it is used to finalize the response. -impl<'r, R: Responder<'r>> Responder<'r> for Unauthorized { - fn respond_to(self, req: &Request<'_>) -> Result, Status> { +impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Unauthorized { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { let mut build = Response::build(); if let Some(responder) = self.0 { build.merge(responder.respond_to(req)?); @@ -354,8 +346,8 @@ pub struct Forbidden(pub Option); /// Sets the status code of the response to 403 Forbidden. If the responder is /// `Some`, it is used to finalize the response. -impl<'r, R: Responder<'r>> Responder<'r> for Forbidden { - fn respond_to(self, req: &Request<'_>) -> Result, Status> { +impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Forbidden { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { let mut build = Response::build(); if let Some(responder) = self.0 { build.merge(responder.respond_to(req)?); @@ -381,10 +373,9 @@ impl<'r, R: Responder<'r>> Responder<'r> for Forbidden { pub struct NotFound(pub R); /// Sets the status code of the response to 404 Not Found. -#[crate::async_trait] -impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for NotFound { - async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> { - Response::build_from(self.0.respond_to(req).await?) +impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for NotFound { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { + Response::build_from(self.0.respond_to(req)?) .status(Status::NotFound) .ok() } @@ -420,8 +411,8 @@ pub struct Conflict(pub Option); /// Sets the status code of the response to 409 Conflict. If the responder is /// `Some`, it is used to finalize the response. -impl<'r, R: Responder<'r>> Responder<'r> for Conflict { - fn respond_to(self, req: &Request<'_>) -> Result, Status> { +impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Conflict { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { let mut build = Response::build(); if let Some(responder) = self.0 { build.merge(responder.respond_to(req)?); @@ -447,10 +438,9 @@ pub struct Custom(pub Status, pub R); /// Sets the status code of the response and then delegates the remainder of the /// response to the wrapped responder. -#[crate::async_trait] -impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Custom { - async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> { - Response::build_from(self.1.respond_to(req).await?) +impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Custom { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { + Response::build_from(self.1.respond_to(req)?) .status(self.0) .ok() } diff --git a/core/lib/src/response/stream.rs b/core/lib/src/response/stream.rs index cfec48cb..cfe6997b 100644 --- a/core/lib/src/response/stream.rs +++ b/core/lib/src/response/stream.rs @@ -11,7 +11,7 @@ use crate::response::{self, Response, Responder, DEFAULT_CHUNK_SIZE}; /// 4KiB. This means that at most 4KiB are stored in memory while the response /// is being sent. This type should be used when sending responses that are /// arbitrarily large in size, such as when streaming from a local socket. -pub struct Stream(T, u64); +pub struct Stream(T, usize); impl Stream { /// Create a new stream from the given `reader` and sets the chunk size for @@ -28,7 +28,7 @@ impl Stream { /// # #[allow(unused_variables)] /// let response = Stream::chunked(tokio::io::stdin(), 10); /// ``` - pub fn chunked(reader: T, chunk_size: u64) -> Stream { + pub fn chunked(reader: T, chunk_size: usize) -> Stream { Stream(reader, chunk_size) } } @@ -66,9 +66,8 @@ impl From for Stream { /// If reading from the input stream fails at any point during the response, the /// response is abandoned, and the response ends abruptly. An error is printed /// to the console with an indication of what went wrong. -#[crate::async_trait] -impl<'r, T: AsyncRead + Send + 'r> Responder<'r> for Stream { - async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> { +impl<'r, 'o: 'r, T: AsyncRead + Send + 'o> Responder<'r, 'o> for Stream { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> { Response::build().chunked_body(self.0, self.1).ok() } } diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index f9e054fa..0b7681c0 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -1,12 +1,10 @@ -use std::collections::HashMap; -use std::convert::{From, TryInto}; +use std::{io, mem}; use std::cmp::min; -use std::io; -use std::mem; use std::sync::Arc; +use std::collections::HashMap; -use futures::future::{Future, FutureExt, BoxFuture}; use futures::stream::StreamExt; +use futures::future::{Future, FutureExt, BoxFuture}; use tokio::sync::{mpsc, oneshot}; use yansi::Paint; @@ -143,8 +141,13 @@ impl Manifest { } let send_response = move |hyp_res: hyper::ResponseBuilder, body| -> io::Result<()> { - let response = hyp_res.body(body).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - tx.send(response).map_err(|_| io::Error::new(io::ErrorKind::BrokenPipe, "Client disconnected before the response was started")) + let response = hyp_res.body(body) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + + tx.send(response).map_err(|_| { + let msg = "Client disconnected before the response was started"; + io::Error::new(io::ErrorKind::BrokenPipe, msg) + }) }; match response.body() { @@ -153,23 +156,22 @@ impl Manifest { send_response(hyp_res, hyper::Body::empty())?; } Some(body) => { - let (body, chunk_size) = match body { - Body::Chunked(body, chunk_size) => { - (body, chunk_size.try_into().expect("u64 -> usize overflow")) - } - Body::Sized(body, size) => { - hyp_res = hyp_res.header(header::CONTENT_LENGTH, size.to_string()); - (body, 4096usize) - } + if let Some(s) = body.size().await { + hyp_res = hyp_res.header(header::CONTENT_LENGTH, s.to_string()); + } + + let chunk_size = match *body { + Body::Chunked(_, chunk_size) => chunk_size as usize, + Body::Sized(_, _) => crate::response::DEFAULT_CHUNK_SIZE, }; let (mut sender, hyp_body) = hyper::Body::channel(); send_response(hyp_res, hyp_body)?; - let mut stream = body.into_bytes_stream(chunk_size); - + let mut stream = body.as_reader().into_bytes_stream(chunk_size); while let Some(next) = stream.next().await { - sender.send_data(next?).await.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + sender.send_data(next?).await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; } } }; diff --git a/core/lib/src/router/mod.rs b/core/lib/src/router/mod.rs index 241c09fe..a4d52d41 100644 --- a/core/lib/src/router/mod.rs +++ b/core/lib/src/router/mod.rs @@ -17,7 +17,7 @@ type Selector = Method; pub(crate) fn dummy_handler<'r>( r: &'r Request<'_>, _: crate::Data ) -> BoxFuture<'r, crate::handler::Outcome<'r>> { - crate::Outcome::from(r, ()) + crate::Outcome::from(r, ()).pin() } #[derive(Default)] diff --git a/core/lib/tests/head_handling.rs b/core/lib/tests/head_handling.rs index f404754b..735a8d54 100644 --- a/core/lib/tests/head_handling.rs +++ b/core/lib/tests/head_handling.rs @@ -22,27 +22,24 @@ fn other() -> content::Json<&'static str> { mod head_handling_tests { use super::*; - use tokio::io::{AsyncRead, AsyncReadExt}; + use tokio::io::AsyncReadExt; use rocket::Route; use rocket::local::Client; use rocket::http::{Status, ContentType}; - use rocket::response::Body; + use rocket::response::ResponseBody; fn routes() -> Vec { routes![index, empty, other] } - async fn assert_empty_sized_body(body: Body, expected_size: u64) { - match body { - Body::Sized(mut body, size) => { - let mut buffer = vec![]; - body.read_to_end(&mut buffer).await.unwrap(); - assert_eq!(size, expected_size); - assert_eq!(buffer.len(), 0); - } - _ => panic!("Expected a sized body.") - } + async fn assert_empty_sized_body(body: &mut ResponseBody<'_>, expected_size: usize) { + let size = body.size().await.expect("sized body"); + assert_eq!(size, expected_size); + + let mut buffer = vec![]; + body.as_reader().read_to_end(&mut buffer).await.unwrap(); + assert_eq!(buffer.len(), 0); } #[rocket::async_test] diff --git a/core/lib/tests/responder_lifetime-issue-345.rs b/core/lib/tests/responder_lifetime-issue-345.rs index 8ef87716..8e797918 100644 --- a/core/lib/tests/responder_lifetime-issue-345.rs +++ b/core/lib/tests/responder_lifetime-issue-345.rs @@ -4,7 +4,6 @@ #[macro_use] extern crate rocket; use rocket::{Request, State}; -use rocket::futures::future::BoxFuture; use rocket::response::{Responder, Result}; struct SomeState; @@ -14,11 +13,9 @@ pub struct CustomResponder<'r, R> { state: &'r SomeState, } -impl<'r, R: Responder<'r>> Responder<'r> for CustomResponder<'r, R> { - fn respond_to<'a, 'x>(self, _: &'r Request<'a>) -> BoxFuture<'x, Result<'r>> - where 'a: 'x, 'r: 'x, Self: 'x - { - unimplemented!() +impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for CustomResponder<'r, R> { + fn respond_to(self, req: &'r Request<'_>) -> Result<'o> { + self.responder.respond_to(req) } } diff --git a/examples/fairings/src/main.rs b/examples/fairings/src/main.rs index 023f166e..c15e30d4 100644 --- a/examples/fairings/src/main.rs +++ b/examples/fairings/src/main.rs @@ -48,7 +48,7 @@ impl Fairing for Counter { let body = format!("Get: {}\nPost: {}", get_count, post_count); res.set_status(Status::Ok); res.set_header(ContentType::Plain); - res.set_sized_body(Cursor::new(body)).await; + res.set_sized_body(body.len(), Cursor::new(body)); } } } @@ -89,7 +89,7 @@ fn rocket() -> rocket::Rocket { Box::pin(async move { if req.uri().path() == "/" { println!(" => Rewriting response body."); - res.set_sized_body(Cursor::new("Hello, fairings!")).await; + res.set_sized_body(None, Cursor::new("Hello, fairings!")); } }) })) diff --git a/examples/manual_routes/src/main.rs b/examples/manual_routes/src/main.rs index ada738a1..2adb110b 100644 --- a/examples/manual_routes/src/main.rs +++ b/examples/manual_routes/src/main.rs @@ -7,11 +7,10 @@ use std::env; use rocket::{Request, Handler, Route, Data, Catcher, try_outcome}; use rocket::http::{Status, RawStr}; -use rocket::response::{self, Responder, status::Custom}; -use rocket::handler::{Outcome, HandlerFuture}; +use rocket::response::{Responder, status::Custom}; +use rocket::handler::{Outcome, HandlerFuture, ErrorHandlerFuture}; use rocket::outcome::IntoOutcome; use rocket::http::Method::*; -use rocket::futures::future::BoxFuture; use rocket::tokio::fs::File; fn forward<'r>(_req: &'r Request, data: Data) -> HandlerFuture<'r> { @@ -19,26 +18,25 @@ fn forward<'r>(_req: &'r Request, data: Data) -> HandlerFuture<'r> { } fn hi<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> { - Box::pin(async move { Outcome::from(req, "Hello!").await }) + Outcome::from(req, "Hello!").pin() } fn name<'a>(req: &'a Request, _: Data) -> HandlerFuture<'a> { - Box::pin(async move { - let param = req.get_param::<&'a RawStr>(0) - .and_then(|res| res.ok()) - .unwrap_or("unnamed".into()); + let param = req.get_param::<&'a RawStr>(0) + .and_then(|res| res.ok()) + .unwrap_or("unnamed".into()); - Outcome::from(req, param.as_str()).await - }) + Outcome::from(req, param.as_str()).pin() } fn echo_url<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> { + let param_outcome = req.get_param::<&RawStr>(1) + .and_then(|res| res.ok()) + .into_outcome(Status::BadRequest); + Box::pin(async move { - let param_outcome = req.get_param::<&RawStr>(1) - .and_then(|res| res.ok()) - .into_outcome(Status::BadRequest); let param = try_outcome!(param_outcome); - Outcome::try_from(req, RawStr::from_str(param).url_decode()).await + Outcome::try_from(req, RawStr::from_str(param).url_decode()) }) } @@ -52,7 +50,7 @@ fn upload<'r>(req: &'r Request, data: Data) -> HandlerFuture<'r> { let file = File::create(env::temp_dir().join("upload.txt")).await; if let Ok(file) = file { if let Ok(n) = data.stream_to(file).await { - return Outcome::from(req, format!("OK: {} bytes uploaded.", n)).await; + return Outcome::from(req, format!("OK: {} bytes uploaded.", n)); } println!(" => Failed copying."); @@ -65,12 +63,12 @@ fn upload<'r>(req: &'r Request, data: Data) -> HandlerFuture<'r> { } fn get_upload<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> { - Outcome::from(req, std::fs::File::open(env::temp_dir().join("upload.txt")).ok()) + Outcome::from(req, std::fs::File::open(env::temp_dir().join("upload.txt")).ok()).pin() } -fn not_found_handler<'r>(req: &'r Request) -> BoxFuture<'r, response::Result<'r>> { +fn not_found_handler<'r>(req: &'r Request) -> ErrorHandlerFuture<'r> { let res = Custom(Status::NotFound, format!("Couldn't find: {}", req.uri())); - res.respond_to(req) + Box::pin(async move { res.respond_to(req) }) } #[derive(Clone)] @@ -87,13 +85,13 @@ impl CustomHandler { impl Handler for CustomHandler { fn handle<'r>(&self, req: &'r Request, data: Data) -> HandlerFuture<'r> { let self_data = self.data; - Box::pin(async move { - let id_outcome = req.get_param::<&RawStr>(0) - .and_then(|res| res.ok()) - .or_forward(data); + let id_outcome = req.get_param::<&RawStr>(0) + .and_then(|res| res.ok()) + .or_forward(data); + Box::pin(async move { let id = try_outcome!(id_outcome); - Outcome::from(req, format!("{} - {}", self_data, id)).await + Outcome::from(req, format!("{} - {}", self_data, id)) }) } }