luminal_handler/
lib.rs

1//! This create wraps `hyper::server::Service` with a slightly more convenient interface.
2//!
3//! The request is wrapped with a new enum that helps provide additional information along with the
4//! request and uses a more liberal error type to allow users to map their own error to an actual
5//! http response.
6extern crate futures;
7extern crate http;
8extern crate hyper;
9
10use futures::future::{self, Future};
11use hyper::Body;
12use hyper::server::{Request, Response, Service};
13
14// A convenience alias.
15pub type LuminalFuture = Box<Future<Item = Response, Error = hyper::Error>>;
16
17/// Trait for handling a request, returning either a success `Response` or an error `Response`.
18pub trait Handler {
19    fn handle(&self, req: http::Request<Body>) -> Result<LuminalFuture, Response>;
20}
21
22/// An impl of `hyper::Service` that consumes an impl of `Handler`.
23pub struct HandlerService<H: Handler> {
24    handler: H,
25}
26
27impl<H: Handler> HandlerService<H> {
28    pub fn new(handler: H) -> Self {
29        HandlerService { handler }
30    }
31}
32
33impl<H: Handler> Service for HandlerService<H> {
34    type Request = Request;
35    type Response = Response;
36    type Error = hyper::Error;
37    type Future = LuminalFuture;
38
39    /// Dispatches to the owned `Handler`, marshalling success or error into the response.
40    fn call(&self, request: Request) -> Self::Future {
41        match self.handler.handle(request.into()) {
42            Ok(response) => response,
43            Err(error) => Box::new(future::ok(error)),
44        }
45    }
46}
47
48/// Accepts a function or closure that takes an `HttpRequest` and returns a compatible `Result`.
49pub fn handler_fn<F>(func: F) -> HandlerFn<F>
50where
51    F: Fn(http::Request<Body>) -> Result<LuminalFuture, Response>,
52{
53    HandlerFn { func }
54}
55
56/// Holds a function to dispatch to via its impl of `Handler<E>`.
57pub struct HandlerFn<F>
58where
59    F: Fn(http::Request<Body>) -> Result<LuminalFuture, Response>,
60{
61    func: F,
62}
63
64impl<F> Handler for HandlerFn<F>
65where
66    F: Fn(http::Request<Body>) -> Result<LuminalFuture, Response>,
67{
68    fn handle(&self, req: http::Request<Body>) -> Result<LuminalFuture, Response> {
69        (self.func)(req)
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    extern crate tokio_core;
76
77    use futures::Stream;
78    use hyper::Method;
79    use hyper::{Body, StatusCode};
80
81    use self::tokio_core::reactor::Core;
82
83    use super::*;
84
85    enum TestHandler {
86        Success(String),
87        Failure(String),
88    }
89
90    impl Handler for TestHandler {
91        fn handle(&self, _request: http::Request<Body>) -> Result<LuminalFuture, Response> {
92            match *self {
93                TestHandler::Success(ref body) => {
94                    let body: String = body.clone();
95                    Ok(Box::new(futures::future::ok(
96                        Response::new().with_status(StatusCode::Ok).with_body(body),
97                    )))
98                }
99                TestHandler::Failure(ref error) => {
100                    let body: String = error.clone();
101                    Err(Response::new()
102                        .with_status(StatusCode::InternalServerError)
103                        .with_body(body))
104                }
105            }
106        }
107    }
108
109    fn test_fn(_req: http::Request<Body>) -> Result<LuminalFuture, Response> {
110        Ok(Box::new(futures::future::ok(
111            Response::new()
112                .with_status(StatusCode::Ok)
113                .with_body(String::from("test")),
114        )))
115    }
116
117    #[test]
118    fn test_success() {
119        let handler = TestHandler::Success(String::from("Success"));
120        let service = HandlerService::new(handler);
121
122        assert_call(&service, Method::Get, "/foo", (&StatusCode::Ok, "Success"));
123    }
124
125    #[test]
126    fn test_failure() {
127        let handler = TestHandler::Failure(String::from("Error"));
128        let service = HandlerService::new(handler);
129
130        assert_call(
131            &service,
132            Method::Get,
133            "/foo",
134            (&StatusCode::InternalServerError, "Error"),
135        );
136    }
137
138    #[test]
139    fn test_handler_fn() {
140        let handler = handler_fn(test_fn);
141        let service = HandlerService::new(handler);
142
143        assert_call(&service, Method::Get, "/foo", (&StatusCode::Ok, "test"));
144    }
145
146    fn assert_call<H>(
147        service: &HandlerService<H>,
148        method: Method,
149        uri: &str,
150        expected: (&StatusCode, &str),
151    ) where
152        H: Handler,
153    {
154        let uri = uri.parse()
155            .expect("Should have been able to convert to uri");
156        let req: Request<Body> = Request::new(method, uri);
157
158        let work = service.call(req);
159
160        let mut core = Core::new().expect("Should have been able to create core");
161
162        let response = core.run(work)
163            .expect("Should have been able to run router call");
164
165        assert_eq!(
166            *expected.0,
167            response.status(),
168            "Should have received {} status.",
169            expected.0
170        );
171
172        let body = core.run(response.body().concat2())
173            .expect("Should have been able to resolve body concat");
174        let body: &[u8] = &body.to_vec();
175
176        assert_eq!(
177            expected.1.as_bytes(),
178            body,
179            "Should have received correct body content"
180        );
181    }
182}