From 94fa599340dca3759dc45cee3d9eff736af00fcd Mon Sep 17 00:00:00 2001 From: David Laban Date: Mon, 29 Jun 2020 22:23:44 +0100 Subject: [PATCH 01/15] WIP: "closure implements `Fn`, so references to captured variables can't escape the closure" --- gotham/src/router/builder/single.rs | 47 +++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/gotham/src/router/builder/single.rs b/gotham/src/router/builder/single.rs index b241bd2af..35a8e19e8 100644 --- a/gotham/src/router/builder/single.rs +++ b/gotham/src/router/builder/single.rs @@ -1,10 +1,10 @@ -use hyper::Body; +use hyper::{Body, Response}; use std::panic::RefUnwindSafe; use crate::extractor::{PathExtractor, QueryStringExtractor}; use crate::handler::assets::{DirHandler, FileHandler, FileOptions, FilePathExtractor}; -use crate::handler::{Handler, HandlerResult, NewHandler}; +use crate::handler::{Handler, HandlerError, HandlerResult, NewHandler}; use crate::pipeline::chain::PipelineHandleChain; use crate::router::builder::{ ExtendRouteMatcher, ReplacePathExtractor, ReplaceQueryStringExtractor, SingleRouteBuilder, @@ -16,6 +16,26 @@ use crate::state::State; use core::future::Future; use futures::FutureExt; +trait FnHelper<'a> { + type Fut: std::future::Future, HandlerError>> + + RefUnwindSafe + + Copy + + Send + + Sync + + 'a; + fn call(self, arg: &'a State) -> Self::Fut; +} + +impl<'a, D, F> FnHelper<'a> for F +where + F: FnOnce(&'a State) -> D, + D: std::future::Future, HandlerError>> + RefUnwindSafe + Copy + Send + Sync + 'a, +{ + type Fut = D; + fn call(self, state: &'a State) -> D { + self(state) + } +} /// Describes the API for defining a single route, after determining which request paths will be /// dispatched here. The API here uses chained function calls to build and add the route into the /// `RouterBuilder` which created it. @@ -157,6 +177,29 @@ pub trait DefineSingleRoute { Self: Sized, H: (FnOnce(State) -> Fut) + RefUnwindSafe + Copy + Send + Sync + 'static, Fut: Future + Send + 'static; + + /// Similar to `to_async`, but passes in State as a reference + fn to_async_borrowing(self, handler: F) + where + Self: Sized, + // F: (FnOnce(State) -> Fut) + RefUnwindSafe + Copy + Send + Sync + 'static, + for<'a> F: FnHelper<'a> + RefUnwindSafe + Copy + Send + Sync + 'static, + // Fut: Future + Send + 'static + { + self.to_new_handler(move || { + Ok(move |state: State| { + async { + let fut = handler.call(&state); + let result = fut.await; + match result { + Ok(response) => Ok((state, response)), + Err(err) => Err((state, err)), + } + } + .boxed() + }) + }) + } /// Directs the route to the given `NewHandler`. This gives more control over how `Handler` /// values are constructed. /// From 8bb7dac1e5286dbca793db06348befcb228a9c15 Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Tue, 30 Jun 2020 01:34:06 +0300 Subject: [PATCH 02/15] to_async_borrowing: pass state by a mutable ref This change is important for two reasons. First, it allows the async handler to take the body (or other data) from State. Second, a mutable reference, unlike a shared one, can be Send without State being Sync (which it is not). --- gotham/src/router/builder/single.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gotham/src/router/builder/single.rs b/gotham/src/router/builder/single.rs index 35a8e19e8..170f2c41b 100644 --- a/gotham/src/router/builder/single.rs +++ b/gotham/src/router/builder/single.rs @@ -23,16 +23,16 @@ trait FnHelper<'a> { + Send + Sync + 'a; - fn call(self, arg: &'a State) -> Self::Fut; + fn call(self, arg: &'a mut State) -> Self::Fut; } impl<'a, D, F> FnHelper<'a> for F where - F: FnOnce(&'a State) -> D, + F: FnOnce(&'a mut State) -> D, D: std::future::Future, HandlerError>> + RefUnwindSafe + Copy + Send + Sync + 'a, { type Fut = D; - fn call(self, state: &'a State) -> D { + fn call(self, state: &'a mut State) -> D { self(state) } } @@ -187,9 +187,9 @@ pub trait DefineSingleRoute { // Fut: Future + Send + 'static { self.to_new_handler(move || { - Ok(move |state: State| { + Ok(move |mut state: State| { async { - let fut = handler.call(&state); + let fut = handler.call(&mut state); let result = fut.await; match result { Ok(response) => Ok((state, response)), From a36cd237ec5b959bab26e37cafa9b53b79f59cd6 Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Tue, 30 Jun 2020 01:34:54 +0300 Subject: [PATCH 03/15] to_async_borrowing: move captures into the async block --- gotham/src/router/builder/single.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gotham/src/router/builder/single.rs b/gotham/src/router/builder/single.rs index 170f2c41b..45703bd1b 100644 --- a/gotham/src/router/builder/single.rs +++ b/gotham/src/router/builder/single.rs @@ -188,7 +188,7 @@ pub trait DefineSingleRoute { { self.to_new_handler(move || { Ok(move |mut state: State| { - async { + async move { let fut = handler.call(&mut state); let result = fut.await; match result { From 7dafd8f2918761306fc946e1beec4b450bf10819 Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Tue, 30 Jun 2020 01:37:56 +0300 Subject: [PATCH 04/15] builder: rename FnHelper to AsyncHandlerFn and make pub --- gotham/src/router/builder/single.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gotham/src/router/builder/single.rs b/gotham/src/router/builder/single.rs index 45703bd1b..fdc1937ca 100644 --- a/gotham/src/router/builder/single.rs +++ b/gotham/src/router/builder/single.rs @@ -16,7 +16,7 @@ use crate::state::State; use core::future::Future; use futures::FutureExt; -trait FnHelper<'a> { +pub trait AsyncHandlerFn<'a> { type Fut: std::future::Future, HandlerError>> + RefUnwindSafe + Copy @@ -26,7 +26,7 @@ trait FnHelper<'a> { fn call(self, arg: &'a mut State) -> Self::Fut; } -impl<'a, D, F> FnHelper<'a> for F +impl<'a, D, F> AsyncHandlerFn<'a> for F where F: FnOnce(&'a mut State) -> D, D: std::future::Future, HandlerError>> + RefUnwindSafe + Copy + Send + Sync + 'a, @@ -183,7 +183,7 @@ pub trait DefineSingleRoute { where Self: Sized, // F: (FnOnce(State) -> Fut) + RefUnwindSafe + Copy + Send + Sync + 'static, - for<'a> F: FnHelper<'a> + RefUnwindSafe + Copy + Send + Sync + 'static, + for<'a> F: AsyncHandlerFn<'a> + RefUnwindSafe + Copy + Send + Sync + 'static, // Fut: Future + Send + 'static { self.to_new_handler(move || { From dc6e03084c1b1a41748d01dfe4479273b57e1c50 Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Tue, 30 Jun 2020 01:39:07 +0300 Subject: [PATCH 05/15] AsyncHandlerFn: drop unneeded bounds These are required on the handler factory (NewHandler impl), but not on the handler itself. --- gotham/src/router/builder/single.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/gotham/src/router/builder/single.rs b/gotham/src/router/builder/single.rs index fdc1937ca..ad0141d53 100644 --- a/gotham/src/router/builder/single.rs +++ b/gotham/src/router/builder/single.rs @@ -17,19 +17,14 @@ use core::future::Future; use futures::FutureExt; pub trait AsyncHandlerFn<'a> { - type Fut: std::future::Future, HandlerError>> - + RefUnwindSafe - + Copy - + Send - + Sync - + 'a; + type Fut: std::future::Future, HandlerError>> + Send + 'a; fn call(self, arg: &'a mut State) -> Self::Fut; } impl<'a, D, F> AsyncHandlerFn<'a> for F where F: FnOnce(&'a mut State) -> D, - D: std::future::Future, HandlerError>> + RefUnwindSafe + Copy + Send + Sync + 'a, + D: std::future::Future, HandlerError>> + Send + 'a, { type Fut = D; fn call(self, state: &'a mut State) -> D { From 335cde5d5d1a277f9d205fea30f52f93d7da90de Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Sun, 5 Jul 2020 22:27:13 +0300 Subject: [PATCH 06/15] AsyncHandlerFn: rename type parameter to Fut --- gotham/src/router/builder/single.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gotham/src/router/builder/single.rs b/gotham/src/router/builder/single.rs index ad0141d53..ec4017e4a 100644 --- a/gotham/src/router/builder/single.rs +++ b/gotham/src/router/builder/single.rs @@ -21,13 +21,13 @@ pub trait AsyncHandlerFn<'a> { fn call(self, arg: &'a mut State) -> Self::Fut; } -impl<'a, D, F> AsyncHandlerFn<'a> for F +impl<'a, Fut, F> AsyncHandlerFn<'a> for F where - F: FnOnce(&'a mut State) -> D, - D: std::future::Future, HandlerError>> + Send + 'a, + F: FnOnce(&'a mut State) -> Fut, + Fut: std::future::Future, HandlerError>> + Send + 'a, { - type Fut = D; - fn call(self, state: &'a mut State) -> D { + type Fut = Fut; + fn call(self, state: &'a mut State) -> Fut { self(state) } } From 4a696fe0bbdc8c52d85ba82ed90d9c083e9d63df Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Tue, 30 Jun 2020 01:49:49 +0300 Subject: [PATCH 07/15] handler: add a SimpleHandlerResult type alias --- gotham/src/handler/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gotham/src/handler/mod.rs b/gotham/src/handler/mod.rs index 27a118e2f..6928dc2a1 100644 --- a/gotham/src/handler/mod.rs +++ b/gotham/src/handler/mod.rs @@ -27,6 +27,9 @@ pub use self::error::{HandlerError, MapHandlerError, MapHandlerErrorFuture}; /// A type alias for the results returned by async fns that can be passed to to_async. pub type HandlerResult = std::result::Result<(State, Response), (State, HandlerError)>; +/// A type alias for the results returned by async fns that can be passed to to_async_borrowing. +pub type SimpleHandlerResult = std::result::Result, HandlerError>; + /// A type alias for the trait objects returned by `HandlerService`. /// /// When the `Future` resolves to an error, the `(State, HandlerError)` value is used to generate From 6657ecd69694c98455e09dc1ba650841657d6ce2 Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Tue, 30 Jun 2020 12:44:23 +0300 Subject: [PATCH 08/15] to_async_borrowing: make return type generic The success path can return not only a response but also anything that implements IntoResponse. --- gotham/src/router/builder/single.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/gotham/src/router/builder/single.rs b/gotham/src/router/builder/single.rs index ec4017e4a..5aae2615f 100644 --- a/gotham/src/router/builder/single.rs +++ b/gotham/src/router/builder/single.rs @@ -1,10 +1,10 @@ -use hyper::{Body, Response}; +use hyper::Body; use std::panic::RefUnwindSafe; use crate::extractor::{PathExtractor, QueryStringExtractor}; use crate::handler::assets::{DirHandler, FileHandler, FileOptions, FilePathExtractor}; -use crate::handler::{Handler, HandlerError, HandlerResult, NewHandler}; +use crate::handler::{Handler, HandlerError, HandlerResult, IntoResponse, NewHandler}; use crate::pipeline::chain::PipelineHandleChain; use crate::router::builder::{ ExtendRouteMatcher, ReplacePathExtractor, ReplaceQueryStringExtractor, SingleRouteBuilder, @@ -17,15 +17,18 @@ use core::future::Future; use futures::FutureExt; pub trait AsyncHandlerFn<'a> { - type Fut: std::future::Future, HandlerError>> + Send + 'a; + type Res: IntoResponse + 'static; + type Fut: std::future::Future> + Send + 'a; fn call(self, arg: &'a mut State) -> Self::Fut; } -impl<'a, Fut, F> AsyncHandlerFn<'a> for F +impl<'a, Fut, R, F> AsyncHandlerFn<'a> for F where F: FnOnce(&'a mut State) -> Fut, - Fut: std::future::Future, HandlerError>> + Send + 'a, + R: IntoResponse + 'static, + Fut: std::future::Future> + Send + 'a, { + type Res = R; type Fut = Fut; fn call(self, state: &'a mut State) -> Fut { self(state) @@ -174,12 +177,10 @@ pub trait DefineSingleRoute { Fut: Future + Send + 'static; /// Similar to `to_async`, but passes in State as a reference - fn to_async_borrowing(self, handler: F) + fn to_async_borrowing(self, handler: F) where Self: Sized, - // F: (FnOnce(State) -> Fut) + RefUnwindSafe + Copy + Send + Sync + 'static, - for<'a> F: AsyncHandlerFn<'a> + RefUnwindSafe + Copy + Send + Sync + 'static, - // Fut: Future + Send + 'static + for<'a> F: AsyncHandlerFn<'a, Res = R> + RefUnwindSafe + Copy + Send + Sync + 'static, { self.to_new_handler(move || { Ok(move |mut state: State| { @@ -187,7 +188,10 @@ pub trait DefineSingleRoute { let fut = handler.call(&mut state); let result = fut.await; match result { - Ok(response) => Ok((state, response)), + Ok(data) => { + let response = data.into_response(&state); + Ok((state, response)) + } Err(err) => Err((state, err)), } } From ea29f92eaa2e22c1f0cd43cbd052f52337ad99ea Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Tue, 30 Jun 2020 18:33:55 +0300 Subject: [PATCH 09/15] to_async_borrowing: move out impl from definition Split the impl into the new handler marker trait and the actual impl using it. --- gotham/src/router/builder/single.rs | 62 +++++++++++++++++++---------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/gotham/src/router/builder/single.rs b/gotham/src/router/builder/single.rs index 5aae2615f..50bc84c73 100644 --- a/gotham/src/router/builder/single.rs +++ b/gotham/src/router/builder/single.rs @@ -1,10 +1,13 @@ use hyper::Body; use std::panic::RefUnwindSafe; +use std::pin::Pin; use crate::extractor::{PathExtractor, QueryStringExtractor}; use crate::handler::assets::{DirHandler, FileHandler, FileOptions, FilePathExtractor}; -use crate::handler::{Handler, HandlerError, HandlerResult, IntoResponse, NewHandler}; +use crate::handler::{ + Handler, HandlerError, HandlerFuture, HandlerResult, IntoResponse, NewHandler, +}; use crate::pipeline::chain::PipelineHandleChain; use crate::router::builder::{ ExtendRouteMatcher, ReplacePathExtractor, ReplaceQueryStringExtractor, SingleRouteBuilder, @@ -16,6 +19,10 @@ use crate::state::State; use core::future::Future; use futures::FutureExt; +pub trait HandlerMarker { + fn call_and_wrap(self, state: State) -> Pin>; +} + pub trait AsyncHandlerFn<'a> { type Res: IntoResponse + 'static; type Fut: std::future::Future> + Send + 'a; @@ -34,6 +41,28 @@ where self(state) } } + +impl HandlerMarker for F +where + R: IntoResponse + 'static, + for<'a> F: AsyncHandlerFn<'a, Res = R> + Send + 'static, +{ + fn call_and_wrap(self, mut state: State) -> Pin> { + async move { + let fut = self.call(&mut state); + let result = fut.await; + match result { + Ok(data) => { + let response = data.into_response(&state); + Ok((state, response)) + } + Err(err) => Err((state, err)), + } + } + .boxed() + } +} + /// Describes the API for defining a single route, after determining which request paths will be /// dispatched here. The API here uses chained function calls to build and add the route into the /// `RouterBuilder` which created it. @@ -177,28 +206,11 @@ pub trait DefineSingleRoute { Fut: Future + Send + 'static; /// Similar to `to_async`, but passes in State as a reference - fn to_async_borrowing(self, handler: F) + fn to_async_borrowing(self, handler: F) where Self: Sized, - for<'a> F: AsyncHandlerFn<'a, Res = R> + RefUnwindSafe + Copy + Send + Sync + 'static, - { - self.to_new_handler(move || { - Ok(move |mut state: State| { - async move { - let fut = handler.call(&mut state); - let result = fut.await; - match result { - Ok(data) => { - let response = data.into_response(&state); - Ok((state, response)) - } - Err(err) => Err((state, err)), - } - } - .boxed() - }) - }) - } + F: HandlerMarker + Copy + Send + Sync + RefUnwindSafe + 'static; + /// Directs the route to the given `NewHandler`. This gives more control over how `Handler` /// values are constructed. /// @@ -573,6 +585,14 @@ where self.to_new_handler(move || Ok(move |s: State| handler(s).boxed())) } + fn to_async_borrowing(self, handler: F) + where + Self: Sized, + F: HandlerMarker + Copy + Send + Sync + RefUnwindSafe + 'static, + { + self.to_new_handler(move || Ok(move |state: State| handler.call_and_wrap(state))) + } + fn to_new_handler(self, new_handler: NH) where NH: NewHandler + 'static, From 25af37c7332523a959a8108e82846fc2e9a48196 Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Mon, 6 Jul 2020 00:57:18 +0300 Subject: [PATCH 10/15] to_async_borrowed: add examples to docs --- gotham/src/router/builder/single.rs | 43 +++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/gotham/src/router/builder/single.rs b/gotham/src/router/builder/single.rs index 50bc84c73..9fab35324 100644 --- a/gotham/src/router/builder/single.rs +++ b/gotham/src/router/builder/single.rs @@ -206,6 +206,49 @@ pub trait DefineSingleRoute { Fut: Future + Send + 'static; /// Similar to `to_async`, but passes in State as a reference + /// + /// # Examples + /// + /// ```rust + /// # extern crate gotham; + /// # extern crate hyper; + /// # + /// # use hyper::{Body, Response, StatusCode}; + /// # use gotham::handler::{HandlerError, IntoResponse}; + /// # use gotham::state::State; + /// # use gotham::router::Router; + /// # use gotham::router::builder::*; + /// # use gotham::pipeline::new_pipeline; + /// # use gotham::pipeline::single::*; + /// # use gotham::middleware::session::NewSessionMiddleware; + /// # use gotham::test::TestServer; + /// # + /// async fn my_handler(state: &mut State) -> Result { + /// // Handler implementation elided. + /// # let _ = state; + /// # Ok(Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap()) + /// } + /// # + /// # fn router() -> Router { + /// # let (chain, pipelines) = single_pipeline( + /// # new_pipeline().add(NewSessionMiddleware::default()).build() + /// # ); + /// + /// build_router(chain, pipelines, |route| { + /// route.get("/request/path").to_async_borrowing(my_handler); + /// }) + /// # + /// # } + /// # + /// # fn main() { + /// # let test_server = TestServer::new(router()).unwrap(); + /// # let response = test_server.client() + /// # .get("https://example.com/request/path") + /// # .perform() + /// # .unwrap(); + /// # assert_eq!(response.status(), StatusCode::ACCEPTED); + /// # } + /// ``` fn to_async_borrowing(self, handler: F) where Self: Sized, From 5f6e0442410a8602ff063f205e71bf2242b030e0 Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Tue, 30 Jun 2020 01:51:29 +0300 Subject: [PATCH 11/15] DEMO: port simple_async_handlers_await example --- .../simple_async_handlers_await/src/main.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/handlers/simple_async_handlers_await/src/main.rs b/examples/handlers/simple_async_handlers_await/src/main.rs index 3d0a1449f..fc3f66c7a 100644 --- a/examples/handlers/simple_async_handlers_await/src/main.rs +++ b/examples/handlers/simple_async_handlers_await/src/main.rs @@ -6,7 +6,7 @@ use std::time::{Duration, Instant}; use gotham::hyper::{Body, StatusCode}; -use gotham::handler::HandlerResult; +use gotham::handler::SimpleHandlerResult; use gotham::helpers::http::response::create_response; use gotham::router::builder::DefineSingleRoute; use gotham::router::builder::{build_simple_router, DrawRoutes}; @@ -60,8 +60,8 @@ fn sleep(seconds: u64) -> SleepFuture { /// This handler sleeps for the requested number of seconds, using the `sleep()` /// helper method, above. -async fn sleep_handler(mut state: State) -> HandlerResult { - let seconds = QueryStringExtractor::take_from(&mut state).seconds; +async fn sleep_handler(state: &mut State) -> SimpleHandlerResult { + let seconds = QueryStringExtractor::borrow_from(state).seconds; println!("sleep for {} seconds once: starting", seconds); // Here, we call the sleep function and turn its old-style future into // a new-style future. Note that this step doesn't block: it just sets @@ -79,15 +79,15 @@ async fn sleep_handler(mut state: State) -> HandlerResult { // expects, using the helper from IntoHandlerError. let res = create_response(&state, StatusCode::OK, mime::TEXT_PLAIN, data); println!("sleep for {} seconds once: finished", seconds); - Ok((state, res)) + Ok(res) } /// It calls sleep(1) as many times as needed to make the requested duration. /// /// Notice how much easier it is to read than the version in /// `simple_async_handlers`. -async fn loop_handler(mut state: State) -> HandlerResult { - let seconds = QueryStringExtractor::take_from(&mut state).seconds; +async fn loop_handler(state: &mut State) -> SimpleHandlerResult { + let seconds = QueryStringExtractor::borrow_from(state).seconds; println!("sleep for one second {} times: starting", seconds); // The code within this block reads exactly like syncronous code. @@ -106,7 +106,7 @@ async fn loop_handler(mut state: State) -> HandlerResult { Body::from(accumulator), ); println!("sleep for one second {} times: finished", seconds); - Ok((state, res)) + Ok(res) } /// Create a `Router`. @@ -115,11 +115,11 @@ fn router() -> Router { route .get("/sleep") .with_query_string_extractor::() - .to_async(sleep_handler); + .to_async_borrowing(sleep_handler); route .get("/loop") .with_query_string_extractor::() - .to_async(loop_handler); + .to_async_borrowing(loop_handler); }) } From ac424d9a7981c5db0e953b1647f9275cd55ad862 Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Mon, 6 Jul 2020 00:22:58 +0300 Subject: [PATCH 12/15] simple_async_handlers_await: update comments --- .../handlers/simple_async_handlers_await/src/main.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/examples/handlers/simple_async_handlers_await/src/main.rs b/examples/handlers/simple_async_handlers_await/src/main.rs index fc3f66c7a..71c8d3a50 100644 --- a/examples/handlers/simple_async_handlers_await/src/main.rs +++ b/examples/handlers/simple_async_handlers_await/src/main.rs @@ -63,20 +63,16 @@ fn sleep(seconds: u64) -> SleepFuture { async fn sleep_handler(state: &mut State) -> SimpleHandlerResult { let seconds = QueryStringExtractor::borrow_from(state).seconds; println!("sleep for {} seconds once: starting", seconds); - // Here, we call the sleep function and turn its old-style future into - // a new-style future. Note that this step doesn't block: it just sets - // up the timer so that we can use it later. + // Here, we call the sleep function. Note that this step doesn't block: + // it just sets up the timer so that we can use it later. let sleep_future = sleep(seconds); // Here is where the serious sleeping happens. We yield execution of // this block until sleep_future is resolved. - // The Ok("slept for x seconds") value is stored in result. + // The "slept for x seconds" value is stored in data. let data = sleep_future.await; - // Here, we convert the result from `sleep()` into the form that Gotham - // expects: `state` is owned by this block so we need to return it. - // We also convert any errors that we have into the form that Hyper - // expects, using the helper from IntoHandlerError. + // We return a `Result, HandlerError>` directly let res = create_response(&state, StatusCode::OK, mime::TEXT_PLAIN, data); println!("sleep for {} seconds once: finished", seconds); Ok(res) From 73f4ed1800771a1f1fc66a7efad8759b02c58946 Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Sun, 5 Jul 2020 22:15:29 +0300 Subject: [PATCH 13/15] DEMO: use generic return type in the example --- .../simple_async_handlers_await/src/main.rs | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/examples/handlers/simple_async_handlers_await/src/main.rs b/examples/handlers/simple_async_handlers_await/src/main.rs index 71c8d3a50..09cdafad8 100644 --- a/examples/handlers/simple_async_handlers_await/src/main.rs +++ b/examples/handlers/simple_async_handlers_await/src/main.rs @@ -4,10 +4,9 @@ use futures::prelude::*; use std::pin::Pin; use std::time::{Duration, Instant}; -use gotham::hyper::{Body, StatusCode}; +use gotham::hyper::StatusCode; -use gotham::handler::SimpleHandlerResult; -use gotham::helpers::http::response::create_response; +use gotham::handler::{HandlerError, IntoResponse}; use gotham::router::builder::DefineSingleRoute; use gotham::router::builder::{build_simple_router, DrawRoutes}; use gotham::router::Router; @@ -60,7 +59,7 @@ fn sleep(seconds: u64) -> SleepFuture { /// This handler sleeps for the requested number of seconds, using the `sleep()` /// helper method, above. -async fn sleep_handler(state: &mut State) -> SimpleHandlerResult { +async fn sleep_handler(state: &mut State) -> Result { let seconds = QueryStringExtractor::borrow_from(state).seconds; println!("sleep for {} seconds once: starting", seconds); // Here, we call the sleep function. Note that this step doesn't block: @@ -72,17 +71,18 @@ async fn sleep_handler(state: &mut State) -> SimpleHandlerResult { // The "slept for x seconds" value is stored in data. let data = sleep_future.await; - // We return a `Result, HandlerError>` directly - let res = create_response(&state, StatusCode::OK, mime::TEXT_PLAIN, data); + // We return a `Result` directly + // where the success type can be anything implementing `IntoResponse` + // (including a `Response`) println!("sleep for {} seconds once: finished", seconds); - Ok(res) + Ok((StatusCode::OK, mime::TEXT_PLAIN, data)) } /// It calls sleep(1) as many times as needed to make the requested duration. /// /// Notice how much easier it is to read than the version in /// `simple_async_handlers`. -async fn loop_handler(state: &mut State) -> SimpleHandlerResult { +async fn loop_handler(state: &mut State) -> Result { let seconds = QueryStringExtractor::borrow_from(state).seconds; println!("sleep for one second {} times: starting", seconds); @@ -95,14 +95,8 @@ async fn loop_handler(state: &mut State) -> SimpleHandlerResult { accumulator.extend(body) } - let res = create_response( - &state, - StatusCode::OK, - mime::TEXT_PLAIN, - Body::from(accumulator), - ); println!("sleep for one second {} times: finished", seconds); - Ok(res) + Ok((StatusCode::OK, mime::TEXT_PLAIN, accumulator)) } /// Create a `Router`. From 9a08d71a62b364b759bbb616e42071c838529bb3 Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Sun, 6 Sep 2020 01:12:03 +0300 Subject: [PATCH 14/15] to_async_borrowing: expand docs and example --- gotham/src/router/builder/single.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/gotham/src/router/builder/single.rs b/gotham/src/router/builder/single.rs index 9fab35324..55e241a99 100644 --- a/gotham/src/router/builder/single.rs +++ b/gotham/src/router/builder/single.rs @@ -205,7 +205,13 @@ pub trait DefineSingleRoute { H: (FnOnce(State) -> Fut) + RefUnwindSafe + Copy + Send + Sync + 'static, Fut: Future + Send + 'static; - /// Similar to `to_async`, but passes in State as a reference + /// Directs the route to the given `async fn`, passing `State` to it by mutable reference. + /// + /// Note that, as of Rust 1.46.0, this does not work for closures due to + /// [rust-lang/rust#70263](https://github.com/rust-lang/rust/issues/70263). + /// + /// On the other hand, one can easily use the `?` operator for error handling + /// in these async functions. /// /// # Examples /// @@ -213,8 +219,8 @@ pub trait DefineSingleRoute { /// # extern crate gotham; /// # extern crate hyper; /// # - /// # use hyper::{Body, Response, StatusCode}; - /// # use gotham::handler::{HandlerError, IntoResponse}; + /// # use hyper::StatusCode; + /// # use gotham::handler::{HandlerError, IntoResponse, MapHandlerError}; /// # use gotham::state::State; /// # use gotham::router::Router; /// # use gotham::router::builder::*; @@ -223,10 +229,10 @@ pub trait DefineSingleRoute { /// # use gotham::middleware::session::NewSessionMiddleware; /// # use gotham::test::TestServer; /// # - /// async fn my_handler(state: &mut State) -> Result { - /// // Handler implementation elided. - /// # let _ = state; - /// # Ok(Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap()) + /// async fn my_handler(_state: &mut State) -> Result { + /// let flavors = std::fs::read("coffee-flavors.txt") + /// .map_err_with_status(StatusCode::IM_A_TEAPOT)?; + /// Ok(flavors) /// } /// # /// # fn router() -> Router { @@ -246,7 +252,7 @@ pub trait DefineSingleRoute { /// # .get("https://example.com/request/path") /// # .perform() /// # .unwrap(); - /// # assert_eq!(response.status(), StatusCode::ACCEPTED); + /// # assert_eq!(response.status(), StatusCode::IM_A_TEAPOT); /// # } /// ``` fn to_async_borrowing(self, handler: F) From f853599b1f924c110bd2126d5ead26a7c973c6dd Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Sun, 6 Sep 2020 01:21:59 +0300 Subject: [PATCH 15/15] examples: have both to_async and to_async_borrowing This partially reverts 5f6e044 and 73f4ed1. --- .../simple_async_handlers_await/src/main.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/examples/handlers/simple_async_handlers_await/src/main.rs b/examples/handlers/simple_async_handlers_await/src/main.rs index 09cdafad8..567b48e09 100644 --- a/examples/handlers/simple_async_handlers_await/src/main.rs +++ b/examples/handlers/simple_async_handlers_await/src/main.rs @@ -4,9 +4,10 @@ use futures::prelude::*; use std::pin::Pin; use std::time::{Duration, Instant}; -use gotham::hyper::StatusCode; +use gotham::hyper::{Body, StatusCode}; -use gotham::handler::{HandlerError, IntoResponse}; +use gotham::handler::{HandlerError, HandlerResult, IntoResponse}; +use gotham::helpers::http::response::create_response; use gotham::router::builder::DefineSingleRoute; use gotham::router::builder::{build_simple_router, DrawRoutes}; use gotham::router::Router; @@ -82,8 +83,8 @@ async fn sleep_handler(state: &mut State) -> Result Result { - let seconds = QueryStringExtractor::borrow_from(state).seconds; +async fn loop_handler(mut state: State) -> HandlerResult { + let seconds = QueryStringExtractor::take_from(&mut state).seconds; println!("sleep for one second {} times: starting", seconds); // The code within this block reads exactly like syncronous code. @@ -95,8 +96,14 @@ async fn loop_handler(state: &mut State) -> Result Router { route .get("/loop") .with_query_string_extractor::() - .to_async_borrowing(loop_handler); + .to_async(loop_handler); }) }