Expand description
Composable (a)synchronous HTTP request routing, guarding and decoding.
This crate provides Rocket-inspired HTTP route definitions based on
attributes (#[get("/user/{id}")]
). It is based on the hyper
and http
crates, works on stable Rust, and supports writing both
synchronous and asynchronous (via futures 0.1) apps.
Check out the examples below for a small taste of how this library can be
used. If you want to dive in deeper, you can check out the FromRequest
trait, which provides the custom derive that powers most of the magic in
this crate.
§Examples
Use the hyper service adapter AsyncService
to create your async
server without much boilerplate:
use hyper::{Server, Response, Body};
use hyperdrive::{service::AsyncService, FromRequest};
use futures::IntoFuture;
#[derive(FromRequest)]
enum Route {
#[get("/")]
Index,
#[get("/users/{id}")]
UserInfo { id: u32 },
}
let srv = Server::bind(&"127.0.0.1:0".parse().unwrap())
.serve(AsyncService::new(|route: Route, _| {
match route {
Route::Index => {
Ok(Response::new(Body::from("Hello World!"))).into_future()
}
Route::UserInfo { id } => {
// You could do an async database query to fetch the user data here
Ok(Response::new(Body::from(format!("User #{}", id)))).into_future()
}
}
}));
If your app doesn’t need to be asynchronous and you’d prefer to write sync
code, you can do that by using SyncService
:
use hyper::{Server, Response, Body};
use hyperdrive::{service::SyncService, FromRequest};
#[derive(FromRequest)]
enum Route {
#[get("/")]
Index,
#[get("/users/{id}")]
UserInfo { id: u32 },
}
let srv = Server::bind(&"127.0.0.1:0".parse().unwrap())
.serve(SyncService::new(|route: Route, _| {
// This closure can block freely, and has to return a `Response<Body>`
match route {
Route::Index => {
Response::new(Body::from("Hello World!"))
},
Route::UserInfo { id } => {
Response::new(Body::from(format!("User #{}", id)))
}
}
}));
If the provided service adapters aren’t sufficient for your use case, you
can always manually use the FromRequest
methods, and hook it up to your
hyper Service
manually:
use hyper::{Request, Response, Body, Method, service::Service};
use futures::Future;
use hyperdrive::{FromRequest, DefaultFuture, BoxedError, NoContext};
#[derive(FromRequest)]
enum Route {
#[get("/")]
Index,
#[get("/users/{id}")]
UserInfo { id: u32 },
}
// Define your hyper `Service`:
struct MyService;
impl Service for MyService {
type ReqBody = Body;
type ResBody = Body;
type Error = BoxedError;
type Future = DefaultFuture<Response<Body>, BoxedError>;
fn call(&mut self, req: Request<Body>) -> Self::Future {
let is_head = req.method() == Method::HEAD;
let future = Route::from_request(req, NoContext).and_then(|route| Ok(match route {
Route::Index => {
Response::new(Body::from("Hello world!"))
}
Route::UserInfo { id } => {
Response::new(Body::from(format!("User #{} is secret!", id)))
}
})).map(move |resp| {
if is_head {
// Response to HEAD requests must have an empty body
resp.map(|_| Body::empty())
} else {
resp
}
});
Box::new(future)
}
}
For detailed documentation on the custom derive syntax, refer to the docs of
FromRequest
.
Re-exports§
Modules§
- body
- Provides wrappers that deserialize a request body.
- service
- Implements hyper
Service
adapters that reduce boilerplate.
Structs§
- Error
- The error type used by the Hyperdrive library.
- NoContext
- A default
RequestContext
containing no data.
Traits§
- From
Body - Asynchronous conversion from an HTTP request body.
- From
Request - Trait for asynchronous conversion from HTTP requests.
- Guard
- A request guard that checks a condition or extracts data out of an incoming request.
- Request
Context - Trait for context types passed to
FromRequest
,FromBody
andGuard
.
Functions§
- blocking
- Turns a blocking closure into an asynchronous
Future
.
Type Aliases§
- Boxed
Error - A boxed
std::error::Error
that can be used when the actual error type is unknown. - Default
Future - A default boxed future that may be returned from
FromRequest
,FromBody
andGuard
implementations.