hyper_routing/
lib.rs

1#![doc(html_root_url = "https://tsharp.github.io/hyper-routing/doc/hyper_routing")]
2
3//! # Hyper Routing
4//!
5//! This cargo is a small extension to the great Hyper HTTP library. It basically is
6//! adds the ability to define routes to request handlers and then query for the handlers
7//! by request path.
8//!
9//! ## Usage
10//!
11//! !!! CODE REMOVED TO FIX TESTS !!!
12//!
13//! This code will start Hyper server and add use router to find handlers for request.
14//! We create the `Route` so that when we visit path `/greet` the `basic_handler` handler
15//! will be called.
16//!
17//! ## Things to note
18//!
19//! * `Path::new` method accepts regular expressions so you can match every path you please.
20//! * If you have request matching multiple paths the one that was first `add`ed will be chosen.
21//! * This library is in an early stage of development so there may be breaking changes comming
22//! (but I'll try as hard as I can not to break backwards compatibility or break it just a little -
23//! I promise I'll try!).
24//!
25//! # Waiting for your feedback
26//!
27//! I've created this little tool to help myself learn Rust and to avoid using big frameworks
28//! like Iron or rustful. I just want to keep things simple.
29//!
30//! Obviously I could make some errors or bad design choices so I'm waiting for your feedback!
31//! You may create an issue at [project's bug tracker](https://github.com/tsharp/hyper-routing/issues).
32
33extern crate futures;
34extern crate hyper;
35
36use std::task::{Context, Poll};
37use futures::future;
38use hyper::header::CONTENT_LENGTH;
39use hyper::service::Service;
40use hyper::{Body, Request, Response};
41
42use hyper::Method;
43use hyper::StatusCode;
44
45mod builder;
46pub mod handlers;
47mod path;
48pub mod route;
49
50pub use self::builder::RouterBuilder;
51pub use self::path::Path;
52pub use self::route::Route;
53pub use self::route::RouteBuilder;
54
55pub type Handler = fn(Request<Body>) -> Response<Body>;
56pub type HttpResult<T> = Result<T, StatusCode>;
57
58/// This is the one. The router.
59#[derive(Debug)]
60pub struct Router {
61    routes: Vec<Route>,
62}
63
64impl Router {
65    /// Finds handler for given Hyper request.
66    ///
67    /// This method uses default error handlers.
68    /// If the request does not match any route than default 404 handler is returned.
69    /// If the request match some routes but http method does not match (used GET but routes are
70    /// defined for POST) than default method not supported handler is returned.
71    pub fn find_handler_with_defaults(&self, request: &Request<Body>) -> Handler {
72        let matching_routes = self.find_matching_routes(request.uri().path());
73        match matching_routes.len() {
74            x if x == 0 => handlers::default_404_handler,
75            _ => self
76                .find_for_method(&matching_routes, request.method())
77                .unwrap_or(handlers::method_not_supported_handler),
78        }
79    }
80
81    /// Finds handler for given Hyper request.
82    ///
83    /// It returns handler if it's found or `StatusCode` for error.
84    /// This method may return `NotFound`, `MethodNotAllowed` or `NotImplemented`
85    /// status codes.
86    pub fn find_handler(&self, request: &Request<Body>) -> HttpResult<Handler> {
87        let matching_routes = self.find_matching_routes(request.uri().path());
88        match matching_routes.len() {
89            x if x == 0 => Err(StatusCode::NOT_FOUND),
90            _ => self
91                .find_for_method(&matching_routes, request.method())
92                .map(Ok)
93                .unwrap_or(Err(StatusCode::METHOD_NOT_ALLOWED)),
94        }
95    }
96
97    /// Returns vector of `Route`s that match to given path.
98    pub fn find_matching_routes(&self, request_path: &str) -> Vec<&Route> {
99        self.routes
100            .iter()
101            .filter(|route| route.path.matcher.is_match(&request_path))
102            .collect()
103    }
104
105    fn find_for_method(&self, routes: &[&Route], method: &Method) -> Option<Handler> {
106        let method = method.clone();
107        routes
108            .iter()
109            .find(|route| route.method == method)
110            .map(|route| route.handler)
111    }
112}
113
114/// The default simple router service.
115#[derive(Debug)]
116pub struct RouterService {
117    pub router: Router,
118    pub error_handler: fn(StatusCode) -> Response<Body>,
119}
120
121impl RouterService {
122    pub fn new(router: Router) -> RouterService {
123        RouterService {
124            router,
125            error_handler: Self::default_error_handler,
126        }
127    }
128
129    fn default_error_handler(status_code: StatusCode) -> Response<Body> {
130        let error = "Routing error: page not found";
131        Response::builder()
132            .header(CONTENT_LENGTH, error.len() as u64)
133            .status(match status_code {
134                StatusCode::NOT_FOUND => StatusCode::NOT_FOUND,
135                _ => StatusCode::INTERNAL_SERVER_ERROR,
136            })
137            .body(Body::from(error))
138            .expect("Failed to construct a response")
139    }
140}
141
142impl Service<Request<Body>> for RouterService {
143    type Response = Response<Body>;
144    type Error = hyper::Error;
145    type Future = future::Ready<Result<Self::Response, Self::Error>>;
146
147    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
148        Ok(()).into()
149    }
150
151    fn call(&mut self, request: Request<Body>) -> Self::Future {
152        futures::future::ok(match self.router.find_handler(&request) {
153            Ok(handler) => handler(request),
154            Err(status_code) => (self.error_handler)(status_code),
155        })
156    }
157}