hyperdrive/lib.rs
1//! Composable (a)synchronous HTTP request routing, guarding and decoding.
2//!
3//! This crate provides [Rocket]-inspired HTTP route definitions based on
4//! attributes (`#[get("/user/{id}")]`). It is based on the [`hyper`]
5//! and [`http`] crates, works on **stable Rust**, and supports writing both
6//! synchronous and asynchronous (via [futures 0.1]) apps.
7//!
8//! Check out the examples below for a small taste of how this library can be
9//! used. If you want to dive in deeper, you can check out the [`FromRequest`]
10//! trait, which provides the custom derive that powers most of the magic in
11//! this crate.
12//!
13//! [Rocket]: https://rocket.rs/
14//! [`hyper`]: https://hyper.rs/
15//! [`http`]: https://docs.rs/http
16//! [futures 0.1]: https://docs.rs/futures/0.1
17//! [`FromRequest`]: trait.FromRequest.html
18//!
19//! # Examples
20//!
21//! Use the hyper service adapter [`AsyncService`] to create your async
22//! server without much boilerplate:
23//!
24//! ```
25//! use hyper::{Server, Response, Body};
26//! use hyperdrive::{service::AsyncService, FromRequest};
27//! use futures::IntoFuture;
28//!
29//! #[derive(FromRequest)]
30//! enum Route {
31//! #[get("/")]
32//! Index,
33//!
34//! #[get("/users/{id}")]
35//! UserInfo { id: u32 },
36//! }
37//!
38//! let srv = Server::bind(&"127.0.0.1:0".parse().unwrap())
39//! .serve(AsyncService::new(|route: Route, _| {
40//! match route {
41//! Route::Index => {
42//! Ok(Response::new(Body::from("Hello World!"))).into_future()
43//! }
44//! Route::UserInfo { id } => {
45//! // You could do an async database query to fetch the user data here
46//! Ok(Response::new(Body::from(format!("User #{}", id)))).into_future()
47//! }
48//! }
49//! }));
50//! ```
51//!
52//! If your app doesn't need to be asynchronous and you'd prefer to write sync
53//! code, you can do that by using [`SyncService`]:
54//!
55//! ```
56//! use hyper::{Server, Response, Body};
57//! use hyperdrive::{service::SyncService, FromRequest};
58//!
59//! #[derive(FromRequest)]
60//! enum Route {
61//! #[get("/")]
62//! Index,
63//!
64//! #[get("/users/{id}")]
65//! UserInfo { id: u32 },
66//! }
67//!
68//! let srv = Server::bind(&"127.0.0.1:0".parse().unwrap())
69//! .serve(SyncService::new(|route: Route, _| {
70//! // This closure can block freely, and has to return a `Response<Body>`
71//! match route {
72//! Route::Index => {
73//! Response::new(Body::from("Hello World!"))
74//! },
75//! Route::UserInfo { id } => {
76//! Response::new(Body::from(format!("User #{}", id)))
77//! }
78//! }
79//! }));
80//! ```
81//!
82//! If the provided service adapters aren't sufficient for your use case, you
83//! can always manually use the [`FromRequest`] methods, and hook it up to your
84//! hyper `Service` manually:
85//!
86//! ```
87//! use hyper::{Request, Response, Body, Method, service::Service};
88//! use futures::Future;
89//! use hyperdrive::{FromRequest, DefaultFuture, BoxedError, NoContext};
90//!
91//! #[derive(FromRequest)]
92//! enum Route {
93//! #[get("/")]
94//! Index,
95//!
96//! #[get("/users/{id}")]
97//! UserInfo { id: u32 },
98//! }
99//!
100//! // Define your hyper `Service`:
101//! struct MyService;
102//!
103//! impl Service for MyService {
104//! type ReqBody = Body;
105//! type ResBody = Body;
106//! type Error = BoxedError;
107//! type Future = DefaultFuture<Response<Body>, BoxedError>;
108//!
109//! fn call(&mut self, req: Request<Body>) -> Self::Future {
110//! let is_head = req.method() == Method::HEAD;
111//! let future = Route::from_request(req, NoContext).and_then(|route| Ok(match route {
112//! Route::Index => {
113//! Response::new(Body::from("Hello world!"))
114//! }
115//! Route::UserInfo { id } => {
116//! Response::new(Body::from(format!("User #{} is secret!", id)))
117//! }
118//! })).map(move |resp| {
119//! if is_head {
120//! // Response to HEAD requests must have an empty body
121//! resp.map(|_| Body::empty())
122//! } else {
123//! resp
124//! }
125//! });
126//!
127//! Box::new(future)
128//! }
129//! }
130//! ```
131//!
132//! For detailed documentation on the custom derive syntax, refer to the docs of
133//! [`FromRequest`].
134//!
135//! [`AsyncService`]: service/struct.AsyncService.html
136//! [`SyncService`]: service/struct.SyncService.html
137//! [`FromRequest`]: trait.FromRequest.html
138
139/*
140
141TODO:
142* How to handle 2015/2018 compat with the proc-macro?
143* Good example that fetches a session from a DB
144
145*/
146// Deny certain warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show
147// *any* warnings at all.
148#![doc(test(attr(deny(unused_imports, unused_must_use))))]
149#![doc(html_root_url = "https://docs.rs/hyperdrive/0.2.0")]
150#![warn(missing_debug_implementations)]
151#![warn(missing_docs)]
152#![warn(rust_2018_idioms)]
153
154pub mod body;
155mod error;
156mod readme;
157pub mod service;
158
159pub use error::*;
160pub use hyperderive::*;
161
162// Reexport public deps for use by the custom derive
163pub use {futures, http, hyper, serde};
164
165// These are hidden because the user never actually interacts with them. They're
166// only used by the generated code internally.
167#[doc(hidden)]
168pub use {lazy_static::lazy_static, regex};
169
170use futures::{Future, IntoFuture};
171use std::sync::Arc;
172use tokio::runtime::current_thread::Runtime;
173
174/// A default boxed future that may be returned from [`FromRequest`],
175/// [`FromBody`] and [`Guard`] implementations.
176///
177/// The future is required to be `Send` to allow running it on a multi-threaded
178/// executor.
179///
180/// [`FromRequest`]: trait.FromRequest.html
181/// [`FromBody`]: trait.FromBody.html
182/// [`Guard`]: trait.Guard.html
183pub type DefaultFuture<T, E> = Box<dyn Future<Item = T, Error = E> + Send>;
184
185/// A boxed `std::error::Error` that can be used when the actual error type is
186/// unknown.
187pub type BoxedError = Box<dyn std::error::Error + Send + Sync>;
188
189/// Trait for asynchronous conversion from HTTP requests.
190///
191/// # `#[derive(FromRequest)]`
192///
193/// This trait can be derived for enums to generate a request router and
194/// decoder. Here's a simple example:
195///
196/// ```
197/// use hyperdrive::{FromRequest, body::Json};
198/// # use serde::Deserialize;
199///
200/// #[derive(FromRequest)]
201/// enum Routes {
202/// #[get("/")]
203/// Index,
204///
205/// #[get("/users/{id}")]
206/// User { id: u32 },
207///
208/// #[post("/login")]
209/// Login {
210/// #[body]
211/// data: Json<Login>,
212/// },
213/// }
214///
215/// #[derive(Deserialize)]
216/// struct Login {
217/// email: String,
218/// password: String,
219/// }
220/// ```
221///
222/// Calling `Routes::from_request` will result in `Routes::Index` for a `GET /`
223/// request, and in `Routes::User` for a `GET /users/123` request, for example.
224/// A `POST /login` request will end up as `Routes::Login`, decoding the POSTed
225/// JSON body.
226///
227/// The generated `FromRequest` implementation will always use
228/// [`DefaultFuture<Self, BoxedError>`][`DefaultFuture`] as the associated
229/// `Result` type.
230///
231/// Note that the generated implementation will make use of `.and_then()` to
232/// chain asynchronous operations instead of running them in parallel using
233/// `join_all`. This is because it simplifies the code and doesn't require
234/// making use of boxed futures everywhere in the generated code. Multiple
235/// requests will still be handled in parallel, so this should not negatively
236/// affect performance.
237///
238/// In order to keep the implementation simple and user code more easily
239/// understandable, overlapping paths are not allowed (unless the paths are
240/// *exactly* the same, and the method differs), so the following will fail to
241/// compile:
242///
243/// ```compile_fail
244/// use from_request::{FromRequest, body::Json};
245/// # use serde::Deserialize;
246///
247/// #[derive(FromRequest)] //~ ERROR: route `#[get("/users/me")]` overlaps with ...
248/// enum Routes {
249/// #[get("/users/{id}")]
250/// User { id: u32 },
251///
252/// #[get("/users/me")]
253/// Me,
254/// }
255/// ```
256///
257/// To fix this, you can define a custom type implementing `FromStr` and use
258/// that:
259///
260/// ```
261/// use hyperdrive::FromRequest;
262/// # use std::str::FromStr;
263/// # use std::num::ParseIntError;
264///
265/// #[derive(FromRequest)]
266/// enum Routes {
267/// #[get("/users/{id}")]
268/// User { id: UserId },
269/// }
270///
271/// enum UserId {
272/// /// User by database ID.
273/// Id(u32),
274/// /// The currently logged-in user.
275/// Me,
276/// }
277///
278/// impl FromStr for UserId {
279/// type Err = ParseIntError;
280///
281/// fn from_str(s: &str) -> Result<Self, Self::Err> {
282/// if s == "me" {
283/// Ok(UserId::Me)
284/// } else {
285/// Ok(UserId::Id(s.parse()?))
286/// }
287/// }
288/// }
289/// ```
290///
291/// ## Implicit `HEAD` routes
292///
293/// The custom derive will create a `HEAD` route for every defined `GET` route,
294/// unless you define one yourself. If your app uses [`AsyncService`] or
295/// [`SyncService`], those adapters will automatically take care of dropping the
296/// body from the response to `HEAD` requests. If you manually call
297/// [`FromRequest::from_request`][`from_request`], you have to make sure no body
298/// is sent back for `HEAD` requests.
299///
300/// ## Extracting Request Data
301///
302/// The custom derive provides easy access to various kinds of data encoded in a
303/// request:
304///
305/// * The Request path (`/users/or/other/stuff`)
306/// * Query parameters (`?name=val`)
307/// * The request body
308///
309/// ### Extracting Path Segments (`{field}` syntax)
310///
311/// In a route attribute, the `{field}` placeholder syntax will match a path
312/// segment and convert it to the type of the named field using `FromStr`:
313///
314/// ```notrust
315/// #[get("/users/{id}")]
316/// ```
317///
318/// To extract multiple path segments this way, the `{field...}` syntax can be
319/// used at the end of the path, which will consume the rest of the path:
320///
321/// ```notrust
322/// #[get("/static/{path...}")]
323/// ```
324///
325/// If the `FromStr` conversion fails, the generated `FromRequest`
326/// implementation will bail out with an error (in other words, this feature
327/// cannot be used to try multiple routes in sequence until one matches).
328///
329/// ### Extracting the request body (`#[body]` attribute)
330///
331/// Putting `#[body]` on a field of a variant will deserialize the request body
332/// using the [`FromBody`] trait and store the result in the annotated field:
333///
334/// ```notrust
335/// #[post("/login")]
336/// Login {
337/// #[body]
338/// data: Json<Login>,
339/// },
340/// ```
341///
342/// The type of the field must implement [`FromBody`]. The [`body`] module
343/// contains predefined adapters implementing that trait, which work with any
344/// type implementing `Deserialize`.
345///
346/// ### Extracting query parameters (`#[query_params]` attribute)
347///
348/// The route attribute cannot match or extract query parameters (`?name=val`).
349/// Instead, query parameters can be extracted by marking a field in the struct
350/// with the `#[query_params]` attribute:
351///
352/// ```
353/// use hyperdrive::FromRequest;
354/// # use serde::Deserialize;
355///
356/// #[derive(FromRequest)]
357/// enum Routes {
358/// #[get("/users")]
359/// UserList {
360/// #[query_params]
361/// pagination: Option<Pagination>,
362/// },
363/// }
364///
365/// #[derive(Deserialize)]
366/// struct Pagination {
367/// start_id: u32,
368/// count: u32,
369/// }
370/// ```
371///
372/// A request like `GET /users?start_id=42&count=10` would thus end up with a
373/// corresponding `Pagination` object, while `GET /users` would store `None` in
374/// the `pagination` field.
375///
376/// The type of the `#[query_params]` field must implement serde's `Deserialize`
377/// trait and the conversion will be performed using the `serde_urlencoded`
378/// crate.
379///
380/// ## Guards
381///
382/// Guards can be used to prevent a route from being called when a condition is
383/// not fulfilled (for example, when the user isn't logged in). They can also
384/// extract arbitrary data from the request headers (eg. a session ID, or the
385/// User-Agent string).
386///
387/// All fields that are neither mentioned in the route path nor annotated with
388/// an attribute are considered guards and thus must implement the [`Guard`]
389/// trait.
390///
391/// ```
392/// use hyperdrive::{FromRequest, Guard};
393/// # use hyperdrive::{BoxedError, NoContext};
394/// # use std::sync::Arc;
395///
396/// struct User {
397/// id: u32,
398/// // ...
399/// }
400///
401/// impl Guard for User {
402/// // (omitted for brevity)
403/// # type Context = NoContext;
404/// # type Result = Result<Self, BoxedError>;
405/// # fn from_request(_: &Arc<http::Request<()>>, _: &NoContext) -> Result<Self, BoxedError> {
406/// # User { id: 0 }.id;
407/// # Ok(User { id: 0 })
408/// # }
409/// }
410///
411/// #[derive(FromRequest)]
412/// enum Route {
413/// #[get("/login")]
414/// LoginForm,
415///
416/// #[get("/staff")]
417/// Staff {
418/// // Require a logged-in user to make this request
419/// user: User,
420/// },
421/// }
422/// ```
423///
424/// ## Forwarding
425///
426/// A field whose type implements `FromRequest` can be marked with `#[forward]`.
427/// The library will then generate code that invokes this nested `FromRequest`
428/// implementation.
429///
430/// This feature can not be combined with `#[body]` inside the same variant,
431/// since both consume the request body.
432///
433/// Currently, this is limited to `FromRequest` implementations that use the
434/// same [`RequestContext`] as the outer type (ie. no automatic `AsRef`
435/// conversion will take place).
436///
437/// A variant or struct defining a `#[forward]` field does not have to define
438/// a route. If no other route matches, this variant will automatically be
439/// created, and is considered a *fallback route*.
440///
441/// Combined with generics, this feature can be used to make request wrappers
442/// that attach a guard or a guard group to any type implementing `FromRequest`:
443///
444/// ```
445/// use hyperdrive::{FromRequest, Guard};
446/// # use hyperdrive::{NoContext, BoxedError};
447/// # use std::sync::Arc;
448///
449/// struct User;
450/// impl Guard for User {
451/// // (omitted for brevity)
452/// # type Context = NoContext;
453/// # type Result = Result<Self, BoxedError>;
454/// # fn from_request(_: &Arc<http::Request<()>>, _: &NoContext) -> Result<Self, BoxedError> {
455/// # Ok(User)
456/// # }
457/// }
458///
459/// #[derive(FromRequest)]
460/// struct Authenticated<T> {
461/// user: User,
462///
463/// #[forward]
464/// inner: T,
465/// }
466/// ```
467///
468/// ## Changing the `Context` type
469///
470/// By default, the generated code will use [`NoContext`] as the associated
471/// `Context` type. You can change this to any other type that implements
472/// [`RequestContext`] by putting a `#[context(MyContext)]` attribute on the
473/// type:
474///
475/// ```
476/// # struct MyDatabaseConnection;
477/// use hyperdrive::{FromRequest, RequestContext};
478///
479/// #[derive(RequestContext)]
480/// struct MyContext {
481/// db: MyDatabaseConnection,
482/// }
483///
484/// #[derive(FromRequest)]
485/// #[context(MyContext)]
486/// enum Routes {
487/// #[get("/users")]
488/// UserList,
489/// }
490/// ```
491///
492/// For more info on this, refer to the [`RequestContext`] trait.
493///
494/// [`AsyncService`]: service/struct.AsyncService.html
495/// [`SyncService`]: service/struct.SyncService.html
496/// [`FromBody`]: trait.FromBody.html
497/// [`RequestContext`]: trait.RequestContext.html
498/// [`Guard`]: trait.Guard.html
499/// [`NoContext`]: struct.NoContext.html
500/// [`DefaultFuture`]: type.DefaultFuture.html
501/// [`body`]: body/index.html
502/// [`from_request`]: #tymethod.from_request
503pub trait FromRequest: Sized {
504 /// A context parameter passed to [`from_request`].
505 ///
506 /// This can be used to pass application-specific data like a logger or a
507 /// database connection around.
508 ///
509 /// If no context is needed, this should be set to [`NoContext`], which is a
510 /// context type that can be obtained from any [`RequestContext`] via
511 /// `AsRef`.
512 ///
513 /// [`from_request`]: #tymethod.from_request
514 /// [`NoContext`]: struct.NoContext.html
515 /// [`RequestContext`]: trait.RequestContext.html
516 type Context: RequestContext;
517
518 /// The future returned by [`from_request`].
519 ///
520 /// Because `impl Trait` cannot be used inside traits (and named
521 /// existential types aren't yet stable), the type here might not be
522 /// nameable. In that case, you can set it to
523 /// [`DefaultFuture<Self, BoxedError>`][`DefaultFuture`] and box the
524 /// returned future.
525 ///
526 /// [`DefaultFuture`]: type.DefaultFuture.html
527 /// [`from_request`]: #tymethod.from_request
528 type Future: Future<Item = Self, Error = BoxedError> + Send;
529
530 /// Creates a `Self` from an HTTP request, asynchronously.
531 ///
532 /// This takes the request metadata, body, and a user-defined context. Only
533 /// the body is consumed.
534 ///
535 /// Implementations of this function must not block, since this function is
536 /// always run on a futures executor. If you need to perform blocking I/O or
537 /// long-running computations, you can call [`tokio_threadpool::blocking`].
538 ///
539 /// # Parameters
540 ///
541 /// * **`request`**: HTTP request data (headers, path, method, etc.).
542 /// * **`body`**: The streamed HTTP body.
543 /// * **`context`**: The user-defined context.
544 ///
545 /// [`tokio_threadpool::blocking`]: ../tokio_threadpool/fn.blocking.html
546 fn from_request_and_body(
547 request: &Arc<http::Request<()>>,
548 body: hyper::Body,
549 context: Self::Context,
550 ) -> Self::Future;
551
552 /// Create a `Self` from an HTTP request, asynchronously.
553 ///
554 /// This consumes the request *and* the context.
555 ///
556 /// Implementations of this function must not block, since this function is
557 /// always run on a futures executor. If you need to perform blocking I/O or
558 /// long-running computations, you can call [`tokio_threadpool::blocking`].
559 ///
560 /// A blocking wrapper around this method is provided by
561 /// [`from_request_sync`].
562 ///
563 /// # Parameters
564 ///
565 /// * **`request`**: An HTTP request from the `http` crate, containing a
566 /// `hyper::Body`.
567 /// * **`context`**: User-defined context.
568 ///
569 /// [`from_request_sync`]: #method.from_request_sync
570 /// [`tokio_threadpool::blocking`]: ../tokio_threadpool/fn.blocking.html
571 fn from_request(request: http::Request<hyper::Body>, context: Self::Context) -> Self::Future {
572 let (parts, body) = request.into_parts();
573 let request = Arc::new(http::Request::from_parts(parts, ()));
574
575 Self::from_request_and_body(&request, body, context)
576 }
577
578 /// Create a `Self` from an HTTP request, synchronously.
579 ///
580 /// This is a blocking version of [`from_request`]. The provided default
581 /// implementation will internally create a single-threaded tokio runtime to
582 /// perform the conversion and receive the request body.
583 ///
584 /// Note that this does not provide a way to *write* a blocking version of
585 /// [`from_request`]. Implementors of this trait must always implement
586 /// [`from_request`] in a non-blocking fashion, even if they *also*
587 /// implement this method.
588 ///
589 /// [`from_request`]: #tymethod.from_request
590 fn from_request_sync(
591 request: http::Request<hyper::Body>,
592 context: Self::Context,
593 ) -> Result<Self, BoxedError> {
594 let mut rt = Runtime::new().expect("couldn't start single-threaded tokio runtime");
595 rt.block_on(Self::from_request(request, context).into_future())
596 }
597}
598
599/// A request guard that checks a condition or extracts data out of an incoming
600/// request.
601///
602/// For example, this could be used to extract an `Authorization` header and
603/// verify user credentials, or to look up a session token in a database.
604///
605/// A `Guard` can not access the request body. If you need to do that, implement
606/// [`FromBody`] instead.
607///
608/// # Examples
609///
610/// Define a guard that ensures that required request headers are present:
611///
612/// ```
613/// # use hyperdrive::{Guard, NoContext, BoxedError};
614/// # use std::sync::Arc;
615/// struct MustFrobnicate;
616///
617/// impl Guard for MustFrobnicate {
618/// type Context = NoContext;
619/// type Result = Result<Self, BoxedError>;
620///
621/// fn from_request(request: &Arc<http::Request<()>>, context: &Self::Context) -> Self::Result {
622/// if request.headers().contains_key("X-Frobnicate") {
623/// Ok(MustFrobnicate)
624/// } else {
625/// let msg = "request did not contain mandatory `X-Frobnicate` header";
626/// Err(String::from(msg).into())
627/// }
628/// }
629/// }
630/// ```
631///
632/// Use server settings stored in a [`RequestContext`] to exclude certain user
633/// agents:
634///
635/// ```
636/// # use hyperdrive::{Guard, RequestContext, BoxedError};
637/// # use std::sync::Arc;
638/// #[derive(RequestContext)]
639/// struct ForbiddenAgents {
640/// agents: Vec<String>,
641/// }
642///
643/// struct RejectForbiddenAgents;
644///
645/// impl Guard for RejectForbiddenAgents {
646/// type Context = ForbiddenAgents;
647/// type Result = Result<Self, BoxedError>;
648///
649/// fn from_request(request: &Arc<http::Request<()>>, context: &Self::Context) -> Self::Result {
650/// let agent = request.headers().get("User-Agent")
651/// .ok_or_else(|| String::from("No User-Agent header"))?;
652///
653/// if context.agents.iter().any(|f| f == agent) {
654/// Err(String::from("This User-Agent is forbidden!").into())
655/// } else {
656/// Ok(RejectForbiddenAgents)
657/// }
658/// }
659/// }
660/// ```
661///
662/// [`FromBody`]: trait.FromBody.html
663/// [`RequestContext`]: trait.RequestContext.html
664pub trait Guard: Sized {
665 /// A context parameter passed to [`Guard::from_request`].
666 ///
667 /// This can be used to pass application-specific data like a database
668 /// connection or server configuration (eg. for limiting the maximum HTTP
669 /// request size) around.
670 ///
671 /// If no context is needed, this should be set to [`NoContext`].
672 ///
673 /// [`Guard::from_request`]: #tymethod.from_request
674 /// [`NoContext`]: struct.NoContext.html
675 type Context: RequestContext;
676
677 /// The result returned by [`Guard::from_request`].
678 ///
679 /// Because `impl Trait` cannot be used inside traits (and named
680 /// existential types aren't stable), the type here might not be
681 /// nameable. In that case, you can set it to
682 /// [`DefaultFuture<Self, Error>`][`DefaultFuture`] and box the returned
683 /// future.
684 ///
685 /// If your guard doesn't need to return a future (eg. because it's just a
686 /// parsing step), you can set this to `Result<Self, BoxedError>` and
687 /// immediately return the result of the conversion.
688 ///
689 /// [`Guard::from_request`]: #tymethod.from_request
690 /// [`DefaultFuture`]: type.DefaultFuture.html
691 type Result: IntoFuture<Item = Self, Error = BoxedError>;
692
693 /// Create an instance of this type from HTTP request data, asynchronously.
694 ///
695 /// This can inspect HTTP headers and other data provided by
696 /// [`http::Request`], but can not access the body of the request. If access
697 /// to the body is needed, [`FromBody`] must be implemented instead.
698 ///
699 /// Implementations of this function must not block, since this function is
700 /// always run on a futures executor. If you need to perform blocking I/O or
701 /// long-running computations, you can call [`tokio_threadpool::blocking`].
702 ///
703 /// # Parameters
704 ///
705 /// * **`request`**: An HTTP request (without body) from the `http` crate.
706 /// * **`context`**: User-defined context needed by the guard.
707 ///
708 /// [`http::Request`]: ../http/request/struct.Request.html
709 /// [`FromBody`]: trait.FromBody.html
710 /// [`tokio_threadpool::blocking`]: ../tokio_threadpool/fn.blocking.html
711 fn from_request(request: &Arc<http::Request<()>>, context: &Self::Context) -> Self::Result;
712}
713
714/// Asynchronous conversion from an HTTP request body.
715///
716/// Types implementing this trait are provided in the [`body`] module. They
717/// allow easy deserialization from a variety of data formats.
718///
719/// # Examples
720///
721/// Collect the whole body and then deserialize it using a serde data format
722/// crate `serde_whatever`:
723///
724/// ```
725/// # use hyperdrive::{FromBody, NoContext, DefaultFuture, BoxedError};
726/// # use futures::prelude::*;
727/// # use serde_json as serde_whatever;
728/// # use std::sync::Arc;
729/// struct CustomFormat<T>(T);
730///
731/// impl<T: serde::de::DeserializeOwned + Send + 'static> FromBody for CustomFormat<T> {
732/// type Context = NoContext;
733/// type Result = DefaultFuture<Self, BoxedError>;
734///
735/// fn from_body(
736/// request: &Arc<http::Request<()>>,
737/// body: hyper::Body,
738/// context: &Self::Context,
739/// ) -> Self::Result {
740/// Box::new(body.concat2().map_err(Into::into).and_then(|body| {
741/// match serde_whatever::from_slice(&body) {
742/// Ok(t) => Ok(CustomFormat(t)),
743/// Err(e) => Err(e.into()),
744/// }
745/// }))
746/// }
747/// }
748/// ```
749///
750/// Process the body stream on-the-fly and calculate a checksum by adding all
751/// the bytes:
752///
753/// ```
754/// # use hyperdrive::{FromBody, NoContext, DefaultFuture, BoxedError};
755/// # use futures::prelude::*;
756/// # use std::sync::Arc;
757/// struct BodyChecksum(u8);
758///
759/// impl FromBody for BodyChecksum {
760/// type Context = NoContext;
761/// type Result = DefaultFuture<Self, BoxedError>;
762///
763/// fn from_body(
764/// request: &Arc<http::Request<()>>,
765/// body: hyper::Body,
766/// context: &Self::Context,
767/// ) -> Self::Result {
768/// Box::new(body
769/// .map_err(BoxedError::from)
770/// .fold(0, |checksum, chunk| -> Result<_, BoxedError> {
771/// Ok(chunk.as_ref().iter()
772/// .fold(checksum, |checksum: u8, byte| {
773/// checksum.wrapping_add(*byte)
774/// }))
775/// })
776/// .map(|checksum| BodyChecksum(checksum)) // wrap it up to create a `Self`
777/// )
778/// }
779/// }
780/// ```
781///
782/// [`body`]: body/index.html
783pub trait FromBody: Sized {
784 /// A context parameter passed to [`from_body`].
785 ///
786 /// This can be used to pass application-specific data like a logger or a
787 /// database connection around.
788 ///
789 /// If no context is needed, this should be set to [`NoContext`].
790 ///
791 /// [`from_body`]: #tymethod.from_body
792 /// [`NoContext`]: struct.NoContext.html
793 type Context: RequestContext;
794
795 /// The result returned by [`from_body`].
796 ///
797 /// Because `impl Trait` cannot be used inside traits (and named
798 /// existential types aren't stable), the type here might not be
799 /// nameable. In that case, you can set it to
800 /// [`DefaultFuture<Self, Error>`][`DefaultFuture`] and box the returned
801 /// future.
802 ///
803 /// If your `FromBody` implementation doesn't need to return a future, you
804 /// can set this to `Result<Self, BoxedError>` and immediately return the
805 /// result of the conversion.
806 ///
807 /// [`DefaultFuture`]: type.DefaultFuture.html
808 /// [`from_body`]: #tymethod.from_body
809 type Result: IntoFuture<Item = Self, Error = BoxedError>;
810
811 /// Create an instance of this type from an HTTP request body,
812 /// asynchronously.
813 ///
814 /// This will consume the body, so only one `FromBody` type can be used for
815 /// every processed request.
816 ///
817 /// Implementations of this function must not block, since this function is
818 /// always run on a futures executor. If you need to perform blocking I/O or
819 /// long-running computations, you can call [`tokio_threadpool::blocking`].
820 ///
821 /// **Note**: You probably want to limit the size of the body to prevent
822 /// denial of service attacks.
823 ///
824 /// # Parameters
825 ///
826 /// * **`request`**: An HTTP request (without body) from the `http` crate.
827 /// * **`body`**: The body stream. Implements `futures::Stream`.
828 /// * **`context`**: User-defined context.
829 ///
830 /// [`Guard`]: trait.Guard.html
831 /// [`tokio_threadpool::blocking`]: ../tokio_threadpool/fn.blocking.html
832 fn from_body(
833 request: &Arc<http::Request<()>>,
834 body: hyper::Body,
835 context: &Self::Context,
836 ) -> Self::Result;
837}
838
839/// A default [`RequestContext`] containing no data.
840///
841/// This context type should be used in [`FromRequest`], [`FromBody`] and
842/// [`Guard`] implementations whenever no application-specific context is
843/// needed. It can be created from any [`RequestContext`] via
844/// `AsRef<NoContext>`.
845///
846/// [`FromRequest`]: trait.FromRequest.html
847/// [`FromBody`]: trait.FromBody.html
848/// [`Guard`]: trait.Guard.html
849/// [`RequestContext`]: trait.RequestContext.html
850#[derive(Debug, Copy, Clone, Default)]
851pub struct NoContext;
852
853/// Trait for context types passed to [`FromRequest`], [`FromBody`] and
854/// [`Guard`].
855///
856/// # `#[derive(RequestContext)]`
857///
858/// This trait can be derived automatically. This will also implement
859/// `AsRef<Self>` and `AsRef<NoContext>`.
860///
861/// On structs, fields can also be annotated using `#[as_ref]`, which generates
862/// an additional implementation of `AsRef` for that field (note that all
863/// `#[as_ref]` fields must have distinct types). This will automatically use
864/// the field's type as a context when required by a `FromRequest` impl.
865///
866/// # Examples
867///
868/// Create your own context that allows running database queries in [`Guard`]s
869/// and elsewhere:
870/// ```
871/// # use hyperdrive::RequestContext;
872/// # struct ConnectionPool {}
873/// #[derive(RequestContext)]
874/// struct MyContext {
875/// db: ConnectionPool,
876/// }
877/// ```
878///
879/// Create a context that contains the above context and additional data:
880/// ```
881/// # use hyperdrive::RequestContext;
882/// # struct Logger {}
883/// # #[derive(RequestContext)]
884/// # struct MyContext {}
885/// #[derive(RequestContext)]
886/// struct BigContext {
887/// #[as_ref]
888/// inner: MyContext,
889/// logger: Logger,
890/// }
891/// ```
892/// This context can be used in the same places where `MyContext` is accepted,
893/// but provides additional data that may be used only by a few [`Guard`],
894/// [`FromRequest`] or [`FromBody`] implementations.
895///
896/// [`Guard`]: trait.Guard.html
897/// [`FromRequest`]: trait.FromRequest.html
898/// [`FromBody`]: trait.FromBody.html
899pub trait RequestContext: AsRef<Self> + AsRef<NoContext> {}
900
901impl RequestContext for NoContext {}
902
903impl AsRef<NoContext> for NoContext {
904 fn as_ref(&self) -> &Self {
905 &NoContext
906 }
907}
908
909/// Turns a blocking closure into an asynchronous `Future`.
910///
911/// This function takes a blocking closure that does synchronous I/O or heavy
912/// computations, and turns it into a well-behaved asynchronous `Future` by
913/// running it on a thread pool.
914///
915/// The returned future will have `'static` lifetime if the passed closure and
916/// the success and error types do.
917///
918/// This is a convenience wrapper around [`tokio_threadpool::blocking`].
919///
920/// [`tokio_threadpool::blocking`]: ../tokio_threadpool/fn.blocking.html
921pub fn blocking<F, T, E>(f: F) -> impl Future<Item = T, Error = E>
922where
923 F: FnOnce() -> Result<T, E>,
924{
925 // Needs a small `Option` dance because `poll_fn` takes an `FnMut`
926 let mut f = Some(f);
927
928 futures::future::poll_fn(move || {
929 tokio_threadpool::blocking(|| f.take().unwrap()()).map_err(|blocking_err| {
930 // `blocking` only returns errors in critical situations:
931 // - when the threadpool has already shut down
932 // - when tokio isn't running
933 // This wrapper just panics in that situation.
934 panic!(
935 "`tokio_threadpool::blocking` returned error: {}",
936 blocking_err
937 );
938 })
939 })
940 .and_then(|result| result)
941}