From 81bc251b74f87924eb9f356af06536f139088012 Mon Sep 17 00:00:00 2001 From: Jacob Rothstein Date: Sun, 24 May 2020 14:21:21 -0700 Subject: [PATCH 1/3] allow for function middlewares --- examples/middleware.rs | 23 +++++++++++++++++++++++ src/middleware.rs | 6 +++++- src/server.rs | 6 ++---- 3 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 examples/middleware.rs diff --git a/examples/middleware.rs b/examples/middleware.rs new file mode 100644 index 000000000..ae0a8ab17 --- /dev/null +++ b/examples/middleware.rs @@ -0,0 +1,23 @@ +fn middleware<'a, State: Send + Sync + 'static>( + request: tide::Request, + next: tide::Next<'a, State>, +) -> std::pin::Pin + Send + 'a>> { + Box::pin(async { + tide::log::info!("before"); + let result = next.run(request).await; + tide::log::info!("after"); + result + }) +} + +#[async_std::main] +async fn main() -> tide::Result<()> { + tide::log::start(); + let mut app = tide::new(); + + app.middleware(middleware); + + app.at("/").get(|_| async move { Ok("Hello, world!") }); + app.listen("127.0.0.1:8080").await?; + Ok(()) +} diff --git a/src/middleware.rs b/src/middleware.rs index 19424f01f..ac9973a74 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -17,9 +17,13 @@ pub trait Middleware: 'static + Send + Sync { /// Asynchronously handle the request, and return a response. fn handle<'a>( &'a self, - cx: Request, + request: Request, next: Next<'a, State>, ) -> BoxFuture<'a, crate::Result>; + + fn name(&self) -> &str { + std::any::type_name::() + } } impl Middleware for F diff --git a/src/server.rs b/src/server.rs index f83e65ee9..de20535b2 100644 --- a/src/server.rs +++ b/src/server.rs @@ -6,8 +6,6 @@ use async_std::prelude::*; use async_std::sync::Arc; use async_std::task; -use std::fmt::Debug; - use crate::cookies; use crate::log; use crate::middleware::{Middleware, Next}; @@ -269,9 +267,9 @@ impl Server { /// and is processed in the order in which it is applied. pub fn middleware(&mut self, middleware: M) -> &mut Self where - M: Middleware + Debug, + M: Middleware, { - log::trace!("Adding middleware {:?}", middleware); + log::trace!("Adding middleware {}", middleware.name()); let m = Arc::get_mut(&mut self.middleware) .expect("Registering middleware is not possible after the Server has started"); m.push(Arc::new(middleware)); From 2af009343a865eddc1816e2130d432de9bb600d1 Mon Sep 17 00:00:00 2001 From: Jacob Rothstein Date: Mon, 25 May 2020 20:58:16 -0700 Subject: [PATCH 2/3] include two different approaches in the middleware example --- examples/middleware.rs | 102 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 12 deletions(-) diff --git a/examples/middleware.rs b/examples/middleware.rs index ae0a8ab17..a2ae65bc6 100644 --- a/examples/middleware.rs +++ b/examples/middleware.rs @@ -1,23 +1,101 @@ -fn middleware<'a, State: Send + Sync + 'static>( - request: tide::Request, - next: tide::Next<'a, State>, -) -> std::pin::Pin + Send + 'a>> { +use std::future::Future; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; +use tide::{Middleware, Next, Request, Response, Result, StatusCode}; + +#[derive(Debug)] +struct User { + name: String, +} + +#[derive(Default)] +struct UserDatabase; +impl UserDatabase { + async fn find_user(&self) -> Option { + Some(User { + name: "nori".into(), + }) + } +} + +// This is an example of a function middleware that uses the +// application state. Because it depends on a specific request state, +// it would likely be closely tied to a specific application +fn user_loader<'a>( + mut request: Request, + next: Next<'a, UserDatabase>, +) -> Pin + Send + 'a>> { Box::pin(async { - tide::log::info!("before"); - let result = next.run(request).await; - tide::log::info!("after"); - result + if let Some(user) = request.state().find_user().await { + tide::log::trace!("user loaded", {user: user.name}); + request.set_ext(user); + next.run(request).await + // this middleware only needs to run before the endpoint, so + // it just passes through the result of Next + } else { + // do not run endpoints, we could not find a user + Ok(Response::new(StatusCode::Unauthorized)) + } }) } +// +// +// this is an example of middleware that keeps its own state and could +// be provided as a third party crate +#[derive(Default)] +struct RequestCounterMiddleware { + requests_counted: Arc, +} + +impl RequestCounterMiddleware { + fn new(start: usize) -> Self { + Self { + requests_counted: Arc::new(AtomicUsize::new(start)), + } + } +} + +struct RequestCount(usize); + +impl Middleware for RequestCounterMiddleware { + fn handle<'a>( + &'a self, + mut req: Request, + next: Next<'a, State>, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let count = self.requests_counted.fetch_add(1, Ordering::Relaxed); + tide::log::trace!("request counter", { count: count }); + req.set_ext(RequestCount(count)); + + let mut response = next.run(req).await?; + + response = response.set_header("request-number", count.to_string()); + Ok(response) + }) + } +} + #[async_std::main] -async fn main() -> tide::Result<()> { +async fn main() -> Result<()> { tide::log::start(); - let mut app = tide::new(); + let mut app = tide::with_state(UserDatabase::default()); + + app.middleware(user_loader); + app.middleware(RequestCounterMiddleware::new(0)); + + app.at("/").get(|req: Request<_>| async move { + let count: &RequestCount = req.ext().unwrap(); + let user: &User = req.ext().unwrap(); - app.middleware(middleware); + Ok(format!( + "Hello {}, this was request number {}!", + user.name, count.0 + )) + }); - app.at("/").get(|_| async move { Ok("Hello, world!") }); app.listen("127.0.0.1:8080").await?; Ok(()) } From 8ab1f894609ae896ad4e046484148b0ed726507f Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 28 May 2020 21:25:56 +0200 Subject: [PATCH 3/3] Document Middleware::name --- src/middleware.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/middleware.rs b/src/middleware.rs index ac9973a74..573f005ea 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -21,6 +21,7 @@ pub trait Middleware: 'static + Send + Sync { next: Next<'a, State>, ) -> BoxFuture<'a, crate::Result>; + /// Set the middleware's name. By default it uses the type signature. fn name(&self) -> &str { std::any::type_name::() }