[][src]Crate hyperdrive

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 this library.

NoContext

A default RequestContext containing no data.

Enums

ErrorKind

The different kinds of errors that can occur when using this library.

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.

Type Definitions

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.