Crate hyperdrive

Source
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§

pub use futures;
pub use http;
pub use hyper;
pub use serde;

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§

FromBody
Asynchronous conversion from an HTTP request body.
FromRequest
Trait for asynchronous conversion from HTTP requests.
Guard
A request guard that checks a condition or extracts data out of an incoming request.
RequestContext
Trait for context types passed to FromRequest, FromBody and Guard.

Functions§

blocking
Turns a blocking closure into an asynchronous Future.

Type Aliases§

BoxedError
A boxed std::error::Error that can be used when the actual error type is unknown.
DefaultFuture
A default boxed future that may be returned from FromRequest, FromBody and Guard implementations.

Derive Macros§

FromRequest
RequestContext