From e41abc09e595c07205f108b97d166f261199a015 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Thu, 19 Dec 2019 18:11:32 -0800 Subject: [PATCH] Update more API documentation to mention futures and async. --- core/lib/src/data/data.rs | 4 +- core/lib/src/data/data_stream.rs | 2 +- core/lib/src/data/from_data.rs | 35 ++++++++------- core/lib/src/fairing/ad_hoc.rs | 6 ++- core/lib/src/handler.rs | 15 ++++--- core/lib/src/request/from_request.rs | 66 ++++++++++++++++++++++++++++ core/lib/src/response/mod.rs | 3 +- core/lib/src/response/responder.rs | 23 +++------- core/lib/src/rocket.rs | 10 +++-- core/lib/src/shutdown.rs | 10 ++++- 10 files changed, 123 insertions(+), 51 deletions(-) diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index 9e03a6fd..90d6dc24 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -126,7 +126,7 @@ impl Data { self.is_complete } - /// A helper method to write the body of the request to any `Write` type. + /// A helper method to write the body of the request to any `AsyncWrite` type. /// /// This method is identical to `tokio::io::copy(&mut data.open(), &mut writer)`. /// @@ -152,7 +152,7 @@ impl Data { /// determined by `path`. /// /// This method is identical to - /// `io::copy(&mut self.open(), &mut File::create(path)?)`. + /// `tokio::io::copy(&mut self.open(), &mut File::create(path).await?)`. /// /// # Example /// diff --git a/core/lib/src/data/data_stream.rs b/core/lib/src/data/data_stream.rs index db4e782f..0ebd04fb 100644 --- a/core/lib/src/data/data_stream.rs +++ b/core/lib/src/data/data_stream.rs @@ -10,7 +10,7 @@ use crate::ext::AsyncReadBody; /// This stream can only be obtained by calling /// [`Data::open()`](crate::data::Data::open()). The stream contains all of the data /// in the body of the request. It exposes no methods directly. Instead, it must -/// be used as an opaque [`Read`] structure. +/// be used as an opaque [`AsyncRead`] structure. pub struct DataStream(pub(crate) Vec, pub(crate) AsyncReadBody); // TODO.async: Consider implementing `AsyncBufRead` diff --git a/core/lib/src/data/from_data.rs b/core/lib/src/data/from_data.rs index 38a843e7..8ec0b308 100644 --- a/core/lib/src/data/from_data.rs +++ b/core/lib/src/data/from_data.rs @@ -112,7 +112,10 @@ pub type Transformed<'a, T> = Outcome<&'a >::Borrowed, >::Error> >; +/// Type alias to the `Future` returned by [`FromData::transform`]. pub type TransformFuture<'fut, T, E> = BoxFuture<'fut, Transform>>; + +/// Type alias to the `Future` returned by [`FromData::from_data`]. pub type FromDataFuture<'fut, T, E> = BoxFuture<'fut, Outcome>; /// Trait implemented by data guards to derive a value from request body data. @@ -161,14 +164,15 @@ pub type FromDataFuture<'fut, T, E> = BoxFuture<'fut, Outcome>; /// implement `FromDataSimple` automatically implement `FromData`. /// /// When exercising a data guard, Rocket first calls the guard's -/// [`FromData::transform()`] method and then subsequently calls the guard's -/// [`FromData::from_data()`] method. Rocket stores data returned by -/// [`FromData::transform()`] on the stack. If `transform` returns a -/// [`Transform::Owned`], Rocket moves the data back to the data guard in the -/// subsequent `from_data` call as a `Transform::Owned`. If instead `transform` -/// returns a [`Transform::Borrowed`] variant, Rocket calls `borrow()` on the -/// owned value, producing a borrow of the associated [`FromData::Borrowed`] -/// type and passing it as a `Transform::Borrowed`. +/// [`FromData::transform()`] method and awaits on the returned future, then +/// calls the guard's [`FromData::from_data()`] method and awaits on that +/// returned future. Rocket stores data returned by [`FromData::transform()`] on +/// the stack. If `transform` returns a [`Transform::Owned`], Rocket moves the +/// data back to the data guard in the subsequent `from_data` call as a +/// `Transform::Owned`. If instead `transform` returns a [`Transform::Borrowed`] +/// variant, Rocket calls `borrow()` on the owned value, producing a borrow of +/// the associated [`FromData::Borrowed`] type and passing it as a +/// `Transform::Borrowed`. /// /// ## Example /// @@ -349,11 +353,11 @@ pub trait FromData<'a>: Sized { /// associated type should be set to `Self::Owned`. type Borrowed: ?Sized; - /// Transforms `data` into a value of type `Self::Owned`. + /// Asynchronously transforms `data` into a value of type `Self::Owned`. /// - /// If this method returns a `Transform::Owned(Self::Owned)`, then + /// If the returned future resolves to `Transform::Owned(Self::Owned)`, then /// `from_data` should subsequently be called with a `data` value of - /// `Transform::Owned(Self::Owned)`. If this method returns a + /// `Transform::Owned(Self::Owned)`. If the future resolves to /// `Transform::Borrowed(Self::Owned)`, `from_data` should subsequently be /// called with a `data` value of `Transform::Borrowed(&Self::Borrowed)`. In /// other words, the variant of `Transform` returned from this method is @@ -369,8 +373,8 @@ pub trait FromData<'a>: Sized { /// returned. On failure, `Failure` is returned. fn transform<'r>(request: &'r Request<'_>, data: Data) -> TransformFuture<'r, Self::Owned, Self::Error>; - /// Validates, parses, and converts the incoming request body data into an - /// instance of `Self`. + /// Asynchronously validates, parses, and converts the incoming request body + /// data into an instance of `Self`. /// /// If validation and parsing succeeds, an outcome of `Success` is returned. /// If the data is not appropriate given the type of `Self`, `Forward` is @@ -512,12 +516,11 @@ impl<'a> FromData<'a> for Data { /// # fn main() { } /// ``` pub trait FromDataSimple: Sized { - // TODO.async: Can/should we relax this 'static? And how? /// The associated error to be returned when the guard fails. type Error: Send + 'static; - /// Validates, parses, and converts an instance of `Self` from the incoming - /// request body data. + /// Asynchronously validates, parses, and converts an instance of `Self` + /// from the incoming request body data. /// /// If validation and parsing succeeds, an outcome of `Success` is returned. /// If the data is not appropriate given the type of `Self`, `Forward` is diff --git a/core/lib/src/fairing/ad_hoc.rs b/core/lib/src/fairing/ad_hoc.rs index c3520856..fd63c1bd 100644 --- a/core/lib/src/fairing/ad_hoc.rs +++ b/core/lib/src/fairing/ad_hoc.rs @@ -94,7 +94,8 @@ impl AdHoc { } /// Constructs an `AdHoc` request fairing named `name`. The function `f` - /// will be called by Rocket when a new request is received. + /// will be called and the returned `Future` will be `await`ed by Rocket + /// when a new request is received. /// /// # Example /// @@ -116,7 +117,8 @@ impl AdHoc { } /// Constructs an `AdHoc` response fairing named `name`. The function `f` - /// will be called by Rocket when a response is ready to be sent. + /// will be called and the returned `Future` will be `await`ed by Rocket + /// when a response is ready to be sent. /// /// # Example /// diff --git a/core/lib/src/handler.rs b/core/lib/src/handler.rs index 11ef07d7..82693968 100644 --- a/core/lib/src/handler.rs +++ b/core/lib/src/handler.rs @@ -140,13 +140,13 @@ pub trait Handler: Cloneable + Send + Sync + 'static { /// Called by Rocket when a `Request` with its associated `Data` should be /// handled by this handler. /// - /// The variant of `Outcome` returned determines what Rocket does next. If - /// the return value is a `Success(Response)`, the wrapped `Response` is - /// used to respond to the client. If the return value is a - /// `Failure(Status)`, the error catcher for `Status` is invoked to generate - /// a response. Otherwise, if the return value is `Forward(Data)`, the next - /// matching route is attempted. If there are no other matching routes, the - /// `404` error catcher is invoked. + /// The variant of `Outcome` returned by the returned `Future` determines + /// what Rocket does next. If the return value is a `Success(Response)`, the + /// wrapped `Response` is used to respond to the client. If the return value + /// is a `Failure(Status)`, the error catcher for `Status` is invoked to + /// generate a response. Otherwise, if the return value is `Forward(Data)`, + /// the next matching route is attempted. If there are no other matching + /// routes, the `404` error catcher is invoked. fn handle<'r>(&self, request: &'r Request<'_>, data: Data) -> HandlerFuture<'r>; } @@ -186,6 +186,7 @@ impl Handler for F /// The type of an error handler. 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> { diff --git a/core/lib/src/request/from_request.rs b/core/lib/src/request/from_request.rs index e3ca2b32..78cee3ca 100644 --- a/core/lib/src/request/from_request.rs +++ b/core/lib/src/request/from_request.rs @@ -37,6 +37,68 @@ impl IntoOutcome for Result { /// Type alias for the future returned by [`FromRequestAsync::from_request`]. pub type FromRequestFuture<'fut, T, E> = BoxFuture<'fut, Outcome>; +/// Trait implemented by asynchronous request guards to derive a value from +/// incoming requests. +/// +/// For more information on request guards in general, see the [`FromRequest`] +/// trait. +/// +/// ## Example +/// +/// Imagine you're running an authenticated service backed by a database. You +/// want to ensure that certain handlers will only run if a valid API key is +/// present in the request, and you need to make a database request in order to +/// determine if the key is valid or not. +/// +/// ```rust +/// # #![feature(proc_macro_hygiene)] +/// # #[macro_use] extern crate rocket; +/// # +/// # struct Database; +/// # impl Database { +/// # async fn check_key(&self, key: &str) -> bool { +/// # true +/// # } +/// # } +/// # +/// use rocket::Outcome; +/// use rocket::http::Status; +/// use rocket::request::{self, Request, State, FromRequestAsync}; +/// +/// struct ApiKey(String); +/// +/// #[derive(Debug)] +/// enum ApiKeyError { +/// BadCount, +/// Missing, +/// Invalid, +/// } +/// +/// impl<'a, 'r> FromRequestAsync<'a, 'r> for ApiKey { +/// type Error = ApiKeyError; +/// +/// fn from_request(request: &'a Request<'r>) -> request::FromRequestFuture<'a, Self, Self::Error> { +/// Box::pin(async move { +/// let keys: Vec<_> = request.headers().get("x-api-key").collect(); +/// let database: State<'_, Database> = request.guard().expect("get database connection"); +/// match keys.len() { +/// 0 => Outcome::Failure((Status::BadRequest, ApiKeyError::Missing)), +/// 1 if database.check_key(keys[0]).await => Outcome::Success(ApiKey(keys[0].to_string())), +/// 1 => Outcome::Failure((Status::BadRequest, ApiKeyError::Invalid)), +/// _ => Outcome::Failure((Status::BadRequest, ApiKeyError::BadCount)), +/// } +/// }) +/// } +/// } +/// +/// #[get("/sensitive")] +/// fn sensitive(key: ApiKey) -> &'static str { +/// # let _key = key; +/// "Sensitive data." +/// } +/// +/// # fn main() { } +/// ``` pub trait FromRequestAsync<'a, 'r>: Sized { /// The associated error to be returned if derivation fails. type Error: Debug; @@ -65,6 +127,10 @@ pub trait FromRequestAsync<'a, 'r>: Sized { /// the handler. Rocket only dispatches requests to a handler when all of its /// guards pass. /// +/// Request guards can be made *asynchronous* by implementing +/// [`FromRequestAsync`] instead of `FromRequest`. This is useful when the +/// validation requires working with a database or performing other I/O. +/// /// ## Example /// /// The following dummy handler makes use of three request guards, `A`, `B`, and diff --git a/core/lib/src/response/mod.rs b/core/lib/src/response/mod.rs index 8d49b5e3..d86da660 100644 --- a/core/lib/src/response/mod.rs +++ b/core/lib/src/response/mod.rs @@ -48,5 +48,6 @@ pub use self::debug::Debug; /// Type alias for the `Result` of a `Responder::respond` call. pub type Result<'r> = std::result::Result, crate::http::Status>; -/// Type alias for the `Result` of a `Responder::respond` call. + +/// Type alias for the `Future` returned by a `Responder::respond` call. pub type ResultFuture<'r> = futures_util::future::BoxFuture<'r, Result<'r>>; diff --git a/core/lib/src/response/responder.rs b/core/lib/src/response/responder.rs index 62c95be1..b60aab81 100644 --- a/core/lib/src/response/responder.rs +++ b/core/lib/src/response/responder.rs @@ -26,7 +26,8 @@ use crate::request::Request; /// /// # Return Value /// -/// A `Responder` returns an `Ok(Response)` or an `Err(Status)`: +/// A `Responder` returns a `Future` whose output type is an `Ok(Response)` or +/// an `Err(Status)`: /// /// * An `Ok` variant means that the `Responder` was successful in generating /// a `Response`. The `Response` will be written out to the client. @@ -84,14 +85,7 @@ use crate::request::Request; /// the client. Otherwise, an `Err` with status **404 Not Found** is /// returned and a warning is printed to the console. /// -/// * **Result<T, E>** _where_ **E: Debug** -/// -/// If the `Result` is `Ok`, the wrapped responder is used to respond to the -/// client. Otherwise, an `Err` with status **500 Internal Server Error** is -/// returned and the error is printed to the console using the `Debug` -/// implementation. -/// -/// * **Result<T, E>** _where_ **E: Debug + Responder** +/// * **Result<T, E>** /// /// If the `Result` is `Ok`, the wrapped `Ok` responder is used to respond /// to the client. If the `Result` is `Err`, the wrapped `Err` responder is @@ -102,13 +96,6 @@ use crate::request::Request; /// This section describes a few best practices to take into account when /// implementing `Responder`. /// -/// ## Debug -/// -/// A type implementing `Responder` should implement the `Debug` trait when -/// possible. This is because the `Responder` implementation for `Result` -/// requires its `Err` type to implement `Debug`. Therefore, a type implementing -/// `Debug` can more easily be composed. -/// /// ## Joining and Merging /// /// When chaining/wrapping other `Responder`s, use the @@ -277,7 +264,7 @@ impl Responder<'_> for () { /// 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> + Send + 'r> Responder<'r> for Option { +impl<'r, R: Responder<'r>> Responder<'r> for Option { fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> { match self { Some(r) => r.respond_to(req), @@ -291,7 +278,7 @@ impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Option { /// Responds with the wrapped `Responder` in `self`, whether it is `Ok` or /// `Err`. -impl<'r, R: Responder<'r> + Send + 'r, E: Responder<'r> + Send + 'r> Responder<'r> for Result { +impl<'r, R: Responder<'r>, E: Responder<'r>> Responder<'r> for Result { fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> { match self { Ok(responder) => responder.respond_to(req), diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 139a3cd3..b9681715 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -772,9 +772,9 @@ impl Rocket { .map_err(crate::error::Error::Run) } - /// Returns a `Future` that completes when the server is shut down or - /// errors. If the `ctrl_c_shutdown` feature is enabled, `Ctrl-C` will be - /// handled as a shutdown signal. + /// Returns a `Future` that drives the server and completes when the server + /// is shut down or errors. If the `ctrl_c_shutdown` feature is enabled, + /// the server will shut down gracefully once `Ctrl-C` is pressed. /// /// # Example /// @@ -870,6 +870,10 @@ impl Rocket { /// unless a shutdown is requested via a [`ShutdownHandle`] or there is an /// error. /// + /// This is a convenience function that creates a suitable default runtime + /// and launches the server on that runtime. If you already have a runtime, + /// use the [`Rocket::serve`] method instead. + /// /// # Error /// /// If there is a problem starting the application, a [`LaunchError`] is diff --git a/core/lib/src/shutdown.rs b/core/lib/src/shutdown.rs index d7b7360a..b30853ce 100644 --- a/core/lib/src/shutdown.rs +++ b/core/lib/src/shutdown.rs @@ -1,6 +1,12 @@ use crate::request::{FromRequest, Outcome, Request}; use tokio::sync::mpsc; +/// A `ShutdownHandle` can be used to instruct a Rocket server to gracefully +/// shut down. Once a server shutdown has been requested manually by calling +/// [`ShutdownHandle::shutdown()`] or automatically by `Ctrl-C` being pressed +/// (if enabled), Rocket will finish handling any pending requests and return to +/// the caller of [`Rocket::serve`] or [`Rocket::launch`]. +/// /// # Example /// /// ```rust @@ -28,7 +34,9 @@ use tokio::sync::mpsc; pub struct ShutdownHandle(pub(crate) mpsc::Sender<()>); impl ShutdownHandle { - /// Notify Rocket to shut down gracefully. + /// Notify Rocket to shut down gracefully. This function returns + /// immediately; pending requests will continue to run until completion + /// before the actual shutdown occurs. #[inline] pub fn shutdown(mut self) { // Intentionally ignore any error, as the only scenarios this can happen