1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
//! A collection of useful HTTP middlewares. mod router; use futures::Future; use http; use {Body, Error, Handler, Responder, ResponseBuilder}; pub use self::router::SimpleRouter; /// Middleware which outputs details of HTTP requests/responses to stdout. /// /// This middleware wraps another HTTP handler, and logs details about each HTTP request (method and /// URI) and the associated response (status) to stdout. The logging only occurs when the /// [`Responder`] returned from the [`Handler`] resolved to a `http::Response`. /// /// This middleware is intended to help during development. It is expected that any production /// application creates its own version of this middleware, to integrate it into the application's /// logging infrastructure. /// /// [`Responder`]: ../trait.Responder.html [`Handler`]: ../trait.Handler.html [`http::Response`]: /// https://docs.rs/http/0.1.7/http/response/struct.Response.html /// /// # Example /// /// ```no_run /// extern crate aitch; /// extern crate http; /// /// use aitch::servers::hyper::Server; /// use aitch::{middlewares, Responder, ResponseBuilder, Result}; /// use http::Request; /// /// fn handler(_req: Request<()>, mut resp: ResponseBuilder) -> impl Responder { /// resp.body("Hello, world!".to_owned()) /// } /// /// fn main() -> Result<()> { /// let wrapped = middlewares::with_stdout_logging(handler); /// /// let addr = "127.0.0.1:3000".parse()?; /// println!("Listening on http://{}", addr); /// Server::new(addr, wrapped)?.run() /// } /// ``` /// /// Which outputs the following in response to requests: /// /// ```ignore /// Listening on http://127.0.0.1:3000" /// GET / 200 /// GET /path/doesnt/matter/in/this/example 200 /// ... /// ``` pub fn with_stdout_logging<B: Body>(handler: impl Handler<B>) -> impl Handler<B> { move |req: http::Request<B>, resp: ResponseBuilder| { let method = req.method().clone(); let uri = req.uri().clone(); handler.handle(req, resp).into_response().map(move |resp| { println!("{} {} {}", method, uri.path(), resp.status()); resp }) } } // TODO: Determine whether this pattern is useful, and consider exporting. pub(crate) fn with_error_handling<B, F, R>(handler: impl Handler<B>, func: F) -> impl Handler<B> where B: Body, F: Fn(Error) -> R + Clone + Send + Sync + 'static, R: Responder, { with_context(func, move |func, req, resp| { handler .handle(req, resp) .into_response() .or_else(move |err| func(err).into_response()) }) } /// A middleware which injects shared context into HTTP handlers. /// /// The `with_context` function is a convenience that makes writing handlers with shared state as /// easy as possible. In many HTTP applications, handlers need access to shared context/state, such /// as database connection pools, or configuration information. /// /// When using `with_context`, handler functions are written using the slightly different type /// signature, which accepts a context type as the first argument: /// /// ```ignore /// Fn(ContextType, http::Request<impl Body>, ResponseBuilder) -> impl Responder /// ``` /// /// They are then wrapped with `with_context(ctx, func)`, which returns a [`Handler`] which can be /// passed to middlewares/servers. /// /// The only constraints on the context type, are that it implements `Clone + Send + Sync`. In many /// applications, this type will be an [`Arc`], or something that uses [`Arc`] internally. /// /// [`Handler`]: ../trait.Handler.html /// [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html /// /// # Example /// /// ```no_run /// extern crate aitch; /// extern crate http; /// /// use std::sync::Arc; /// /// use aitch::servers::hyper::Server; /// use aitch::{middlewares, Responder, ResponseBuilder, Result}; /// use http::Request; /// /// struct Context { /// message: String, /// // In many applications, this context would also contain a database /// // connection pool, configuration data, and anything other state that /// // needs to be shared between handlers: /// // pool: DatabasePool, /// // config: Config, /// } /// /// fn handler(ctx: Arc<Context>, _req: Request<()>, mut resp: ResponseBuilder) -> impl Responder { /// resp.body(ctx.message.clone()) /// } /// /// fn main() -> Result<()> { /// let ctx = Arc::new(Context { /// message: "Hello from a world with context!".to_owned(), /// }); /// let handler = middlewares::with_context(ctx, handler); /// let wrapped = middlewares::with_stdout_logging(handler); /// /// let addr = "127.0.0.1:3000".parse()?; /// println!("Listening on http://{}", addr); /// Server::new(addr, wrapped)?.run() /// } /// ``` pub fn with_context<Ctx, Func, ReqBody, Resp>(ctx: Ctx, handler: Func) -> impl Handler<ReqBody> where Ctx: Clone + Send + Sync + 'static, Func: Fn(Ctx, http::Request<ReqBody>, ResponseBuilder) -> Resp + Send + Sync + 'static, ReqBody: Body, Resp: Responder, { move |req, resp| handler(ctx.clone(), req, resp) }