From 43a0c7f2efb750511975f1c4835cf16f97a772b5 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Sun, 10 Jul 2022 14:12:19 -0500 Subject: [PATCH 01/20] Update response extensions to match request methods --- src/response.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/response.rs b/src/response.rs index 044b9f22..c375e302 100644 --- a/src/response.rs +++ b/src/response.rs @@ -422,9 +422,15 @@ impl Response { self.res.ext().get() } - /// Set a response scoped extension value. - pub fn insert_ext(&mut self, val: T) { - self.res.ext_mut().insert(val); + /// Get a mutable reference to value stored in response extensions. + #[must_use] + pub fn ext_mut(&mut self) -> Option<&mut T> { + self.res.ext_mut().get_mut() + } + + /// Set a response extension value. + pub fn set_ext(&mut self, val: T) -> Option { + self.res.ext_mut().insert(val) } /// Create a `tide::Response` from a type that can be converted into an From dd98a40b527cefdc2a7484668f193d1ff9ea4907 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Thu, 14 Jul 2022 20:17:36 -0500 Subject: [PATCH 02/20] Move Request generics to ext and trait --- examples/catflap.rs | 2 +- examples/chunked.rs | 2 +- examples/concurrent_listeners.rs | 4 +- examples/cookies.rs | 8 +-- examples/error_handling.rs | 4 +- examples/fib.rs | 2 +- examples/graphql.rs | 12 +++- examples/hello.rs | 2 +- examples/json.rs | 4 +- examples/middleware.rs | 18 ++++-- examples/nested.rs | 2 +- examples/redirect.rs | 2 +- examples/sessions.rs | 15 +++-- examples/sse.rs | 2 +- examples/state.rs | 14 ++++- examples/static_file.rs | 2 +- examples/upload.rs | 17 ++++-- src/cookies/middleware.rs | 6 +- src/endpoint.rs | 31 +++++------ src/fs/serve_dir.rs | 11 ++-- src/fs/serve_file.rs | 8 +-- src/lib.rs | 15 +++-- src/listener/concurrent_listener.rs | 2 +- src/listener/failover_listener.rs | 2 +- src/log/middleware.rs | 4 +- src/log/mod.rs | 2 +- src/middleware.rs | 10 ++-- src/redirect.rs | 7 +-- src/request.rs | 85 ++++++++++++++--------------- src/route.rs | 46 +++++++--------- src/router.rs | 33 ++++------- src/security/cors.rs | 2 +- src/server.rs | 26 ++++++--- src/sessions/middleware.rs | 8 +-- src/sse/endpoint.rs | 8 +-- src/sse/upgrade.rs | 4 +- src/utils.rs | 10 ++-- tests/cookies.rs | 8 +-- tests/endpoint.rs | 2 +- tests/function_middleware.rs | 4 +- tests/nested.rs | 9 ++- tests/params.rs | 4 +- tests/route_middleware.rs | 4 +- tests/serve_dir.rs | 2 +- tests/server.rs | 4 +- tests/sessions.rs | 21 ++++--- tests/unix.rs | 2 +- tests/wildcard.rs | 8 +-- 48 files changed, 262 insertions(+), 238 deletions(-) diff --git a/examples/catflap.rs b/examples/catflap.rs index c3be1e67..486d565a 100644 --- a/examples/catflap.rs +++ b/examples/catflap.rs @@ -2,7 +2,7 @@ #[async_std::main] async fn main() -> Result<(), std::io::Error> { use std::{env, net::TcpListener, os::unix::io::FromRawFd}; - tide::log::start(); + // tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); app.at("/").get(|_| async { Ok(CHANGE_THIS_TEXT) }); diff --git a/examples/chunked.rs b/examples/chunked.rs index 6ea80539..b3250951 100644 --- a/examples/chunked.rs +++ b/examples/chunked.rs @@ -2,7 +2,7 @@ use tide::Body; #[async_std::main] async fn main() -> Result<(), std::io::Error> { - tide::log::start(); + // tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); app.at("/").get(|_| async { diff --git a/examples/concurrent_listeners.rs b/examples/concurrent_listeners.rs index 10d2ec64..1ee93af3 100644 --- a/examples/concurrent_listeners.rs +++ b/examples/concurrent_listeners.rs @@ -2,11 +2,11 @@ use tide::Request; #[async_std::main] async fn main() -> Result<(), std::io::Error> { - tide::log::start(); + // tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); - app.at("/").get(|request: Request<_>| async move { + app.at("/").get(|request: Request| async move { Ok(format!( "Hi! You reached this app through: {}", request.local_addr().unwrap_or("an unknown port") diff --git a/examples/cookies.rs b/examples/cookies.rs index 68f3a611..975465d7 100644 --- a/examples/cookies.rs +++ b/examples/cookies.rs @@ -3,17 +3,17 @@ use tide::{Request, Response, StatusCode}; /// Tide will use the the `Cookies`'s `Extract` implementation to build this parameter. /// -async fn retrieve_cookie(req: Request<()>) -> tide::Result { +async fn retrieve_cookie(req: Request) -> tide::Result { Ok(format!("hello cookies: {:?}", req.cookie("hello").unwrap())) } -async fn insert_cookie(_req: Request<()>) -> tide::Result { +async fn insert_cookie(_req: Request) -> tide::Result { let mut res = Response::new(StatusCode::Ok); res.insert_cookie(Cookie::new("hello", "world")); Ok(res) } -async fn remove_cookie(_req: Request<()>) -> tide::Result { +async fn remove_cookie(_req: Request) -> tide::Result { let mut res = Response::new(StatusCode::Ok); res.remove_cookie(Cookie::named("hello")); Ok(res) @@ -21,7 +21,7 @@ async fn remove_cookie(_req: Request<()>) -> tide::Result { #[async_std::main] async fn main() -> Result<(), std::io::Error> { - tide::log::start(); + // tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); diff --git a/examples/error_handling.rs b/examples/error_handling.rs index 96aa79eb..90b47eb4 100644 --- a/examples/error_handling.rs +++ b/examples/error_handling.rs @@ -5,7 +5,7 @@ use tide::{Body, Request, Response, Result, StatusCode}; #[async_std::main] async fn main() -> Result<()> { - tide::log::start(); + // tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); @@ -23,7 +23,7 @@ async fn main() -> Result<()> { })); app.at("/") - .get(|_req: Request<_>| async { Ok(Body::from_file("./does-not-exist").await?) }); + .get(|_req: Request| async { Ok(Body::from_file("./does-not-exist").await?) }); app.listen("127.0.0.1:8080").await?; diff --git a/examples/fib.rs b/examples/fib.rs index 2b0451ec..bde8bb7d 100644 --- a/examples/fib.rs +++ b/examples/fib.rs @@ -8,7 +8,7 @@ fn fib(n: usize) -> usize { } } -async fn fibsum(req: Request<()>) -> tide::Result { +async fn fibsum(req: Request) -> tide::Result { use std::time::Instant; let n: usize = req.param("n")?.parse().unwrap_or(0); // Start a stopwatch diff --git a/examples/graphql.rs b/examples/graphql.rs index c771afb4..eee046ce 100644 --- a/examples/graphql.rs +++ b/examples/graphql.rs @@ -2,7 +2,7 @@ use std::sync::{Arc, RwLock}; use juniper::{http::graphiql, http::GraphQLRequest, RootNode}; use lazy_static::lazy_static; -use tide::{http::mime, Body, Redirect, Request, Response, Server, StatusCode}; +use tide::{http::mime, Body, Redirect, Request, RequestState, Response, Server, StatusCode}; #[derive(Clone)] struct User { @@ -74,7 +74,7 @@ lazy_static! { static ref SCHEMA: Schema = Schema::new(QueryRoot {}, MutationRoot {}); } -async fn handle_graphql(mut request: Request) -> tide::Result { +async fn handle_graphql(mut request: Request) -> tide::Result { let query: GraphQLRequest = request.body_json().await?; let response = query.execute(&SCHEMA, request.state()); let status = if response.is_ok() { @@ -88,7 +88,7 @@ async fn handle_graphql(mut request: Request) -> tide::Result { .build()) } -async fn handle_graphiql(_: Request) -> tide::Result> { +async fn handle_graphiql(_: Request) -> tide::Result> { Ok(Response::builder(200) .body(graphiql::graphiql_source("/graphql")) .content_type(mime::HTML)) @@ -105,3 +105,9 @@ async fn main() -> std::io::Result<()> { app.listen("0.0.0.0:8080").await?; Ok(()) } + +impl RequestState for Request { + fn state(&self) -> &State { + self.ext::().unwrap() + } +} diff --git a/examples/hello.rs b/examples/hello.rs index d6647531..232bfb37 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -1,6 +1,6 @@ #[async_std::main] async fn main() -> Result<(), std::io::Error> { - tide::log::start(); + // tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); diff --git a/examples/json.rs b/examples/json.rs index 8bfb123e..0b077a2e 100644 --- a/examples/json.rs +++ b/examples/json.rs @@ -9,11 +9,11 @@ struct Cat { #[async_std::main] async fn main() -> tide::Result<()> { - tide::log::start(); + // tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); - app.at("/submit").post(|mut req: Request<()>| async move { + app.at("/submit").post(|mut req: Request| async move { let cat: Cat = req.body_json().await?; println!("cat name: {}", cat.name); diff --git a/examples/middleware.rs b/examples/middleware.rs index a7488a59..a64292df 100644 --- a/examples/middleware.rs +++ b/examples/middleware.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use tide::http::mime; use tide::utils::{After, Before}; -use tide::{Middleware, Next, Request, Response, Result, StatusCode}; +use tide::{Middleware, Next, Request, RequestState, Response, Result, StatusCode}; #[derive(Debug)] struct User { @@ -26,7 +26,7 @@ impl UserDatabase { // 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, + mut request: Request, next: Next<'a, UserDatabase>, ) -> Pin + Send + 'a>> { Box::pin(async { @@ -62,7 +62,7 @@ struct RequestCount(usize); #[tide::utils::async_trait] impl Middleware for RequestCounterMiddleware { - async fn handle(&self, mut req: Request, next: Next<'_, State>) -> Result { + async fn handle(&self, mut req: Request, next: Next<'_, State>) -> Result { let count = self.requests_counted.fetch_add(1, Ordering::Relaxed); tide::log::trace!("request counter", { count: count }); req.set_ext(RequestCount(count)); @@ -91,7 +91,7 @@ const INTERNAL_SERVER_ERROR_HTML_PAGE: &str = " #[async_std::main] async fn main() -> Result<()> { - tide::log::start(); + // tide::log::start(); let mut app = tide::with_state(UserDatabase::default()); app.with(After(|response: Response| async move { @@ -114,12 +114,12 @@ async fn main() -> Result<()> { app.with(user_loader); app.with(RequestCounterMiddleware::new(0)); - app.with(Before(|mut request: Request| async move { + app.with(Before(|mut request: Request| async move { request.set_ext(std::time::Instant::now()); request })); - app.at("/").get(|req: Request<_>| async move { + app.at("/").get(|req: Request| async move { let count: &RequestCount = req.ext().unwrap(); let user: &User = req.ext().unwrap(); @@ -132,3 +132,9 @@ async fn main() -> Result<()> { app.listen("127.0.0.1:8080").await?; Ok(()) } + +impl RequestState for Request { + fn state(&self) -> &UserDatabase { + self.ext::().unwrap() + } +} diff --git a/examples/nested.rs b/examples/nested.rs index b9eece73..4b1b027b 100644 --- a/examples/nested.rs +++ b/examples/nested.rs @@ -1,6 +1,6 @@ #[async_std::main] async fn main() -> Result<(), std::io::Error> { - tide::log::start(); + // tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); app.at("/").get(|_| async { Ok("Root") }); diff --git a/examples/redirect.rs b/examples/redirect.rs index c0ab4c64..50b461c9 100644 --- a/examples/redirect.rs +++ b/examples/redirect.rs @@ -2,7 +2,7 @@ use tide::{Redirect, Response, StatusCode}; #[async_std::main] async fn main() -> Result<(), std::io::Error> { - tide::log::start(); + // tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); app.at("/").get(|_| async { Ok("Root") }); diff --git a/examples/sessions.rs b/examples/sessions.rs index 5a2cf899..196b41df 100644 --- a/examples/sessions.rs +++ b/examples/sessions.rs @@ -1,6 +1,6 @@ #[async_std::main] async fn main() -> Result<(), std::io::Error> { - tide::log::start(); + // tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); @@ -15,7 +15,7 @@ async fn main() -> Result<(), std::io::Error> { )); app.with(tide::utils::Before( - |mut request: tide::Request<()>| async move { + |mut request: tide::Request| async move { let session = request.session_mut(); let visits: usize = session.get("visits").unwrap_or_default(); session.insert("visits", visits + 1).unwrap(); @@ -23,16 +23,15 @@ async fn main() -> Result<(), std::io::Error> { }, )); - app.at("/").get(|req: tide::Request<()>| async move { + app.at("/").get(|req: tide::Request| async move { let visits: usize = req.session().get("visits").unwrap(); Ok(format!("you have visited this website {} times", visits)) }); - app.at("/reset") - .get(|mut req: tide::Request<()>| async move { - req.session_mut().destroy(); - Ok(tide::Redirect::new("/")) - }); + app.at("/reset").get(|mut req: tide::Request| async move { + req.session_mut().destroy(); + Ok(tide::Redirect::new("/")) + }); app.listen("127.0.0.1:8080").await?; diff --git a/examples/sse.rs b/examples/sse.rs index 2f7d9fd7..90fb5a70 100644 --- a/examples/sse.rs +++ b/examples/sse.rs @@ -2,7 +2,7 @@ use tide::sse; #[async_std::main] async fn main() -> Result<(), std::io::Error> { - tide::log::start(); + // tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); app.at("/sse").get(sse::endpoint(|_req, sender| async move { diff --git a/examples/state.rs b/examples/state.rs index a687ca2f..95fc0b53 100644 --- a/examples/state.rs +++ b/examples/state.rs @@ -1,6 +1,8 @@ use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; +use tide::RequestState; + #[derive(Clone)] struct State { value: Arc, @@ -16,15 +18,15 @@ impl State { #[async_std::main] async fn main() -> tide::Result<()> { - tide::log::start(); + // tide::log::start(); let mut app = tide::with_state(State::new()); app.with(tide::log::LogMiddleware::new()); - app.at("/").get(|req: tide::Request| async move { + app.at("/").get(|req: tide::Request| async move { let state = req.state(); let value = state.value.load(Ordering::Relaxed); Ok(format!("{}\n", value)) }); - app.at("/inc").get(|req: tide::Request| async move { + app.at("/inc").get(|req: tide::Request| async move { let state = req.state(); let value = state.value.fetch_add(1, Ordering::Relaxed) + 1; Ok(format!("{}\n", value)) @@ -32,3 +34,9 @@ async fn main() -> tide::Result<()> { app.listen("127.0.0.1:8080").await?; Ok(()) } + +impl RequestState for tide::Request { + fn state(&self) -> &State { + self.ext::().unwrap() + } +} diff --git a/examples/static_file.rs b/examples/static_file.rs index 6cd80a6a..ab1a3b6d 100644 --- a/examples/static_file.rs +++ b/examples/static_file.rs @@ -1,6 +1,6 @@ #[async_std::main] async fn main() -> Result<(), std::io::Error> { - tide::log::start(); + // tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); app.at("/").get(|_| async { Ok("visit /src/*") }); diff --git a/examples/upload.rs b/examples/upload.rs index 0297a368..84344ef7 100644 --- a/examples/upload.rs +++ b/examples/upload.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use async_std::{fs::OpenOptions, io}; use tempfile::TempDir; use tide::prelude::*; -use tide::{Body, Request, Response, StatusCode}; +use tide::{Body, Request, RequestState, Response, StatusCode}; #[derive(Clone)] struct TempDirState { @@ -24,9 +24,15 @@ impl TempDirState { } } +impl RequestState for Request { + fn state(&self) -> &TempDirState { + self.ext::().unwrap() + } +} + #[async_std::main] async fn main() -> Result<(), IoError> { - tide::log::start(); + // tide::log::start(); let mut app = tide::with_state(TempDirState::try_new()?); app.with(tide::log::LogMiddleware::new()); @@ -36,9 +42,10 @@ async fn main() -> Result<(), IoError> { // $ curl localhost:8080/README.md # this reads the file from the same temp directory app.at(":file") - .put(|req: Request| async move { + .put(|req: Request| async move { let path = req.param("file")?; - let fs_path = req.state().path().join(path); + let state = req.state(); + let fs_path = state.path().join(path); let file = OpenOptions::new() .create(true) @@ -55,7 +62,7 @@ async fn main() -> Result<(), IoError> { Ok(json!({ "bytes": bytes_written })) }) - .get(|req: Request| async move { + .get(|req: Request| async move { let path = req.param("file")?; let fs_path = req.state().path().join(path); diff --git a/src/cookies/middleware.rs b/src/cookies/middleware.rs index 145d2417..98183ed5 100644 --- a/src/cookies/middleware.rs +++ b/src/cookies/middleware.rs @@ -15,7 +15,7 @@ use std::sync::{Arc, RwLock}; /// # use tide::{Request, Response, StatusCode}; /// # use tide::http::cookies::Cookie; /// let mut app = tide::Server::new(); -/// app.at("/get").get(|req: Request<()>| async move { +/// app.at("/get").get(|req: Request| async move { /// Ok(req.cookie("testCookie").unwrap().value().to_string()) /// }); /// app.at("/set").get(|_| async { @@ -36,7 +36,7 @@ impl CookiesMiddleware { #[async_trait] impl Middleware for CookiesMiddleware { - async fn handle(&self, mut ctx: Request, next: Next<'_, State>) -> crate::Result { + async fn handle(&self, mut ctx: Request, next: Next<'_, State>) -> crate::Result { let cookie_jar = if let Some(cookie_data) = ctx.ext::() { cookie_data.content.clone() } else { @@ -112,7 +112,7 @@ impl LazyJar { } impl CookieData { - pub(crate) fn from_request(req: &Request) -> Self { + pub(crate) fn from_request(req: &Request) -> Self { let jar = if let Some(cookie_headers) = req.header(&headers::COOKIE) { let mut jar = CookieJar::new(); for cookie_header in cookie_headers { diff --git a/src/endpoint.rs b/src/endpoint.rs index 2857c291..a5170332 100644 --- a/src/endpoint.rs +++ b/src/endpoint.rs @@ -23,7 +23,7 @@ use crate::{Middleware, Request, Response}; /// A simple endpoint that is invoked on a `GET` request and returns a `String`: /// /// ```no_run -/// async fn hello(_req: tide::Request<()>) -> tide::Result { +/// async fn hello(_req: tide::Request) -> tide::Result { /// Ok(String::from("hello")) /// } /// @@ -35,7 +35,7 @@ use crate::{Middleware, Request, Response}; /// /// ```no_run /// # use core::future::Future; -/// fn hello(_req: tide::Request<()>) -> impl Future> { +/// fn hello(_req: tide::Request) -> impl Future> { /// async_std::future::ready(Ok(String::from("hello"))) /// } /// @@ -45,22 +45,21 @@ use crate::{Middleware, Request, Response}; /// /// Tide routes will also accept endpoints with `Fn` signatures of this form, but using the `async` keyword has better ergonomics. #[async_trait] -pub trait Endpoint: Send + Sync + 'static { +pub trait Endpoint: Send + Sync + 'static { /// Invoke the endpoint within the given context - async fn call(&self, req: Request) -> crate::Result; + async fn call(&self, req: Request) -> crate::Result; } -pub(crate) type DynEndpoint = dyn Endpoint; +pub(crate) type DynEndpoint = dyn Endpoint; #[async_trait] -impl Endpoint for F +impl Endpoint for F where - State: Clone + Send + Sync + 'static, - F: Send + Sync + 'static + Fn(Request) -> Fut, + F: Send + Sync + 'static + Fn(Request) -> Fut, Fut: Future> + Send + 'static, Res: Into + 'static, { - async fn call(&self, req: Request) -> crate::Result { + async fn call(&self, req: Request) -> crate::Result { let fut = (self)(req); let res = fut.await?; Ok(res.into()) @@ -94,12 +93,12 @@ impl std::fmt::Debug for MiddlewareEndpoint { impl MiddlewareEndpoint where State: Clone + Send + Sync + 'static, - E: Endpoint, + E: Endpoint, { pub(crate) fn wrap_with_middleware( ep: E, middleware: &[Arc>], - ) -> Box + Send + Sync + 'static> { + ) -> Box { if middleware.is_empty() { Box::new(ep) } else { @@ -112,12 +111,12 @@ where } #[async_trait] -impl Endpoint for MiddlewareEndpoint +impl Endpoint for MiddlewareEndpoint where State: Clone + Send + Sync + 'static, - E: Endpoint, + E: Endpoint, { - async fn call(&self, req: Request) -> crate::Result { + async fn call(&self, req: Request) -> crate::Result { let next = Next { endpoint: &self.endpoint, next_middleware: &self.middleware, @@ -127,8 +126,8 @@ where } #[async_trait] -impl Endpoint for Box> { - async fn call(&self, request: Request) -> crate::Result { +impl Endpoint for Box { + async fn call(&self, request: Request) -> crate::Result { self.as_ref().call(request).await } } diff --git a/src/fs/serve_dir.rs b/src/fs/serve_dir.rs index 9b464153..d7581ee9 100644 --- a/src/fs/serve_dir.rs +++ b/src/fs/serve_dir.rs @@ -19,11 +19,8 @@ impl ServeDir { } #[async_trait::async_trait] -impl Endpoint for ServeDir -where - State: Clone + Send + Sync + 'static, -{ - async fn call(&self, req: Request) -> Result { +impl Endpoint for ServeDir { + async fn call(&self, req: Request) -> Result { let path = req.url().path(); let path = path .strip_prefix(&self.prefix.trim_end_matches('*')) @@ -80,11 +77,11 @@ mod test { }) } - fn request(path: &str) -> crate::Request<()> { + fn request(path: &str) -> crate::Request { let request = crate::http::Request::get( crate::http::Url::parse(&format!("http://localhost/{}", path)).unwrap(), ); - crate::Request::new((), request, vec![]) + crate::Request::new(request, vec![]) } #[async_std::test] diff --git a/src/fs/serve_file.rs b/src/fs/serve_file.rs index 0c3d72c4..8aa9c862 100644 --- a/src/fs/serve_file.rs +++ b/src/fs/serve_file.rs @@ -21,8 +21,8 @@ impl ServeFile { } #[async_trait] -impl Endpoint for ServeFile { - async fn call(&self, _: Request) -> Result { +impl Endpoint for ServeFile { + async fn call(&self, _: Request) -> Result { match Body::from_file(&self.path).await { Ok(body) => Ok(Response::builder(StatusCode::Ok).body(body).build()), Err(e) if e.kind() == io::ErrorKind::NotFound => { @@ -53,10 +53,10 @@ mod test { Ok(ServeFile::init(file_path)?) } - fn request(path: &str) -> crate::Request<()> { + fn request(path: &str) -> crate::Request { let request = crate::http::Request::get(Url::parse(&format!("http://localhost/{}", path)).unwrap()); - crate::Request::new((), request, vec![]) + crate::Request::new(request, vec![]) } #[async_std::test] diff --git a/src/lib.rs b/src/lib.rs index 90f43d6b..26771a43 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,7 +32,7 @@ //! //! #[async_std::main] //! async fn main() -> tide::Result<()> { -//! tide::log::start(); +//! // tide::log::start(); //! let mut app = tide::new(); //! app.with(tide::log::LogMiddleware::new()); //! app.at("/orders/shoes").post(order_shoes); @@ -40,7 +40,7 @@ //! Ok(()) //! } //! -//! async fn order_shoes(mut req: Request<()>) -> tide::Result { +//! async fn order_shoes(mut req: Request) -> tide::Result { //! let Animal { name, legs } = req.body_json().await?; //! Ok(format!("Hello, {}! I've put in an order for {} shoes", name, legs).into()) //! } @@ -97,6 +97,7 @@ pub use request::Request; pub use response::Response; pub use response_builder::ResponseBuilder; pub use route::Route; +pub use server::RequestState; pub use server::Server; pub use http_types::{self as http, Body, Error, Status, StatusCode}; @@ -130,7 +131,7 @@ pub fn new() -> server::Server<()> { /// # use async_std::task::block_on; /// # fn main() -> Result<(), std::io::Error> { block_on(async { /// # -/// use tide::Request; +/// use tide::{Request, RequestState}; /// /// /// The shared application state. /// #[derive(Clone)] @@ -143,9 +144,15 @@ pub fn new() -> server::Server<()> { /// name: "Nori".to_string() /// }; /// +/// impl RequestState for Request { +/// fn state(&self) -> &State { +/// self.ext::().unwrap() +/// } +/// } +/// /// // Initialize the application with state. /// let mut app = tide::with_state(state); -/// app.at("/").get(|req: Request| async move { +/// app.at("/").get(|req: Request| async move { /// Ok(format!("Hello, {}!", &req.state().name)) /// }); /// app.listen("127.0.0.1:8080").await?; diff --git a/src/listener/concurrent_listener.rs b/src/listener/concurrent_listener.rs index 68ef8e67..0cc52bd8 100644 --- a/src/listener/concurrent_listener.rs +++ b/src/listener/concurrent_listener.rs @@ -13,7 +13,7 @@ use futures_util::stream::{futures_unordered::FuturesUnordered, StreamExt}; /// ```rust /// fn main() -> Result<(), std::io::Error> { /// async_std::task::block_on(async { -/// tide::log::start(); +/// // tide::log::start(); /// let mut app = tide::new(); /// app.at("/").get(|_| async { Ok("Hello, world!") }); /// diff --git a/src/listener/failover_listener.rs b/src/listener/failover_listener.rs index d81658c7..d4eea4d3 100644 --- a/src/listener/failover_listener.rs +++ b/src/listener/failover_listener.rs @@ -15,7 +15,7 @@ use crate::listener::ListenInfo; /// ```rust /// fn main() -> Result<(), std::io::Error> { /// async_std::task::block_on(async { -/// tide::log::start(); +/// // tide::log::start(); /// let mut app = tide::new(); /// app.at("/").get(|_| async { Ok("Hello, world!") }); /// diff --git a/src/log/middleware.rs b/src/log/middleware.rs index cfc7f261..8ef7e036 100644 --- a/src/log/middleware.rs +++ b/src/log/middleware.rs @@ -28,7 +28,7 @@ impl LogMiddleware { /// Log a request and a response. async fn log<'a, State: Clone + Send + Sync + 'static>( &'a self, - mut req: Request, + mut req: Request, next: Next<'a, State>, ) -> crate::Result { if req.ext::().is_some() { @@ -95,7 +95,7 @@ impl LogMiddleware { #[async_trait::async_trait] impl Middleware for LogMiddleware { - async fn handle(&self, req: Request, next: Next<'_, State>) -> crate::Result { + async fn handle(&self, req: Request, next: Next<'_, State>) -> crate::Result { self.log(req, next).await } } diff --git a/src/log/mod.rs b/src/log/mod.rs index 8f74ac2d..306687b1 100644 --- a/src/log/mod.rs +++ b/src/log/mod.rs @@ -5,7 +5,7 @@ //! ```no_run //! use tide::log; //! -//! log::start(); +//! // log::start(); //! //! log::info!("Hello cats"); //! log::debug!("{} wants tuna", "Nori"); diff --git a/src/middleware.rs b/src/middleware.rs index 7e1ca9a9..20a32d74 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -12,7 +12,7 @@ use std::pin::Pin; #[async_trait] pub trait Middleware: Send + Sync + 'static { /// Asynchronously handle the request, and return a response. - async fn handle(&self, request: Request, next: Next<'_, State>) -> crate::Result; + async fn handle(&self, request: Request, next: Next<'_, State>) -> crate::Result; /// Set the middleware's name. By default it uses the type signature. fn name(&self) -> &str { @@ -28,11 +28,11 @@ where + Sync + 'static + for<'a> Fn( - Request, + Request, Next<'a, State>, ) -> Pin + 'a + Send>>, { - async fn handle(&self, req: Request, next: Next<'_, State>) -> crate::Result { + async fn handle(&self, req: Request, next: Next<'_, State>) -> crate::Result { (self)(req, next).await } } @@ -40,13 +40,13 @@ where /// The remainder of a middleware chain, including the endpoint. #[allow(missing_debug_implementations)] pub struct Next<'a, State> { - pub(crate) endpoint: &'a DynEndpoint, + pub(crate) endpoint: &'a DynEndpoint, pub(crate) next_middleware: &'a [Arc>], } impl Next<'_, State> { /// Asynchronously execute the remaining middleware chain. - pub async fn run(mut self, req: Request) -> Response { + pub async fn run(mut self, req: Request) -> Response { if let Some((current, next)) = self.next_middleware.split_first() { self.next_middleware = next; match current.handle(req, self).await { diff --git a/src/redirect.rs b/src/redirect.rs index acb50f92..ad9113a0 100644 --- a/src/redirect.rs +++ b/src/redirect.rs @@ -28,7 +28,7 @@ use crate::{Endpoint, Request, Response}; /// # use tide::{Response, Redirect, Request, StatusCode}; /// # fn next_product() -> Option { None } /// # #[allow(dead_code)] -/// async fn route_handler(request: Request<()>) -> tide::Result { +/// async fn route_handler(request: Request) -> tide::Result { /// if let Some(product_url) = next_product() { /// Ok(Redirect::new(product_url).into()) /// } else { @@ -86,12 +86,11 @@ impl> Redirect { } #[async_trait::async_trait] -impl Endpoint for Redirect +impl Endpoint for Redirect where - State: Clone + Send + Sync + 'static, T: AsRef + Send + Sync + 'static, { - async fn call(&self, _req: Request) -> crate::Result { + async fn call(&self, _req: Request) -> crate::Result { Ok(self.into()) } } diff --git a/src/request.rs b/src/request.rs index 854154e6..d69c777d 100644 --- a/src/request.rs +++ b/src/request.rs @@ -23,26 +23,31 @@ pin_project_lite::pin_project! { /// Requests also provide *extensions*, a type map primarily used for low-level /// communication between middleware and endpoints. #[derive(Debug)] - pub struct Request { - pub(crate) state: State, + pub struct Request { #[pin] pub(crate) req: http::Request, pub(crate) route_params: Vec>, } } -impl Request { +impl Request { /// Create a new `Request`. pub(crate) fn new( - state: State, req: http_types::Request, route_params: Vec>, ) -> Self { - Self { - state, - req, - route_params, - } + Self { req, route_params } + } + + /// Create a new `Request`. + pub(crate) fn with_state( + state: S, + req: http_types::Request, + route_params: Vec>, + ) -> Self { + let mut req = Request::new(req, route_params); + req.set_ext::(state); + req } /// Access the request's HTTP method. @@ -56,7 +61,7 @@ impl Request { /// use tide::Request; /// /// let mut app = tide::new(); - /// app.at("/").get(|req: Request<()>| async move { + /// app.at("/").get(|req: Request| async move { /// assert_eq!(req.method(), http_types::Method::Get); /// Ok("") /// }); @@ -80,7 +85,7 @@ impl Request { /// use tide::Request; /// /// let mut app = tide::new(); - /// app.at("/").get(|req: Request<()>| async move { + /// app.at("/").get(|req: Request| async move { /// assert_eq!(req.url(), &"/".parse::().unwrap()); /// Ok("") /// }); @@ -104,7 +109,7 @@ impl Request { /// use tide::Request; /// /// let mut app = tide::new(); - /// app.at("/").get(|req: Request<()>| async move { + /// app.at("/").get(|req: Request| async move { /// assert_eq!(req.version(), Some(http_types::Version::Http1_1)); /// Ok("") /// }); @@ -175,7 +180,7 @@ impl Request { /// use tide::Request; /// /// let mut app = tide::new(); - /// app.at("/").get(|req: Request<()>| async move { + /// app.at("/").get(|req: Request| async move { /// assert_eq!(req.header("X-Forwarded-For").unwrap(), "127.0.0.1"); /// Ok("") /// }); @@ -260,12 +265,6 @@ impl Request { self.req.ext_mut().insert(val) } - #[must_use] - /// Access application scoped state. - pub fn state(&self) -> &State { - &self.state - } - /// Extract and parse a route parameter by name. /// /// Returns the parameter as a `&str`, borrowed from this `Request`. @@ -284,7 +283,7 @@ impl Request { /// # /// use tide::{Request, Result}; /// - /// async fn greet(req: Request<()>) -> Result { + /// async fn greet(req: Request) -> Result { /// let name = req.param("name").unwrap_or("world"); /// Ok(format!("Hello, {}!", name)) /// } @@ -316,7 +315,7 @@ impl Request { /// # /// use tide::{Request, Result}; /// - /// async fn greet(req: Request<()>) -> Result { + /// async fn greet(req: Request) -> Result { /// let name = req.wildcard().unwrap_or("world"); /// Ok(format!("Hello, {}!", name)) /// } @@ -352,7 +351,7 @@ impl Request { /// selections: HashMap, /// } /// - /// let req: Request<()> = http::Request::get("https://httpbin.org/get?page=2&selections[width]=narrow&selections[height]=tall").into(); + /// let req: Request = http::Request::get("https://httpbin.org/get?page=2&selections[width]=narrow&selections[height]=tall").into(); /// let Index { page, selections } = req.query().unwrap(); /// assert_eq!(page, 2); /// assert_eq!(selections["width"], "narrow"); @@ -365,7 +364,7 @@ impl Request { /// format: &'q str, /// } /// - /// let req: Request<()> = http::Request::get("https://httpbin.org/get?format=bananna").into(); + /// let req: Request = http::Request::get("https://httpbin.org/get?format=bananna").into(); /// let Query { format } = req.query().unwrap(); /// assert_eq!(format, "bananna"); /// ``` @@ -407,7 +406,7 @@ impl Request { /// use tide::Request; /// /// let mut app = tide::new(); - /// app.at("/").get(|mut req: Request<()>| async move { + /// app.at("/").get(|mut req: Request| async move { /// let _body: Vec = req.body_bytes().await.unwrap(); /// Ok("") /// }); @@ -441,7 +440,7 @@ impl Request { /// use tide::Request; /// /// let mut app = tide::new(); - /// app.at("/").get(|mut req: Request<()>| async move { + /// app.at("/").get(|mut req: Request| async move { /// let _body: String = req.body_string().await.unwrap(); /// Ok("") /// }); @@ -481,7 +480,7 @@ impl Request { /// legs: u8 /// } /// - /// app.at("/").post(|mut req: tide::Request<()>| async move { + /// app.at("/").post(|mut req: tide::Request| async move { /// let animal: Animal = req.body_form().await?; /// Ok(format!( /// "hello, {}! i've put in an order for {} shoes", @@ -556,31 +555,31 @@ impl Request { } } -impl AsRef for Request { +impl AsRef for Request { fn as_ref(&self) -> &http::Request { &self.req } } -impl AsMut for Request { +impl AsMut for Request { fn as_mut(&mut self) -> &mut http::Request { &mut self.req } } -impl AsRef for Request { +impl AsRef for Request { fn as_ref(&self) -> &http::Headers { self.req.as_ref() } } -impl AsMut for Request { +impl AsMut for Request { fn as_mut(&mut self) -> &mut http::Headers { self.req.as_mut() } } -impl Read for Request { +impl Read for Request { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -590,27 +589,27 @@ impl Read for Request { } } -impl From> for http::Request { - fn from(request: Request) -> http::Request { +impl From for http::Request { + fn from(request: Request) -> http::Request { request.req } } -impl From for Request { - fn from(request: http_types::Request) -> Request { - Request::new(State::default(), request, vec![]) +impl From for Request { + fn from(request: http_types::Request) -> Request { + Request::new(request, vec![]) } } -impl From> for Response { - fn from(mut request: Request) -> Response { +impl From for Response { + fn from(mut request: Request) -> Response { let mut res = Response::new(StatusCode::Ok); res.set_body(request.take_body()); res } } -impl IntoIterator for Request { +impl IntoIterator for Request { type Item = (HeaderName, HeaderValues); type IntoIter = http_types::headers::IntoIter; @@ -621,7 +620,7 @@ impl IntoIterator for Request { } } -impl<'a, State> IntoIterator for &'a Request { +impl<'a> IntoIterator for &'a Request { type Item = (&'a HeaderName, &'a HeaderValues); type IntoIter = http_types::headers::Iter<'a>; @@ -631,7 +630,7 @@ impl<'a, State> IntoIterator for &'a Request { } } -impl<'a, State> IntoIterator for &'a mut Request { +impl<'a> IntoIterator for &'a mut Request { type Item = (&'a HeaderName, &'a mut HeaderValues); type IntoIter = http_types::headers::IterMut<'a>; @@ -641,7 +640,7 @@ impl<'a, State> IntoIterator for &'a mut Request { } } -impl Index for Request { +impl Index for Request { type Output = HeaderValues; /// Returns a reference to the value corresponding to the supplied name. @@ -655,7 +654,7 @@ impl Index for Request { } } -impl Index<&str> for Request { +impl Index<&str> for Request { type Output = HeaderValues; /// Returns a reference to the value corresponding to the supplied name. diff --git a/src/route.rs b/src/route.rs index 8b827c1d..4401d181 100644 --- a/src/route.rs +++ b/src/route.rs @@ -18,7 +18,7 @@ use crate::{router::Router, Endpoint, Middleware}; /// [`Server::at`]: ./struct.Server.html#method.at #[allow(missing_debug_implementations)] pub struct Route<'a, State> { - router: &'a mut Router, + router: &'a mut Router, path: String, middleware: Vec>>, /// Indicates whether the path of current route is treated as a prefix. Set by @@ -29,7 +29,7 @@ pub struct Route<'a, State> { } impl<'a, State: Clone + Send + Sync + 'static> Route<'a, State> { - pub(crate) fn new(router: &'a mut Router, path: String) -> Route<'a, State> { + pub(crate) fn new(router: &'a mut Router, path: String) -> Route<'a, State> { Route { router, path, @@ -112,8 +112,8 @@ impl<'a, State: Clone + Send + Sync + 'static> Route<'a, State> { /// let mut example = tide::with_state("world"); /// example /// .at("/") - /// .get(|req: tide::Request<&'static str>| async move { - /// Ok(format!("Hello {state}!", state = req.state())) + /// .get(|req: tide::Request| async move { + /// Ok(format!("Hello {state}!", state = req.ext::<&str>().unwrap())) /// }); /// example /// }); @@ -181,7 +181,7 @@ impl<'a, State: Clone + Send + Sync + 'static> Route<'a, State> { } /// Add an endpoint for the given HTTP method - pub fn method(&mut self, method: http_types::Method, ep: impl Endpoint) -> &mut Self { + pub fn method(&mut self, method: http_types::Method, ep: impl Endpoint) -> &mut Self { if self.prefix { let ep = StripPrefixEndpoint::new(ep); let wildcard = self.at("*"); @@ -203,7 +203,7 @@ impl<'a, State: Clone + Send + Sync + 'static> Route<'a, State> { /// Add an endpoint for all HTTP methods, as a fallback. /// /// Routes with specific HTTP methods will be tried first. - pub fn all(&mut self, ep: impl Endpoint) -> &mut Self { + pub fn all(&mut self, ep: impl Endpoint) -> &mut Self { if self.prefix { let ep = StripPrefixEndpoint::new(ep); let wildcard = self.at("*"); @@ -221,55 +221,55 @@ impl<'a, State: Clone + Send + Sync + 'static> Route<'a, State> { } /// Add an endpoint for `GET` requests - pub fn get(&mut self, ep: impl Endpoint) -> &mut Self { + pub fn get(&mut self, ep: impl Endpoint) -> &mut Self { self.method(http_types::Method::Get, ep); self } /// Add an endpoint for `HEAD` requests - pub fn head(&mut self, ep: impl Endpoint) -> &mut Self { + pub fn head(&mut self, ep: impl Endpoint) -> &mut Self { self.method(http_types::Method::Head, ep); self } /// Add an endpoint for `PUT` requests - pub fn put(&mut self, ep: impl Endpoint) -> &mut Self { + pub fn put(&mut self, ep: impl Endpoint) -> &mut Self { self.method(http_types::Method::Put, ep); self } /// Add an endpoint for `POST` requests - pub fn post(&mut self, ep: impl Endpoint) -> &mut Self { + pub fn post(&mut self, ep: impl Endpoint) -> &mut Self { self.method(http_types::Method::Post, ep); self } /// Add an endpoint for `DELETE` requests - pub fn delete(&mut self, ep: impl Endpoint) -> &mut Self { + pub fn delete(&mut self, ep: impl Endpoint) -> &mut Self { self.method(http_types::Method::Delete, ep); self } /// Add an endpoint for `OPTIONS` requests - pub fn options(&mut self, ep: impl Endpoint) -> &mut Self { + pub fn options(&mut self, ep: impl Endpoint) -> &mut Self { self.method(http_types::Method::Options, ep); self } /// Add an endpoint for `CONNECT` requests - pub fn connect(&mut self, ep: impl Endpoint) -> &mut Self { + pub fn connect(&mut self, ep: impl Endpoint) -> &mut Self { self.method(http_types::Method::Connect, ep); self } /// Add an endpoint for `PATCH` requests - pub fn patch(&mut self, ep: impl Endpoint) -> &mut Self { + pub fn patch(&mut self, ep: impl Endpoint) -> &mut Self { self.method(http_types::Method::Patch, ep); self } /// Add an endpoint for `TRACE` requests - pub fn trace(&mut self, ep: impl Endpoint) -> &mut Self { + pub fn trace(&mut self, ep: impl Endpoint) -> &mut Self { self.method(http_types::Method::Trace, ep); self } @@ -291,14 +291,12 @@ impl Clone for StripPrefixEndpoint { } #[async_trait::async_trait] -impl Endpoint for StripPrefixEndpoint +impl Endpoint for StripPrefixEndpoint where - State: Clone + Send + Sync + 'static, - E: Endpoint, + E: Endpoint, { - async fn call(&self, req: crate::Request) -> crate::Result { + async fn call(&self, req: crate::Request) -> crate::Result { let crate::Request { - state, mut req, route_params, } = req; @@ -311,12 +309,6 @@ where req.url_mut().set_path(rest); - self.0 - .call(crate::Request { - state, - req, - route_params, - }) - .await + self.0.call(crate::Request { req, route_params }).await } } diff --git a/src/router.rs b/src/router.rs index 70b1ab35..a1ec4e52 100644 --- a/src/router.rs +++ b/src/router.rs @@ -9,12 +9,12 @@ use crate::{Request, Response, StatusCode}; /// Internally, we have a separate state machine per http method; indexing /// by the method first allows the table itself to be more efficient. #[allow(missing_debug_implementations)] -pub(crate) struct Router { - method_map: HashMap>>>, - all_method_router: MethodRouter>>, +pub(crate) struct Router { + method_map: HashMap>>, + all_method_router: MethodRouter>, } -impl std::fmt::Debug for Router { +impl std::fmt::Debug for Router { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Router") .field("method_map", &self.method_map) @@ -24,12 +24,12 @@ impl std::fmt::Debug for Router { } /// The result of routing a URL -pub(crate) struct Selection<'a, State> { - pub(crate) endpoint: &'a DynEndpoint, +pub(crate) struct Selection<'a> { + pub(crate) endpoint: &'a DynEndpoint, pub(crate) params: Captures<'static, 'static>, } -impl Router { +impl Router { pub(crate) fn new() -> Self { Router { method_map: HashMap::default(), @@ -37,12 +37,7 @@ impl Router { } } - pub(crate) fn add( - &mut self, - path: &str, - method: http_types::Method, - ep: Box>, - ) { + pub(crate) fn add(&mut self, path: &str, method: http_types::Method, ep: Box) { self.method_map .entry(method) .or_insert_with(MethodRouter::new) @@ -50,11 +45,11 @@ impl Router { .unwrap() } - pub(crate) fn add_all(&mut self, path: &str, ep: Box>) { + pub(crate) fn add_all(&mut self, path: &str, ep: Box) { self.all_method_router.add(path, ep).unwrap() } - pub(crate) fn route(&self, path: &str, method: http_types::Method) -> Selection<'_, State> { + pub(crate) fn route(&self, path: &str, method: http_types::Method) -> Selection<'_> { if let Some(m) = self .method_map .get(&method) @@ -95,14 +90,10 @@ impl Router { } } -async fn not_found_endpoint( - _req: Request, -) -> crate::Result { +async fn not_found_endpoint(_req: Request) -> crate::Result { Ok(Response::new(StatusCode::NotFound)) } -async fn method_not_allowed( - _req: Request, -) -> crate::Result { +async fn method_not_allowed(_req: Request) -> crate::Result { Ok(Response::new(StatusCode::MethodNotAllowed)) } diff --git a/src/security/cors.rs b/src/security/cors.rs index cfb2f04d..b3e21a6c 100644 --- a/src/security/cors.rs +++ b/src/security/cors.rs @@ -137,7 +137,7 @@ impl CorsMiddleware { #[async_trait::async_trait] impl Middleware for CorsMiddleware { - async fn handle(&self, req: Request, next: Next<'_, State>) -> Result { + async fn handle(&self, req: Request, next: Next<'_, State>) -> Result { // TODO: how should multiple origin values be handled? let origins = req.header(&headers::ORIGIN).cloned(); diff --git a/src/server.rs b/src/server.rs index 1849e9ca..88430181 100644 --- a/src/server.rs +++ b/src/server.rs @@ -27,7 +27,7 @@ use crate::{Endpoint, Request, Route}; /// response processing, such as compression, default headers, or logging. To /// add middleware to an app, use the [`Server::with`] method. pub struct Server { - router: Arc>, + router: Arc, state: State, /// Holds the middleware stack. /// @@ -81,7 +81,7 @@ where /// # use async_std::task::block_on; /// # fn main() -> Result<(), std::io::Error> { block_on(async { /// # - /// use tide::Request; + /// use tide::{Request, RequestState}; /// /// /// The shared application state. /// #[derive(Clone)] @@ -94,9 +94,15 @@ where /// name: "Nori".to_string() /// }; /// + /// impl RequestState for Request { + /// fn state(&self) -> &State { + /// self.ext::().unwrap() + /// } + /// } + /// /// // Initialize the application with state. /// let mut app = tide::with_state(state); - /// app.at("/").get(|req: Request| async move { + /// app.at("/").get(|req: Request| async move { /// Ok(format!("Hello, {}!", &req.state().name)) /// }); /// app.listen("127.0.0.1:8080").await?; @@ -289,7 +295,7 @@ where let method = req.method().to_owned(); let Selection { endpoint, params } = router.route(req.url().path(), method); let route_params = vec![params]; - let req = Request::new(state, req, route_params); + let req = Request::with_state(state, req, route_params); let next = Next { endpoint, @@ -334,10 +340,8 @@ impl Clone for Server { } #[async_trait::async_trait] -impl - Endpoint for Server -{ - async fn call(&self, req: Request) -> crate::Result { +impl Endpoint for Server { + async fn call(&self, req: Request) -> crate::Result { let Request { req, mut route_params, @@ -351,7 +355,7 @@ impl { + fn state(&self) -> &State; +} + #[crate::utils::async_trait] impl http_client::HttpClient for Server { async fn send(&self, req: crate::http::Request) -> crate::http::Result { diff --git a/src/sessions/middleware.rs b/src/sessions/middleware.rs index ce564f72..12b88229 100644 --- a/src/sessions/middleware.rs +++ b/src/sessions/middleware.rs @@ -27,20 +27,20 @@ const BASE64_DIGEST_LEN: usize = 44; /// b"we recommend you use std::env::var(\"TIDE_SECRET\").unwrap().as_bytes() instead of a fixed value" /// )); /// -/// app.with(tide::utils::Before(|mut request: tide::Request<()>| async move { +/// app.with(tide::utils::Before(|mut request: tide::Request| async move { /// let session = request.session_mut(); /// let visits: usize = session.get("visits").unwrap_or_default(); /// session.insert("visits", visits + 1).unwrap(); /// request /// })); /// -/// app.at("/").get(|req: tide::Request<()>| async move { +/// app.at("/").get(|req: tide::Request| async move { /// let visits: usize = req.session().get("visits").unwrap(); /// Ok(format!("you have visited this website {} times", visits)) /// }); /// /// app.at("/reset") -/// .get(|mut req: tide::Request<()>| async move { +/// .get(|mut req: tide::Request| async move { /// req.session_mut().destroy(); /// Ok(tide::Redirect::new("/")) /// }); @@ -79,7 +79,7 @@ where Store: SessionStore, State: Clone + Send + Sync + 'static, { - async fn handle(&self, mut request: Request, next: Next<'_, State>) -> crate::Result { + async fn handle(&self, mut request: Request, next: Next<'_, State>) -> crate::Result { let cookie = request.cookie(&self.cookie_name); let cookie_value = cookie .clone() diff --git a/src/sse/endpoint.rs b/src/sse/endpoint.rs index fb81c26e..929e7859 100644 --- a/src/sse/endpoint.rs +++ b/src/sse/endpoint.rs @@ -14,7 +14,7 @@ use std::sync::Arc; pub fn endpoint(handler: F) -> SseEndpoint where State: Clone + Send + Sync + 'static, - F: Fn(Request, Sender) -> Fut + Send + Sync + 'static, + F: Fn(Request, Sender) -> Fut + Send + Sync + 'static, Fut: Future> + Send + 'static, { SseEndpoint { @@ -28,7 +28,7 @@ where pub struct SseEndpoint where State: Clone + Send + Sync + 'static, - F: Fn(Request, Sender) -> Fut + Send + Sync + 'static, + F: Fn(Request, Sender) -> Fut + Send + Sync + 'static, Fut: Future> + Send + 'static, { handler: Arc, @@ -39,10 +39,10 @@ where impl Endpoint for SseEndpoint where State: Clone + Send + Sync + 'static, - F: Fn(Request, Sender) -> Fut + Send + Sync + 'static, + F: Fn(Request, Sender) -> Fut + Send + Sync + 'static, Fut: Future> + Send + 'static, { - async fn call(&self, req: Request) -> Result { + async fn call(&self, req: Request) -> Result { let handler = self.handler.clone(); let (sender, encoder) = async_sse::encode(); task::spawn(async move { diff --git a/src/sse/upgrade.rs b/src/sse/upgrade.rs index 6cccda96..4171d96b 100644 --- a/src/sse/upgrade.rs +++ b/src/sse/upgrade.rs @@ -9,10 +9,10 @@ use async_std::io::BufReader; use async_std::task; /// Upgrade an existing HTTP connection to an SSE connection. -pub fn upgrade(req: Request, handler: F) -> Response +pub fn upgrade(req: Request, handler: F) -> Response where State: Clone + Send + Sync + 'static, - F: Fn(Request, Sender) -> Fut + Send + Sync + 'static, + F: Fn(Request, Sender) -> Fut + Send + Sync + 'static, Fut: Future> + Send + 'static, { let (sender, encoder) = async_sse::encode(); diff --git a/src/utils.rs b/src/utils.rs index fde11b3e..acea7e20 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -16,7 +16,7 @@ use std::future::Future; /// use std::time::Instant; /// /// let mut app = tide::new(); -/// app.with(utils::Before(|mut request: Request<()>| async move { +/// app.with(utils::Before(|mut request: Request| async move { /// request.set_ext(Instant::now()); /// request /// })); @@ -28,10 +28,10 @@ pub struct Before(pub F); impl Middleware for Before where State: Clone + Send + Sync + 'static, - F: Fn(Request) -> Fut + Send + Sync + 'static, - Fut: Future> + Send + Sync + 'static, + F: Fn(Request) -> Fut + Send + Sync + 'static, + Fut: Future + Send + Sync + 'static, { - async fn handle(&self, request: Request, next: Next<'_, State>) -> crate::Result { + async fn handle(&self, request: Request, next: Next<'_, State>) -> crate::Result { let request = (self.0)(request).await; Ok(next.run(request).await) } @@ -65,7 +65,7 @@ where F: Fn(Response) -> Fut + Send + Sync + 'static, Fut: Future + Send + Sync + 'static, { - async fn handle(&self, request: Request, next: Next<'_, State>) -> crate::Result { + async fn handle(&self, request: Request, next: Next<'_, State>) -> crate::Result { let response = next.run(request).await; (self.0)(response).await } diff --git a/tests/cookies.rs b/tests/cookies.rs index 3eed212e..2c4bfbba 100644 --- a/tests/cookies.rs +++ b/tests/cookies.rs @@ -6,7 +6,7 @@ use tide::{Request, Response, Server, StatusCode}; static COOKIE_NAME: &str = "testCookie"; -async fn retrieve_cookie(req: Request<()>) -> tide::Result { +async fn retrieve_cookie(req: Request) -> tide::Result { Ok(format!( "{} and also {}", req.cookie(COOKIE_NAME).unwrap().value(), @@ -14,19 +14,19 @@ async fn retrieve_cookie(req: Request<()>) -> tide::Result { )) } -async fn insert_cookie(_req: Request<()>) -> tide::Result { +async fn insert_cookie(_req: Request) -> tide::Result { let mut res = Response::new(StatusCode::Ok); res.insert_cookie(Cookie::new(COOKIE_NAME, "NewCookieValue")); Ok(res) } -async fn remove_cookie(_req: Request<()>) -> tide::Result { +async fn remove_cookie(_req: Request) -> tide::Result { let mut res = Response::new(StatusCode::Ok); res.remove_cookie(Cookie::named(COOKIE_NAME)); Ok(res) } -async fn set_multiple_cookie(_req: Request<()>) -> tide::Result { +async fn set_multiple_cookie(_req: Request) -> tide::Result { let mut res = Response::new(StatusCode::Ok); res.insert_cookie(Cookie::new("C1", "V1")); res.insert_cookie(Cookie::new("C2", "V2")); diff --git a/tests/endpoint.rs b/tests/endpoint.rs index 7d376997..ba8dcf16 100644 --- a/tests/endpoint.rs +++ b/tests/endpoint.rs @@ -3,7 +3,7 @@ use tide::Response; #[async_std::test] async fn should_accept_boxed_endpoints() { - fn endpoint() -> Box> { + fn endpoint() -> Box { Box::new(|_| async { Ok("hello world") }) } diff --git a/tests/function_middleware.rs b/tests/function_middleware.rs index fd3680a6..2f288639 100644 --- a/tests/function_middleware.rs +++ b/tests/function_middleware.rs @@ -5,7 +5,7 @@ use tide::http::{self, url::Url, Method}; mod test_utils; fn auth_middleware<'a>( - request: tide::Request<()>, + request: tide::Request, next: tide::Next<'a, ()>, ) -> Pin + 'a + Send>> { let authenticated = match request.header("X-Auth") { @@ -22,7 +22,7 @@ fn auth_middleware<'a>( }) } -async fn echo_path(req: tide::Request) -> tide::Result { +async fn echo_path(req: tide::Request) -> tide::Result { Ok(req.url().path().to_string()) } diff --git a/tests/nested.rs b/tests/nested.rs index 7aaeb945..6fe00e8f 100644 --- a/tests/nested.rs +++ b/tests/nested.rs @@ -1,5 +1,6 @@ mod test_utils; use test_utils::ServerTestingExt; +use tide::RequestState; #[async_std::test] async fn nested() -> tide::Result<()> { @@ -18,7 +19,7 @@ async fn nested() -> tide::Result<()> { #[async_std::test] async fn nested_middleware() -> tide::Result<()> { - let echo_path = |req: tide::Request<()>| async move { Ok(req.url().path().to_string()) }; + let echo_path = |req: tide::Request| async move { Ok(req.url().path().to_string()) }; let mut app = tide::new(); let mut inner_app = tide::new(); inner_app.with(tide::utils::After(|mut res: tide::Response| async move { @@ -62,3 +63,9 @@ async fn nested_with_different_state() -> tide::Result<()> { assert_eq!(outer.get("/").recv_string().await?, "Hello, world!"); Ok(()) } + +impl RequestState for tide::Request { + fn state(&self) -> &i32 { + self.ext::().unwrap() + } +} diff --git a/tests/params.rs b/tests/params.rs index 0d551b51..0da56f0a 100644 --- a/tests/params.rs +++ b/tests/params.rs @@ -3,7 +3,7 @@ use tide::{self, Request, Response, Result}; #[async_std::test] async fn test_missing_param() -> tide::Result<()> { - async fn greet(req: Request<()>) -> Result { + async fn greet(req: Request) -> Result { assert_eq!(req.param("name")?, "Param \"name\" not found"); Ok(Response::new(200)) } @@ -19,7 +19,7 @@ async fn test_missing_param() -> tide::Result<()> { #[async_std::test] async fn hello_world_parametrized() -> Result<()> { - async fn greet(req: tide::Request<()>) -> Result> { + async fn greet(req: tide::Request) -> Result> { let body = format!("{} says hello", req.param("name").unwrap_or("nori")); Ok(Response::builder(200).body(body)) } diff --git a/tests/route_middleware.rs b/tests/route_middleware.rs index ba89a15d..157a96b0 100644 --- a/tests/route_middleware.rs +++ b/tests/route_middleware.rs @@ -17,7 +17,7 @@ impl TestMiddleware { impl Middleware for TestMiddleware { async fn handle( &self, - req: tide::Request, + req: tide::Request, next: tide::Next<'_, State>, ) -> tide::Result { let mut res = next.run(req).await; @@ -26,7 +26,7 @@ impl Middleware for TestMiddleware } } -async fn echo_path(req: tide::Request) -> tide::Result { +async fn echo_path(req: tide::Request) -> tide::Result { Ok(req.url().path().to_string()) } diff --git a/tests/serve_dir.rs b/tests/serve_dir.rs index 932eb8a6..d05cce1b 100644 --- a/tests/serve_dir.rs +++ b/tests/serve_dir.rs @@ -3,7 +3,7 @@ use tide::{http, Result, Server}; use std::fs::{self, File}; use std::io::Write; -fn api() -> Box> { +fn api() -> Box { Box::new(|_| async { Ok("api") }) } diff --git a/tests/server.rs b/tests/server.rs index db9c3c54..cc22d569 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -12,7 +12,7 @@ fn hello_world() -> tide::Result<()> { let port = test_utils::find_port().await; let server = task::spawn(async move { let mut app = tide::new(); - app.at("/").get(move |mut req: Request<()>| async move { + app.at("/").get(move |mut req: Request| async move { assert_eq!(req.body_string().await.unwrap(), "nori".to_string()); assert!(req.local_addr().unwrap().contains(&port.to_string())); assert!(req.peer_addr().is_some()); @@ -75,7 +75,7 @@ fn json() -> tide::Result<()> { let port = test_utils::find_port().await; let server = task::spawn(async move { let mut app = tide::new(); - app.at("/").get(|mut req: Request<()>| async move { + app.at("/").get(|mut req: Request| async move { let mut counter: Counter = req.body_json().await.unwrap(); assert_eq!(counter.count, 0); counter.count = 1; diff --git a/tests/sessions.rs b/tests/sessions.rs index 924907a2..b1eaa318 100644 --- a/tests/sessions.rs +++ b/tests/sessions.rs @@ -22,13 +22,13 @@ async fn test_basic_sessions() -> tide::Result<()> { b"12345678901234567890123456789012345", )); - app.with(Before(|mut request: tide::Request<()>| async move { + app.with(Before(|mut request: tide::Request| async move { let visits: usize = request.session().get("visits").unwrap_or_default(); request.session_mut().insert("visits", visits + 1).unwrap(); request })); - app.at("/").get(|req: tide::Request<()>| async move { + app.at("/").get(|req: tide::Request| async move { let visits: usize = req.session().get("visits").unwrap(); Ok(format!("you have visited this website {} times", visits)) }); @@ -70,14 +70,14 @@ async fn test_customized_sessions() -> tide::Result<()> { ); app.at("/").get(|_| async { Ok("/") }); - app.at("/nested").get(|req: tide::Request<()>| async move { + app.at("/nested").get(|req: tide::Request| async move { Ok(format!( "/nested {}", req.session().get::("visits").unwrap_or_default() )) }); app.at("/nested/incr") - .get(|mut req: tide::Request<()>| async move { + .get(|mut req: tide::Request| async move { let mut visits: usize = req.session().get("visits").unwrap_or_default(); visits += 1; req.session_mut().insert("visits", visits)?; @@ -135,22 +135,21 @@ async fn test_session_destruction() -> tide::Result<()> { b"12345678901234567890123456789012345", )); - app.with(Before(|mut request: tide::Request<()>| async move { + app.with(Before(|mut request: tide::Request| async move { let visits: usize = request.session().get("visits").unwrap_or_default(); request.session_mut().insert("visits", visits + 1).unwrap(); request })); - app.at("/").get(|req: tide::Request<()>| async move { + app.at("/").get(|req: tide::Request| async move { let visits: usize = req.session().get("visits").unwrap(); Ok(format!("you have visited this website {} times", visits)) }); - app.at("/logout") - .post(|mut req: tide::Request<()>| async move { - req.session_mut().destroy(); - Ok(Response::new(200)) - }); + app.at("/logout").post(|mut req: tide::Request| async move { + req.session_mut().destroy(); + Ok(Response::new(200)) + }); let response = app.get("/").await?; let cookies = Cookies::from_response(&response); diff --git a/tests/unix.rs b/tests/unix.rs index bc28a5ed..0151ac8d 100644 --- a/tests/unix.rs +++ b/tests/unix.rs @@ -16,7 +16,7 @@ mod unix_tests { let server = task::spawn(async move { let mut app = tide::new(); - app.at("/").get(|req: tide::Request<()>| async move { + app.at("/").get(|req: tide::Request| async move { Ok(req.local_addr().unwrap().to_string()) }); app.listen(sock_path).await?; diff --git a/tests/wildcard.rs b/tests/wildcard.rs index bb45752c..7045fdf3 100644 --- a/tests/wildcard.rs +++ b/tests/wildcard.rs @@ -2,7 +2,7 @@ mod test_utils; use test_utils::ServerTestingExt; use tide::{Error, Request, StatusCode}; -async fn add_one(req: Request<()>) -> Result { +async fn add_one(req: Request) -> Result { let num: i64 = req .param("num")? .parse() @@ -10,7 +10,7 @@ async fn add_one(req: Request<()>) -> Result { Ok((num + 1).to_string()) } -async fn add_two(req: Request<()>) -> Result { +async fn add_two(req: Request) -> Result { let one: i64 = req .param("one")? .parse() @@ -22,14 +22,14 @@ async fn add_two(req: Request<()>) -> Result { Ok((one + two).to_string()) } -async fn echo_param(req: Request<()>) -> tide::Result { +async fn echo_param(req: Request) -> tide::Result { match req.param("param") { Ok(path) => Ok(path.into()), Err(_) => Ok(StatusCode::NotFound.into()), } } -async fn echo_wildcard(req: Request<()>) -> tide::Result { +async fn echo_wildcard(req: Request) -> tide::Result { match req.wildcard() { Some(path) => Ok(path.into()), None => Ok(StatusCode::NotFound.into()), From d9a15140874f417e2130d7a152156ce0778a4feb Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Thu, 14 Jul 2022 21:36:22 -0500 Subject: [PATCH 03/20] Remove state from next/middleware layer --- examples/middleware.rs | 6 +++--- src/endpoint.rs | 16 +++++++--------- src/log/middleware.rs | 10 +++------- src/middleware.rs | 20 ++++++++------------ src/route.rs | 13 ++++++------- src/security/cors.rs | 4 ++-- src/server.rs | 6 +++--- src/utils.rs | 10 ++++------ tests/function_middleware.rs | 2 +- tests/route_middleware.rs | 4 ++-- 10 files changed, 39 insertions(+), 52 deletions(-) diff --git a/examples/middleware.rs b/examples/middleware.rs index a64292df..c2aa05be 100644 --- a/examples/middleware.rs +++ b/examples/middleware.rs @@ -27,7 +27,7 @@ impl UserDatabase { // it would likely be closely tied to a specific application fn user_loader<'a>( mut request: Request, - next: Next<'a, UserDatabase>, + next: Next<'a>, ) -> Pin + Send + 'a>> { Box::pin(async { if let Some(user) = request.state().find_user().await { @@ -61,8 +61,8 @@ impl RequestCounterMiddleware { struct RequestCount(usize); #[tide::utils::async_trait] -impl Middleware for RequestCounterMiddleware { - async fn handle(&self, mut req: Request, next: Next<'_, State>) -> Result { +impl Middleware for RequestCounterMiddleware { + async fn handle(&self, mut req: Request, next: Next<'_>) -> Result { let count = self.requests_counted.fetch_add(1, Ordering::Relaxed); tide::log::trace!("request counter", { count: count }); req.set_ext(RequestCount(count)); diff --git a/src/endpoint.rs b/src/endpoint.rs index a5170332..44712bc7 100644 --- a/src/endpoint.rs +++ b/src/endpoint.rs @@ -66,12 +66,12 @@ where } } -pub(crate) struct MiddlewareEndpoint { +pub(crate) struct MiddlewareEndpoint { endpoint: E, - middleware: Vec>>, + middleware: Vec>, } -impl Clone for MiddlewareEndpoint { +impl Clone for MiddlewareEndpoint { fn clone(&self) -> Self { Self { endpoint: self.endpoint.clone(), @@ -80,7 +80,7 @@ impl Clone for MiddlewareEndpoint { } } -impl std::fmt::Debug for MiddlewareEndpoint { +impl std::fmt::Debug for MiddlewareEndpoint { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( fmt, @@ -90,14 +90,13 @@ impl std::fmt::Debug for MiddlewareEndpoint { } } -impl MiddlewareEndpoint +impl MiddlewareEndpoint where - State: Clone + Send + Sync + 'static, E: Endpoint, { pub(crate) fn wrap_with_middleware( ep: E, - middleware: &[Arc>], + middleware: &[Arc], ) -> Box { if middleware.is_empty() { Box::new(ep) @@ -111,9 +110,8 @@ where } #[async_trait] -impl Endpoint for MiddlewareEndpoint +impl Endpoint for MiddlewareEndpoint where - State: Clone + Send + Sync + 'static, E: Endpoint, { async fn call(&self, req: Request) -> crate::Result { diff --git a/src/log/middleware.rs b/src/log/middleware.rs index 8ef7e036..3a6ee7f7 100644 --- a/src/log/middleware.rs +++ b/src/log/middleware.rs @@ -26,11 +26,7 @@ impl LogMiddleware { } /// Log a request and a response. - async fn log<'a, State: Clone + Send + Sync + 'static>( - &'a self, - mut req: Request, - next: Next<'a, State>, - ) -> crate::Result { + async fn log<'a>(&'a self, mut req: Request, next: Next<'a>) -> crate::Result { if req.ext::().is_some() { return Ok(next.run(req).await); } @@ -94,8 +90,8 @@ impl LogMiddleware { } #[async_trait::async_trait] -impl Middleware for LogMiddleware { - async fn handle(&self, req: Request, next: Next<'_, State>) -> crate::Result { +impl Middleware for LogMiddleware { + async fn handle(&self, req: Request, next: Next<'_>) -> crate::Result { self.log(req, next).await } } diff --git a/src/middleware.rs b/src/middleware.rs index 20a32d74..86b24fea 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -10,9 +10,9 @@ use std::pin::Pin; /// Middleware that wraps around the remaining middleware chain. #[async_trait] -pub trait Middleware: Send + Sync + 'static { +pub trait Middleware: Send + Sync + 'static { /// Asynchronously handle the request, and return a response. - async fn handle(&self, request: Request, next: Next<'_, State>) -> crate::Result; + async fn handle(&self, request: Request, next: Next<'_>) -> crate::Result; /// Set the middleware's name. By default it uses the type signature. fn name(&self) -> &str { @@ -21,30 +21,26 @@ pub trait Middleware: Send + Sync + 'static { } #[async_trait] -impl Middleware for F +impl Middleware for F where - State: Clone + Send + Sync + 'static, F: Send + Sync + 'static - + for<'a> Fn( - Request, - Next<'a, State>, - ) -> Pin + 'a + Send>>, + + for<'a> Fn(Request, Next<'a>) -> Pin + 'a + Send>>, { - async fn handle(&self, req: Request, next: Next<'_, State>) -> crate::Result { + async fn handle(&self, req: Request, next: Next<'_>) -> crate::Result { (self)(req, next).await } } /// The remainder of a middleware chain, including the endpoint. #[allow(missing_debug_implementations)] -pub struct Next<'a, State> { +pub struct Next<'a> { pub(crate) endpoint: &'a DynEndpoint, - pub(crate) next_middleware: &'a [Arc>], + pub(crate) next_middleware: &'a [Arc], } -impl Next<'_, State> { +impl Next<'_> { /// Asynchronously execute the remaining middleware chain. pub async fn run(mut self, req: Request) -> Response { if let Some((current, next)) = self.next_middleware.split_first() { diff --git a/src/route.rs b/src/route.rs index 4401d181..253eb1e8 100644 --- a/src/route.rs +++ b/src/route.rs @@ -17,10 +17,10 @@ use crate::{router::Router, Endpoint, Middleware}; /// /// [`Server::at`]: ./struct.Server.html#method.at #[allow(missing_debug_implementations)] -pub struct Route<'a, State> { +pub struct Route<'a> { router: &'a mut Router, path: String, - middleware: Vec>>, + middleware: Vec>, /// Indicates whether the path of current route is treated as a prefix. Set by /// [`strip_prefix`]. /// @@ -28,8 +28,8 @@ pub struct Route<'a, State> { prefix: bool, } -impl<'a, State: Clone + Send + Sync + 'static> Route<'a, State> { - pub(crate) fn new(router: &'a mut Router, path: String) -> Route<'a, State> { +impl<'a> Route<'a> { + pub(crate) fn new(router: &'a mut Router, path: String) -> Route<'a> { Route { router, path, @@ -39,7 +39,7 @@ impl<'a, State: Clone + Send + Sync + 'static> Route<'a, State> { } /// Extend the route with the given `path`. - pub fn at<'b>(&'b mut self, path: &str) -> Route<'b, State> { + pub fn at<'b>(&'b mut self, path: &str) -> Route<'b> { let mut p = self.path.clone(); if !p.ends_with('/') && !path.starts_with('/') { @@ -79,7 +79,7 @@ impl<'a, State: Clone + Send + Sync + 'static> Route<'a, State> { /// Apply the given middleware to the current route. pub fn with(&mut self, middleware: M) -> &mut Self where - M: Middleware, + M: Middleware, { log::trace!( "Adding middleware {} to route {:?}", @@ -126,7 +126,6 @@ impl<'a, State: Clone + Send + Sync + 'static> Route<'a, State> { /// [`Server`]: struct.Server.html pub fn nest(&mut self, service: crate::Server) -> &mut Self where - State: Clone + Send + Sync + 'static, InnerState: Clone + Send + Sync + 'static, { let prefix = self.prefix; diff --git a/src/security/cors.rs b/src/security/cors.rs index b3e21a6c..d09c3b23 100644 --- a/src/security/cors.rs +++ b/src/security/cors.rs @@ -136,8 +136,8 @@ impl CorsMiddleware { } #[async_trait::async_trait] -impl Middleware for CorsMiddleware { - async fn handle(&self, req: Request, next: Next<'_, State>) -> Result { +impl Middleware for CorsMiddleware { + async fn handle(&self, req: Request, next: Next<'_>) -> Result { // TODO: how should multiple origin values be handled? let origins = req.header(&headers::ORIGIN).cloned(); diff --git a/src/server.rs b/src/server.rs index 88430181..c418407d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -37,7 +37,7 @@ pub struct Server { /// The inner Arc-s allow MiddlewareEndpoint-s to be cloned internally. /// We don't use a Mutex around the Vec here because adding a middleware during execution should be an error. #[allow(clippy::rc_buffer)] - middleware: Arc>>>, + middleware: Arc>>, } impl Server<()> { @@ -166,7 +166,7 @@ where /// There is no fallback route matching, i.e. either a resource is a full /// match or not, which means that the order of adding resources has no /// effect. - pub fn at<'a>(&'a mut self, path: &str) -> Route<'a, State> { + pub fn at<'a>(&'a mut self, path: &str) -> Route<'a> { let router = Arc::get_mut(&mut self.router) .expect("Registering routes is not possible after the Server has started"); Route::new(router, path.to_owned()) @@ -183,7 +183,7 @@ where /// order in which it is applied. pub fn with(&mut self, middleware: M) -> &mut Self where - M: Middleware, + M: Middleware, { log::trace!("Adding middleware {}", middleware.name()); let m = Arc::get_mut(&mut self.middleware) diff --git a/src/utils.rs b/src/utils.rs index acea7e20..08b266db 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -25,13 +25,12 @@ use std::future::Future; pub struct Before(pub F); #[async_trait] -impl Middleware for Before +impl Middleware for Before where - State: Clone + Send + Sync + 'static, F: Fn(Request) -> Fut + Send + Sync + 'static, Fut: Future + Send + Sync + 'static, { - async fn handle(&self, request: Request, next: Next<'_, State>) -> crate::Result { + async fn handle(&self, request: Request, next: Next<'_>) -> crate::Result { let request = (self.0)(request).await; Ok(next.run(request).await) } @@ -59,13 +58,12 @@ where #[derive(Debug)] pub struct After(pub F); #[async_trait] -impl Middleware for After +impl Middleware for After where - State: Clone + Send + Sync + 'static, F: Fn(Response) -> Fut + Send + Sync + 'static, Fut: Future + Send + Sync + 'static, { - async fn handle(&self, request: Request, next: Next<'_, State>) -> crate::Result { + async fn handle(&self, request: Request, next: Next<'_>) -> crate::Result { let response = next.run(request).await; (self.0)(response).await } diff --git a/tests/function_middleware.rs b/tests/function_middleware.rs index 2f288639..409e5147 100644 --- a/tests/function_middleware.rs +++ b/tests/function_middleware.rs @@ -6,7 +6,7 @@ mod test_utils; fn auth_middleware<'a>( request: tide::Request, - next: tide::Next<'a, ()>, + next: tide::Next<'a>, ) -> Pin + 'a + Send>> { let authenticated = match request.header("X-Auth") { Some(header) => header == "secret_key", diff --git a/tests/route_middleware.rs b/tests/route_middleware.rs index 157a96b0..ecc0e26a 100644 --- a/tests/route_middleware.rs +++ b/tests/route_middleware.rs @@ -14,11 +14,11 @@ impl TestMiddleware { } #[async_trait::async_trait] -impl Middleware for TestMiddleware { +impl Middleware for TestMiddleware { async fn handle( &self, req: tide::Request, - next: tide::Next<'_, State>, + next: tide::Next<'_>, ) -> tide::Result { let mut res = next.run(req).await; res.insert_header(self.0.clone(), self.1); From 5e1601949caba283da0e3c6eb47c2e94c6dcc65d Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Thu, 14 Jul 2022 23:09:35 -0500 Subject: [PATCH 04/20] Update docs on request state --- src/server.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server.rs b/src/server.rs index c418407d..953c0025 100644 --- a/src/server.rs +++ b/src/server.rs @@ -366,7 +366,9 @@ impl Endpoint for Server } } +/// Request extension trait that returns a reference to the State pub trait RequestState { + /// Extends the Request to be able to return a fn state(&self) -> &State; } From 4f77500ff7d5cb4dadf27cc08e8fa5238a8942ac Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Thu, 14 Jul 2022 23:11:35 -0500 Subject: [PATCH 05/20] Update readme to exclude request state --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c65eb89f..69b5bc81 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ async fn main() -> tide::Result<()> { Ok(()) } -async fn order_shoes(mut req: Request<()>) -> tide::Result { +async fn order_shoes(mut req: Request) -> tide::Result { let Animal { name, legs } = req.body_json().await?; Ok(format!("Hello, {}! I've put in an order for {} shoes", name, legs).into()) } From f607e07d3e5344419453cee2b4c091b85d2432d7 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Thu, 14 Jul 2022 23:23:03 -0500 Subject: [PATCH 06/20] Fix tests on disallowed primitive states --- tests/nested.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/nested.rs b/tests/nested.rs index 6fe00e8f..fb37301f 100644 --- a/tests/nested.rs +++ b/tests/nested.rs @@ -1,6 +1,6 @@ mod test_utils; use test_utils::ServerTestingExt; -use tide::RequestState; +use tide::{Request, RequestState}; #[async_std::test] async fn nested() -> tide::Result<()> { @@ -19,7 +19,7 @@ async fn nested() -> tide::Result<()> { #[async_std::test] async fn nested_middleware() -> tide::Result<()> { - let echo_path = |req: tide::Request| async move { Ok(req.url().path().to_string()) }; + let echo_path = |req: Request| async move { Ok(req.url().path().to_string()) }; let mut app = tide::new(); let mut inner_app = tide::new(); inner_app.with(tide::utils::After(|mut res: tide::Response| async move { @@ -48,12 +48,15 @@ async fn nested_middleware() -> tide::Result<()> { Ok(()) } +#[derive(Clone)] +struct Num(i32); + #[async_std::test] async fn nested_with_different_state() -> tide::Result<()> { let mut outer = tide::new(); - let mut inner = tide::with_state(42); - inner.at("/").get(|req: tide::Request| async move { - let num = req.state(); + let mut inner = tide::with_state(Num(42)); + inner.at("/").get(|req: Request| async move { + let num = req.state().0; Ok(format!("the number is {}", num)) }); outer.at("/").get(|_| async { Ok("Hello, world!") }); @@ -64,8 +67,8 @@ async fn nested_with_different_state() -> tide::Result<()> { Ok(()) } -impl RequestState for tide::Request { - fn state(&self) -> &i32 { - self.ext::().unwrap() +impl RequestState for Request { + fn state(&self) -> &Num { + self.ext::().unwrap() } } From 49d7a6dd1fe2be6322823136ddc73e560b975468 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Thu, 14 Jul 2022 23:39:14 -0500 Subject: [PATCH 07/20] Fix warning on symetrical boolean nonminimal bool --- src/security/cors.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/security/cors.rs b/src/security/cors.rs index d09c3b23..3111b43a 100644 --- a/src/security/cors.rs +++ b/src/security/cors.rs @@ -462,6 +462,7 @@ mod test { } #[test] + #[allow(clippy::nonminimal_bool)] fn symetrical() { let regex = Regex::new(r"e[xzs]a.*le.com*").unwrap(); let x = Origin::from(regex.clone()); From ff5f57e6843d91627755bcfb5383c640f8471786 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Fri, 15 Jul 2022 23:46:36 -0500 Subject: [PATCH 08/20] Update Next to use cursors with Arc references - Removes for<'a> and <'_> &'a lifetimes on Next - Changes all references to endpoints and middleware to use Arc - Updates selection routing to return Arc handlers instead of Boxed - Simplfy server respond to pass along endpoint and middleware --- examples/middleware.rs | 7 ++----- src/cookies/middleware.rs | 4 ++-- src/endpoint.rs | 21 +++++++++----------- src/log/middleware.rs | 4 ++-- src/middleware.rs | 37 ++++++++++++++++++++++-------------- src/route.rs | 8 ++++---- src/router.rs | 23 +++++++++++----------- src/security/cors.rs | 2 +- src/server.rs | 12 ++---------- src/sessions/middleware.rs | 2 +- src/utils.rs | 4 ++-- tests/function_middleware.rs | 6 +++--- tests/route_middleware.rs | 6 +----- 13 files changed, 64 insertions(+), 72 deletions(-) diff --git a/examples/middleware.rs b/examples/middleware.rs index c2aa05be..08efa494 100644 --- a/examples/middleware.rs +++ b/examples/middleware.rs @@ -25,10 +25,7 @@ impl UserDatabase { // 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>, -) -> Pin + Send + 'a>> { +fn user_loader(mut request: Request, next: Next) -> Pin + Send>> { Box::pin(async { if let Some(user) = request.state().find_user().await { tide::log::trace!("user loaded", {user: user.name}); @@ -62,7 +59,7 @@ struct RequestCount(usize); #[tide::utils::async_trait] impl Middleware for RequestCounterMiddleware { - async fn handle(&self, mut req: Request, next: Next<'_>) -> Result { + async fn handle(&self, mut req: Request, next: Next) -> Result { let count = self.requests_counted.fetch_add(1, Ordering::Relaxed); tide::log::trace!("request counter", { count: count }); req.set_ext(RequestCount(count)); diff --git a/src/cookies/middleware.rs b/src/cookies/middleware.rs index 98183ed5..12d0da85 100644 --- a/src/cookies/middleware.rs +++ b/src/cookies/middleware.rs @@ -35,8 +35,8 @@ impl CookiesMiddleware { } #[async_trait] -impl Middleware for CookiesMiddleware { - async fn handle(&self, mut ctx: Request, next: Next<'_, State>) -> crate::Result { +impl Middleware for CookiesMiddleware { + async fn handle(&self, mut ctx: Request, next: Next) -> crate::Result { let cookie_jar = if let Some(cookie_data) = ctx.ext::() { cookie_data.content.clone() } else { diff --git a/src/endpoint.rs b/src/endpoint.rs index 44712bc7..427430e0 100644 --- a/src/endpoint.rs +++ b/src/endpoint.rs @@ -67,8 +67,8 @@ where } pub(crate) struct MiddlewareEndpoint { - endpoint: E, - middleware: Vec>, + endpoint: Arc, + middleware: Arc>>, } impl Clone for MiddlewareEndpoint { @@ -96,14 +96,14 @@ where { pub(crate) fn wrap_with_middleware( ep: E, - middleware: &[Arc], - ) -> Box { + middleware: Vec>, + ) -> Arc { if middleware.is_empty() { - Box::new(ep) + Arc::new(ep) } else { - Box::new(Self { - endpoint: ep, - middleware: middleware.to_vec(), + Arc::new(Self { + endpoint: Arc::new(ep), + middleware: Arc::new(middleware), }) } } @@ -115,10 +115,7 @@ where E: Endpoint, { async fn call(&self, req: Request) -> crate::Result { - let next = Next { - endpoint: &self.endpoint, - next_middleware: &self.middleware, - }; + let next = Next::new(self.endpoint.clone(), self.middleware.clone()); Ok(next.run(req).await) } } diff --git a/src/log/middleware.rs b/src/log/middleware.rs index 3a6ee7f7..2cfe9002 100644 --- a/src/log/middleware.rs +++ b/src/log/middleware.rs @@ -26,7 +26,7 @@ impl LogMiddleware { } /// Log a request and a response. - async fn log<'a>(&'a self, mut req: Request, next: Next<'a>) -> crate::Result { + async fn log(&self, mut req: Request, next: Next) -> crate::Result { if req.ext::().is_some() { return Ok(next.run(req).await); } @@ -91,7 +91,7 @@ impl LogMiddleware { #[async_trait::async_trait] impl Middleware for LogMiddleware { - async fn handle(&self, req: Request, next: Next<'_>) -> crate::Result { + async fn handle(&self, req: Request, next: Next) -> crate::Result { self.log(req, next).await } } diff --git a/src/middleware.rs b/src/middleware.rs index 86b24fea..eb070813 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -2,8 +2,7 @@ use std::sync::Arc; -use crate::endpoint::DynEndpoint; -use crate::{Request, Response}; +use crate::{Endpoint, Request, Response}; use async_trait::async_trait; use std::future::Future; use std::pin::Pin; @@ -12,7 +11,7 @@ use std::pin::Pin; #[async_trait] pub trait Middleware: Send + Sync + 'static { /// Asynchronously handle the request, and return a response. - async fn handle(&self, request: Request, next: Next<'_>) -> crate::Result; + async fn handle(&self, request: Request, next: Next) -> crate::Result; /// Set the middleware's name. By default it uses the type signature. fn name(&self) -> &str { @@ -26,32 +25,42 @@ where F: Send + Sync + 'static - + for<'a> Fn(Request, Next<'a>) -> Pin + 'a + Send>>, + + Fn(Request, Next) -> Pin + Send>>, { - async fn handle(&self, req: Request, next: Next<'_>) -> crate::Result { + async fn handle(&self, req: Request, next: Next) -> crate::Result { (self)(req, next).await } } /// The remainder of a middleware chain, including the endpoint. #[allow(missing_debug_implementations)] -pub struct Next<'a> { - pub(crate) endpoint: &'a DynEndpoint, - pub(crate) next_middleware: &'a [Arc], +pub struct Next { + cursor: usize, + endpoint: Arc, + middleware: Arc>>, } -impl Next<'_> { +impl Next { + /// Creates a new Next middleware with an arc to the endpoint and middleware + pub fn new(endpoint: Arc, middleware: Arc>>) -> Self { + Self { + cursor: 0, + endpoint, + middleware, + } + } + /// Asynchronously execute the remaining middleware chain. pub async fn run(mut self, req: Request) -> Response { - if let Some((current, next)) = self.next_middleware.split_first() { - self.next_middleware = next; - match current.handle(req, self).await { - Ok(request) => request, + if let Some(mid) = self.middleware.get(self.cursor) { + self.cursor += 1; + match mid.to_owned().handle(req, self).await { + Ok(response) => response, Err(err) => err.into(), } } else { match self.endpoint.call(req).await { - Ok(request) => request, + Ok(response) => response, Err(err) => err.into(), } } diff --git a/src/route.rs b/src/route.rs index 253eb1e8..13e8d9db 100644 --- a/src/route.rs +++ b/src/route.rs @@ -187,13 +187,13 @@ impl<'a> Route<'a> { wildcard.router.add( &wildcard.path, method, - MiddlewareEndpoint::wrap_with_middleware(ep, &wildcard.middleware), + MiddlewareEndpoint::wrap_with_middleware(ep, wildcard.middleware.clone()), ); } else { self.router.add( &self.path, method, - MiddlewareEndpoint::wrap_with_middleware(ep, &self.middleware), + MiddlewareEndpoint::wrap_with_middleware(ep, self.middleware.clone()), ); } self @@ -208,12 +208,12 @@ impl<'a> Route<'a> { let wildcard = self.at("*"); wildcard.router.add_all( &wildcard.path, - MiddlewareEndpoint::wrap_with_middleware(ep, &wildcard.middleware), + MiddlewareEndpoint::wrap_with_middleware(ep, wildcard.middleware.clone()), ); } else { self.router.add_all( &self.path, - MiddlewareEndpoint::wrap_with_middleware(ep, &self.middleware), + MiddlewareEndpoint::wrap_with_middleware(ep, self.middleware.clone()), ); } self diff --git a/src/router.rs b/src/router.rs index a1ec4e52..3334eb61 100644 --- a/src/router.rs +++ b/src/router.rs @@ -1,5 +1,6 @@ use routefinder::{Captures, Router as MethodRouter}; use std::collections::HashMap; +use std::sync::Arc; use crate::endpoint::DynEndpoint; use crate::{Request, Response, StatusCode}; @@ -10,8 +11,8 @@ use crate::{Request, Response, StatusCode}; /// by the method first allows the table itself to be more efficient. #[allow(missing_debug_implementations)] pub(crate) struct Router { - method_map: HashMap>>, - all_method_router: MethodRouter>, + method_map: HashMap>>, + all_method_router: MethodRouter>, } impl std::fmt::Debug for Router { @@ -24,8 +25,8 @@ impl std::fmt::Debug for Router { } /// The result of routing a URL -pub(crate) struct Selection<'a> { - pub(crate) endpoint: &'a DynEndpoint, +pub(crate) struct Selection { + pub(crate) endpoint: Arc, pub(crate) params: Captures<'static, 'static>, } @@ -37,7 +38,7 @@ impl Router { } } - pub(crate) fn add(&mut self, path: &str, method: http_types::Method, ep: Box) { + pub(crate) fn add(&mut self, path: &str, method: http_types::Method, ep: Arc) { self.method_map .entry(method) .or_insert_with(MethodRouter::new) @@ -45,23 +46,23 @@ impl Router { .unwrap() } - pub(crate) fn add_all(&mut self, path: &str, ep: Box) { + pub(crate) fn add_all(&mut self, path: &str, ep: Arc) { self.all_method_router.add(path, ep).unwrap() } - pub(crate) fn route(&self, path: &str, method: http_types::Method) -> Selection<'_> { + pub(crate) fn route(&self, path: &str, method: http_types::Method) -> Selection { if let Some(m) = self .method_map .get(&method) .and_then(|r| r.best_match(path)) { Selection { - endpoint: m.handler(), + endpoint: m.handler().to_owned(), params: m.captures().into_owned(), } } else if let Some(m) = self.all_method_router.best_match(path) { Selection { - endpoint: m.handler(), + endpoint: m.handler().to_owned(), params: m.captures().into_owned(), } } else if method == http_types::Method::Head { @@ -78,12 +79,12 @@ impl Router { // If this `path` can be handled by a callback registered with a different HTTP method // should return 405 Method Not Allowed Selection { - endpoint: &method_not_allowed, + endpoint: Arc::new(method_not_allowed), params: Captures::default(), } } else { Selection { - endpoint: ¬_found_endpoint, + endpoint: Arc::new(not_found_endpoint), params: Captures::default(), } } diff --git a/src/security/cors.rs b/src/security/cors.rs index 3111b43a..78e65b1d 100644 --- a/src/security/cors.rs +++ b/src/security/cors.rs @@ -137,7 +137,7 @@ impl CorsMiddleware { #[async_trait::async_trait] impl Middleware for CorsMiddleware { - async fn handle(&self, req: Request, next: Next<'_>) -> Result { + async fn handle(&self, req: Request, next: Next) -> Result { // TODO: how should multiple origin values be handled? let origins = req.header(&headers::ORIGIN).cloned(); diff --git a/src/server.rs b/src/server.rs index 953c0025..03e95a35 100644 --- a/src/server.rs +++ b/src/server.rs @@ -297,11 +297,7 @@ where let route_params = vec![params]; let req = Request::with_state(state, req, route_params); - let next = Next { - endpoint, - next_middleware: &middleware, - }; - + let next = Next::new(endpoint, middleware); let res = next.run(req).await; let res: http_types::Response = res.into(); Ok(res.into()) @@ -357,11 +353,7 @@ impl Endpoint for Server route_params.push(params); let req = Request::with_state(state, req, route_params); - let next = Next { - endpoint, - next_middleware: &middleware, - }; - + let next = Next::new(endpoint, middleware); Ok(next.run(req).await) } } diff --git a/src/sessions/middleware.rs b/src/sessions/middleware.rs index 12b88229..90de8f28 100644 --- a/src/sessions/middleware.rs +++ b/src/sessions/middleware.rs @@ -79,7 +79,7 @@ where Store: SessionStore, State: Clone + Send + Sync + 'static, { - async fn handle(&self, mut request: Request, next: Next<'_, State>) -> crate::Result { + async fn handle(&self, mut request: Request, next: Next) -> crate::Result { let cookie = request.cookie(&self.cookie_name); let cookie_value = cookie .clone() diff --git a/src/utils.rs b/src/utils.rs index 08b266db..0832f3a0 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -30,7 +30,7 @@ where F: Fn(Request) -> Fut + Send + Sync + 'static, Fut: Future + Send + Sync + 'static, { - async fn handle(&self, request: Request, next: Next<'_>) -> crate::Result { + async fn handle(&self, request: Request, next: Next) -> crate::Result { let request = (self.0)(request).await; Ok(next.run(request).await) } @@ -63,7 +63,7 @@ where F: Fn(Response) -> Fut + Send + Sync + 'static, Fut: Future + Send + Sync + 'static, { - async fn handle(&self, request: Request, next: Next<'_>) -> crate::Result { + async fn handle(&self, request: Request, next: Next) -> crate::Result { let response = next.run(request).await; (self.0)(response).await } diff --git a/tests/function_middleware.rs b/tests/function_middleware.rs index 409e5147..b6d36fda 100644 --- a/tests/function_middleware.rs +++ b/tests/function_middleware.rs @@ -4,10 +4,10 @@ use tide::http::{self, url::Url, Method}; mod test_utils; -fn auth_middleware<'a>( +fn auth_middleware( request: tide::Request, - next: tide::Next<'a>, -) -> Pin + 'a + Send>> { + next: tide::Next, +) -> Pin + Send>> { let authenticated = match request.header("X-Auth") { Some(header) => header == "secret_key", None => false, diff --git a/tests/route_middleware.rs b/tests/route_middleware.rs index ecc0e26a..c815288b 100644 --- a/tests/route_middleware.rs +++ b/tests/route_middleware.rs @@ -15,11 +15,7 @@ impl TestMiddleware { #[async_trait::async_trait] impl Middleware for TestMiddleware { - async fn handle( - &self, - req: tide::Request, - next: tide::Next<'_>, - ) -> tide::Result { + async fn handle(&self, req: tide::Request, next: tide::Next) -> tide::Result { let mut res = next.run(req).await; res.insert_header(self.0.clone(), self.1); Ok(res) From b19de6f832e633e8e17a186b6f7d7278719829c5 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Sat, 16 Jul 2022 00:06:59 -0500 Subject: [PATCH 09/20] Add support for impl Middleware to allow closure middleware --- examples/middleware.rs | 26 +++++++++++--------------- src/middleware.rs | 14 +++++++------- src/server.rs | 5 +---- tests/function_middleware.rs | 19 ++++++------------- 4 files changed, 25 insertions(+), 39 deletions(-) diff --git a/examples/middleware.rs b/examples/middleware.rs index 08efa494..e706e2ce 100644 --- a/examples/middleware.rs +++ b/examples/middleware.rs @@ -1,5 +1,3 @@ -use std::future::Future; -use std::pin::Pin; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -25,19 +23,17 @@ impl UserDatabase { // 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(mut request: Request, next: Next) -> Pin + Send>> { - Box::pin(async { - if let Some(user) = request.state().find_user().await { - tide::log::trace!("user loaded", {user: user.name}); - request.set_ext(user); - Ok(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)) - } - }) +async fn user_loader(mut request: Request, next: Next) -> Result { + if let Some(user) = request.state().find_user().await { + tide::log::trace!("user loaded", {user: user.name}); + request.set_ext(user); + Ok(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 diff --git a/src/middleware.rs b/src/middleware.rs index eb070813..b6fb3b8c 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -5,7 +5,6 @@ use std::sync::Arc; use crate::{Endpoint, Request, Response}; use async_trait::async_trait; use std::future::Future; -use std::pin::Pin; /// Middleware that wraps around the remaining middleware chain. #[async_trait] @@ -20,15 +19,16 @@ pub trait Middleware: Send + Sync + 'static { } #[async_trait] -impl Middleware for F +impl Middleware for F where - F: Send - + Sync - + 'static - + Fn(Request, Next) -> Pin + Send>>, + F: Send + Sync + 'static + Fn(Request, Next) -> Fut, + Fut: Future> + Send + 'static, + Res: Into + 'static, { async fn handle(&self, req: Request, next: Next) -> crate::Result { - (self)(req, next).await + let fut = (self)(req, next); + let res = fut.await?; + Ok(res.into()) } } diff --git a/src/server.rs b/src/server.rs index 03e95a35..e62e658f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -181,10 +181,7 @@ where /// /// Middleware can only be added at the "top level" of an application, and is processed in the /// order in which it is applied. - pub fn with(&mut self, middleware: M) -> &mut Self - where - M: Middleware, - { + pub fn with(&mut self, middleware: impl Middleware) -> &mut Self { 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"); diff --git a/tests/function_middleware.rs b/tests/function_middleware.rs index b6d36fda..b41d374b 100644 --- a/tests/function_middleware.rs +++ b/tests/function_middleware.rs @@ -1,25 +1,18 @@ -use std::future::Future; -use std::pin::Pin; use tide::http::{self, url::Url, Method}; mod test_utils; -fn auth_middleware( - request: tide::Request, - next: tide::Next, -) -> Pin + Send>> { +async fn auth_middleware(request: tide::Request, next: tide::Next) -> tide::Result { let authenticated = match request.header("X-Auth") { Some(header) => header == "secret_key", None => false, }; - Box::pin(async move { - if authenticated { - Ok(next.run(request).await) - } else { - Ok(tide::Response::new(tide::StatusCode::Unauthorized)) - } - }) + if authenticated { + Ok(next.run(request).await) + } else { + Ok(tide::Response::new(tide::StatusCode::Unauthorized)) + } } async fn echo_path(req: tide::Request) -> tide::Result { From 41afeb9ed66f33976a789baeac84a0cb6dc48076 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Sat, 16 Jul 2022 00:09:57 -0500 Subject: [PATCH 10/20] Add support for impl Middleware in routes --- src/route.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/route.rs b/src/route.rs index 13e8d9db..1374fb81 100644 --- a/src/route.rs +++ b/src/route.rs @@ -77,10 +77,7 @@ impl<'a> Route<'a> { } /// Apply the given middleware to the current route. - pub fn with(&mut self, middleware: M) -> &mut Self - where - M: Middleware, - { + pub fn with(&mut self, middleware: impl Middleware) -> &mut Self { log::trace!( "Adding middleware {} to route {:?}", middleware.name(), From ac0abd37bd5e3bb6088fadf06336921b6104674d Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Mon, 18 Jul 2022 14:51:55 -0500 Subject: [PATCH 11/20] Erase type on server state and place in Arc any hashmap --- Cargo.toml | 1 + examples/graphql.rs | 10 +- examples/middleware.rs | 10 +- examples/state.rs | 12 +-- examples/upload.rs | 12 +-- src/endpoint.rs | 2 +- src/lib.rs | 17 ++-- src/listener/concurrent_listener.rs | 21 ++-- src/listener/failover_listener.rs | 24 ++--- src/listener/mod.rs | 15 +-- src/listener/parsed_listener.rs | 17 ++-- src/listener/tcp_listener.rs | 19 ++-- src/listener/to_listener.rs | 4 +- src/listener/to_listener_impls.rs | 142 ++++++++-------------------- src/listener/unix_listener.rs | 19 ++-- src/request.rs | 21 +++- src/route.rs | 14 ++- src/security/cors.rs | 2 +- src/server.rs | 81 ++++++---------- src/sessions/middleware.rs | 3 +- src/sse/endpoint.rs | 11 +-- src/sse/upgrade.rs | 3 +- src/state.rs | 76 +++++++++++++++ tests/nested.rs | 13 +-- tests/serve_dir.rs | 2 +- tests/test_utils.rs | 2 +- 26 files changed, 247 insertions(+), 306 deletions(-) create mode 100644 src/state.rs diff --git a/Cargo.toml b/Cargo.toml index 3a0f7ee9..7da6f38b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ serde = "1.0.117" serde_json = "1.0.59" routefinder = "0.5.0" regex = "1.5.5" +hashbrown = "0.12.3" [dev-dependencies] async-std = { version = "1.6.5", features = ["unstable", "attributes"] } diff --git a/examples/graphql.rs b/examples/graphql.rs index eee046ce..13db866c 100644 --- a/examples/graphql.rs +++ b/examples/graphql.rs @@ -2,7 +2,7 @@ use std::sync::{Arc, RwLock}; use juniper::{http::graphiql, http::GraphQLRequest, RootNode}; use lazy_static::lazy_static; -use tide::{http::mime, Body, Redirect, Request, RequestState, Response, Server, StatusCode}; +use tide::{http::mime, Body, Redirect, Request, Response, Server, StatusCode}; #[derive(Clone)] struct User { @@ -76,7 +76,7 @@ lazy_static! { async fn handle_graphql(mut request: Request) -> tide::Result { let query: GraphQLRequest = request.body_json().await?; - let response = query.execute(&SCHEMA, request.state()); + let response = query.execute(&SCHEMA, request.state::()); let status = if response.is_ok() { StatusCode::Ok } else { @@ -105,9 +105,3 @@ async fn main() -> std::io::Result<()> { app.listen("0.0.0.0:8080").await?; Ok(()) } - -impl RequestState for Request { - fn state(&self) -> &State { - self.ext::().unwrap() - } -} diff --git a/examples/middleware.rs b/examples/middleware.rs index e706e2ce..3e0589b8 100644 --- a/examples/middleware.rs +++ b/examples/middleware.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use tide::http::mime; use tide::utils::{After, Before}; -use tide::{Middleware, Next, Request, RequestState, Response, Result, StatusCode}; +use tide::{Middleware, Next, Request, Response, Result, StatusCode}; #[derive(Debug)] struct User { @@ -24,7 +24,7 @@ impl UserDatabase { // application state. Because it depends on a specific request state, // it would likely be closely tied to a specific application async fn user_loader(mut request: Request, next: Next) -> Result { - if let Some(user) = request.state().find_user().await { + if let Some(user) = request.state::().find_user().await { tide::log::trace!("user loaded", {user: user.name}); request.set_ext(user); Ok(next.run(request).await) @@ -125,9 +125,3 @@ async fn main() -> Result<()> { app.listen("127.0.0.1:8080").await?; Ok(()) } - -impl RequestState for Request { - fn state(&self) -> &UserDatabase { - self.ext::().unwrap() - } -} diff --git a/examples/state.rs b/examples/state.rs index 95fc0b53..c25aac40 100644 --- a/examples/state.rs +++ b/examples/state.rs @@ -1,8 +1,6 @@ use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; -use tide::RequestState; - #[derive(Clone)] struct State { value: Arc, @@ -22,21 +20,15 @@ async fn main() -> tide::Result<()> { let mut app = tide::with_state(State::new()); app.with(tide::log::LogMiddleware::new()); app.at("/").get(|req: tide::Request| async move { - let state = req.state(); + let state = req.state::(); let value = state.value.load(Ordering::Relaxed); Ok(format!("{}\n", value)) }); app.at("/inc").get(|req: tide::Request| async move { - let state = req.state(); + let state = req.state::(); let value = state.value.fetch_add(1, Ordering::Relaxed) + 1; Ok(format!("{}\n", value)) }); app.listen("127.0.0.1:8080").await?; Ok(()) } - -impl RequestState for tide::Request { - fn state(&self) -> &State { - self.ext::().unwrap() - } -} diff --git a/examples/upload.rs b/examples/upload.rs index 84344ef7..42d44c88 100644 --- a/examples/upload.rs +++ b/examples/upload.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use async_std::{fs::OpenOptions, io}; use tempfile::TempDir; use tide::prelude::*; -use tide::{Body, Request, RequestState, Response, StatusCode}; +use tide::{Body, Request, Response, StatusCode}; #[derive(Clone)] struct TempDirState { @@ -24,12 +24,6 @@ impl TempDirState { } } -impl RequestState for Request { - fn state(&self) -> &TempDirState { - self.ext::().unwrap() - } -} - #[async_std::main] async fn main() -> Result<(), IoError> { // tide::log::start(); @@ -44,7 +38,7 @@ async fn main() -> Result<(), IoError> { app.at(":file") .put(|req: Request| async move { let path = req.param("file")?; - let state = req.state(); + let state = req.state::(); let fs_path = state.path().join(path); let file = OpenOptions::new() @@ -64,7 +58,7 @@ async fn main() -> Result<(), IoError> { }) .get(|req: Request| async move { let path = req.param("file")?; - let fs_path = req.state().path().join(path); + let fs_path = req.state::().path().join(path); if let Ok(body) = Body::from_file(fs_path).await { Ok(body.into()) diff --git a/src/endpoint.rs b/src/endpoint.rs index 427430e0..6b3b2ca2 100644 --- a/src/endpoint.rs +++ b/src/endpoint.rs @@ -11,7 +11,7 @@ use crate::{Middleware, Request, Response}; /// This trait is automatically implemented for `Fn` types, and so is rarely implemented /// directly by Tide users. /// -/// In practice, endpoints are functions that take a `Request` as an argument and +/// In practice, endpoints are functions that take a `Request` as an argument and /// return a type `T` that implements `Into`. /// /// # Examples diff --git a/src/lib.rs b/src/lib.rs index 26771a43..c651bd37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,6 +77,7 @@ mod response_builder; mod route; mod router; mod server; +mod state; pub mod convert; pub mod listener; @@ -97,8 +98,8 @@ pub use request::Request; pub use response::Response; pub use response_builder::ResponseBuilder; pub use route::Route; -pub use server::RequestState; pub use server::Server; +pub use state::State; pub use http_types::{self as http, Body, Error, Status, StatusCode}; @@ -117,7 +118,7 @@ pub use http_types::{self as http, Body, Error, Status, StatusCode}; /// # Ok(()) }) } /// ``` #[must_use] -pub fn new() -> server::Server<()> { +pub fn new() -> server::Server { Server::new() } @@ -131,7 +132,7 @@ pub fn new() -> server::Server<()> { /// # use async_std::task::block_on; /// # fn main() -> Result<(), std::io::Error> { block_on(async { /// # -/// use tide::{Request, RequestState}; +/// use tide::{Request}; /// /// /// The shared application state. /// #[derive(Clone)] @@ -144,22 +145,16 @@ pub fn new() -> server::Server<()> { /// name: "Nori".to_string() /// }; /// -/// impl RequestState for Request { -/// fn state(&self) -> &State { -/// self.ext::().unwrap() -/// } -/// } -/// /// // Initialize the application with state. /// let mut app = tide::with_state(state); /// app.at("/").get(|req: Request| async move { -/// Ok(format!("Hello, {}!", &req.state().name)) +/// Ok(format!("Hello, {}!", &req.state::().name)) /// }); /// app.listen("127.0.0.1:8080").await?; /// # /// # Ok(()) }) } /// ``` -pub fn with_state(state: State) -> server::Server +pub fn with_state(state: State) -> server::Server where State: Clone + Send + Sync + 'static, { diff --git a/src/listener/concurrent_listener.rs b/src/listener/concurrent_listener.rs index 0cc52bd8..5d59cfb2 100644 --- a/src/listener/concurrent_listener.rs +++ b/src/listener/concurrent_listener.rs @@ -33,11 +33,11 @@ use futures_util::stream::{futures_unordered::FuturesUnordered, StreamExt}; ///``` #[derive(Default)] -pub struct ConcurrentListener { - listeners: Vec>>, +pub struct ConcurrentListener { + listeners: Vec>, } -impl ConcurrentListener { +impl ConcurrentListener { /// creates a new ConcurrentListener pub fn new() -> Self { Self { listeners: vec![] } @@ -59,7 +59,7 @@ impl ConcurrentListener { /// ``` pub fn add(&mut self, listener: L) -> io::Result<()> where - L: ToListener, + L: ToListener, { self.listeners.push(Box::new(listener.to_listener()?)); Ok(()) @@ -78,7 +78,7 @@ impl ConcurrentListener { /// # Ok(()) }) } pub fn with_listener(mut self, listener: L) -> Self where - L: ToListener, + L: ToListener, { self.add(listener).expect("Unable to add listener"); self @@ -86,11 +86,8 @@ impl ConcurrentListener { } #[async_trait::async_trait] -impl Listener for ConcurrentListener -where - State: Clone + Send + Sync + 'static, -{ - async fn bind(&mut self, app: Server) -> io::Result<()> { +impl Listener for ConcurrentListener { + async fn bind(&mut self, app: Server) -> io::Result<()> { for listener in self.listeners.iter_mut() { listener.bind(app.clone()).await?; } @@ -118,13 +115,13 @@ where } } -impl Debug for ConcurrentListener { +impl Debug for ConcurrentListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.listeners) } } -impl Display for ConcurrentListener { +impl Display for ConcurrentListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let string = self .listeners diff --git a/src/listener/failover_listener.rs b/src/listener/failover_listener.rs index d4eea4d3..4fe37c2a 100644 --- a/src/listener/failover_listener.rs +++ b/src/listener/failover_listener.rs @@ -34,15 +34,12 @@ use crate::listener::ListenInfo; ///} ///``` #[derive(Default)] -pub struct FailoverListener { - listeners: Vec>>>, +pub struct FailoverListener { + listeners: Vec>>, index: Option, } -impl FailoverListener -where - State: Clone + Send + Sync + 'static, -{ +impl FailoverListener { /// creates a new FailoverListener pub fn new() -> Self { Self { @@ -69,7 +66,7 @@ where /// ``` pub fn add(&mut self, listener: L) -> io::Result<()> where - L: ToListener, + L: ToListener, { self.listeners.push(Some(Box::new(listener.to_listener()?))); Ok(()) @@ -88,7 +85,7 @@ where /// # Ok(()) }) } pub fn with_listener(mut self, listener: L) -> Self where - L: ToListener, + L: ToListener, { self.add(listener).expect("Unable to add listener"); self @@ -96,11 +93,8 @@ where } #[async_trait::async_trait] -impl Listener for FailoverListener -where - State: Clone + Send + Sync + 'static, -{ - async fn bind(&mut self, app: Server) -> io::Result<()> { +impl Listener for FailoverListener { + async fn bind(&mut self, app: Server) -> io::Result<()> { for (index, listener) in self.listeners.iter_mut().enumerate() { let listener = listener.as_deref_mut().expect("bind called twice"); match listener.bind(app.clone()).await { @@ -148,13 +142,13 @@ where } } -impl Debug for FailoverListener { +impl Debug for FailoverListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.listeners) } } -impl Display for FailoverListener { +impl Display for FailoverListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let string = self .listeners diff --git a/src/listener/mod.rs b/src/listener/mod.rs index 2d469872..2adb965f 100644 --- a/src/listener/mod.rs +++ b/src/listener/mod.rs @@ -35,14 +35,11 @@ pub(crate) use unix_listener::UnixListener; /// implement at least one [`ToListener`](crate::listener::ToListener) that /// outputs your Listener type. #[async_trait] -pub trait Listener: Debug + Display + Send + Sync + 'static -where - State: Send + Sync + 'static, -{ +pub trait Listener: Debug + Display + Send + Sync + 'static { /// Bind the listener. This starts the listening process by opening the /// necessary network ports, but not yet accepting incoming connections. This /// method must be called before `accept`. - async fn bind(&mut self, app: Server) -> io::Result<()>; + async fn bind(&mut self, app: Server) -> io::Result<()>; /// Start accepting incoming connections. This method must be called only /// after `bind` has succeeded. @@ -54,12 +51,8 @@ where } #[async_trait] -impl Listener for Box -where - L: Listener, - State: Send + Sync + 'static, -{ - async fn bind(&mut self, app: Server) -> io::Result<()> { +impl Listener for Box { + async fn bind(&mut self, app: Server) -> io::Result<()> { self.as_mut().bind(app).await } diff --git a/src/listener/parsed_listener.rs b/src/listener/parsed_listener.rs index ad2926a1..82143eac 100644 --- a/src/listener/parsed_listener.rs +++ b/src/listener/parsed_listener.rs @@ -13,13 +13,13 @@ use std::fmt::{self, Debug, Display, Formatter}; /// /// This is currently crate-visible only, and tide users are expected /// to create these through [ToListener](crate::ToListener) conversions. -pub enum ParsedListener { +pub enum ParsedListener { #[cfg(unix)] - Unix(UnixListener), - Tcp(TcpListener), + Unix(UnixListener), + Tcp(TcpListener), } -impl Debug for ParsedListener { +impl Debug for ParsedListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { #[cfg(unix)] @@ -29,7 +29,7 @@ impl Debug for ParsedListener { } } -impl Display for ParsedListener { +impl Display for ParsedListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { #[cfg(unix)] @@ -40,11 +40,8 @@ impl Display for ParsedListener { } #[async_trait::async_trait] -impl Listener for ParsedListener -where - State: Clone + Send + Sync + 'static, -{ - async fn bind(&mut self, server: Server) -> io::Result<()> { +impl Listener for ParsedListener { + async fn bind(&mut self, server: Server) -> io::Result<()> { match self { #[cfg(unix)] Self::Unix(u) => u.bind(server).await, diff --git a/src/listener/tcp_listener.rs b/src/listener/tcp_listener.rs index 7b86a013..90f2dde4 100644 --- a/src/listener/tcp_listener.rs +++ b/src/listener/tcp_listener.rs @@ -17,14 +17,14 @@ use async_std::{io, task}; /// /// This is currently crate-visible only, and tide users are expected /// to create these through [ToListener](crate::ToListener) conversions. -pub struct TcpListener { +pub struct TcpListener { addrs: Option>, listener: Option, - server: Option>, + server: Option, info: Option, } -impl TcpListener { +impl TcpListener { pub fn from_addrs(addrs: Vec) -> Self { Self { addrs: Some(addrs), @@ -44,7 +44,7 @@ impl TcpListener { } } -fn handle_tcp(app: Server, stream: TcpStream) { +fn handle_tcp(app: Server, stream: TcpStream) { task::spawn(async move { let local_addr = stream.local_addr().ok(); let peer_addr = stream.peer_addr().ok(); @@ -62,11 +62,8 @@ fn handle_tcp(app: Server, stream: } #[async_trait::async_trait] -impl Listener for TcpListener -where - State: Clone + Send + Sync + 'static, -{ - async fn bind(&mut self, server: Server) -> io::Result<()> { +impl Listener for TcpListener { + async fn bind(&mut self, server: Server) -> io::Result<()> { assert!(self.server.is_none(), "`bind` should only be called once"); self.server = Some(server); @@ -126,7 +123,7 @@ where } } -impl fmt::Debug for TcpListener { +impl fmt::Debug for TcpListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("TcpListener") .field("listener", &self.listener) @@ -143,7 +140,7 @@ impl fmt::Debug for TcpListener { } } -impl Display for TcpListener { +impl Display for TcpListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let http_fmt = |a| format!("http://{}", a); match &self.listener { diff --git a/src/listener/to_listener.rs b/src/listener/to_listener.rs index b25b8f36..79b125c3 100644 --- a/src/listener/to_listener.rs +++ b/src/listener/to_listener.rs @@ -47,9 +47,9 @@ use async_std::io; /// ``` /// # Other implementations /// See below for additional provided implementations of ToListener. -pub trait ToListener { +pub trait ToListener { /// What listener are we converting into? - type Listener: Listener; + type Listener: Listener; /// Transform self into a /// [`Listener`](crate::listener::Listener). Unless self is diff --git a/src/listener/to_listener_impls.rs b/src/listener/to_listener_impls.rs index 1e92fef0..bcf80a83 100644 --- a/src/listener/to_listener_impls.rs +++ b/src/listener/to_listener_impls.rs @@ -5,11 +5,8 @@ use crate::http::url::Url; use async_std::io; use std::net::ToSocketAddrs; -impl ToListener for Url -where - State: Clone + Send + Sync + 'static, -{ - type Listener = ParsedListener; +impl ToListener for Url { + type Listener = ParsedListener; fn to_listener(self) -> io::Result { match self.scheme() { @@ -51,31 +48,22 @@ where } } -impl ToListener for String -where - State: Clone + Send + Sync + 'static, -{ - type Listener = ParsedListener; +impl ToListener for String { + type Listener = ParsedListener; fn to_listener(self) -> io::Result { - ToListener::::to_listener(self.as_str()) + ToListener::to_listener(self.as_str()) } } -impl ToListener for &String -where - State: Clone + Send + Sync + 'static, -{ - type Listener = ParsedListener; +impl ToListener for &String { + type Listener = ParsedListener; fn to_listener(self) -> io::Result { - ToListener::::to_listener(self.as_str()) + ToListener::to_listener(self.as_str()) } } -impl ToListener for &str -where - State: Clone + Send + Sync + 'static, -{ - type Listener = ParsedListener; +impl ToListener for &str { + type Listener = ParsedListener; fn to_listener(self) -> io::Result { if let Ok(socket_addrs) = self.to_socket_addrs() { @@ -83,7 +71,7 @@ where socket_addrs.collect(), ))) } else if let Ok(url) = Url::parse(self) { - ToListener::::to_listener(url) + ToListener::to_listener(url) } else { Err(io::Error::new( io::ErrorKind::InvalidInput, @@ -94,72 +82,51 @@ where } #[cfg(unix)] -impl ToListener for async_std::path::PathBuf -where - State: Clone + Send + Sync + 'static, -{ - type Listener = UnixListener; +impl ToListener for async_std::path::PathBuf { + type Listener = UnixListener; fn to_listener(self) -> io::Result { Ok(UnixListener::from_path(self)) } } #[cfg(unix)] -impl ToListener for std::path::PathBuf -where - State: Clone + Send + Sync + 'static, -{ - type Listener = UnixListener; +impl ToListener for std::path::PathBuf { + type Listener = UnixListener; fn to_listener(self) -> io::Result { Ok(UnixListener::from_path(self)) } } -impl ToListener for async_std::net::TcpListener -where - State: Clone + Send + Sync + 'static, -{ - type Listener = TcpListener; +impl ToListener for async_std::net::TcpListener { + type Listener = TcpListener; fn to_listener(self) -> io::Result { Ok(TcpListener::from_listener(self)) } } -impl ToListener for std::net::TcpListener -where - State: Clone + Send + Sync + 'static, -{ - type Listener = TcpListener; +impl ToListener for std::net::TcpListener { + type Listener = TcpListener; fn to_listener(self) -> io::Result { Ok(TcpListener::from_listener(self)) } } -impl ToListener for (String, u16) -where - State: Clone + Send + Sync + 'static, -{ - type Listener = TcpListener; +impl ToListener for (String, u16) { + type Listener = TcpListener; fn to_listener(self) -> io::Result { - ToListener::::to_listener((self.0.as_str(), self.1)) + ToListener::to_listener((self.0.as_str(), self.1)) } } -impl ToListener for (&String, u16) -where - State: Clone + Send + Sync + 'static, -{ - type Listener = TcpListener; +impl ToListener for (&String, u16) { + type Listener = TcpListener; fn to_listener(self) -> io::Result { - ToListener::::to_listener((self.0.as_str(), self.1)) + ToListener::to_listener((self.0.as_str(), self.1)) } } -impl ToListener for (&str, u16) -where - State: Clone + Send + Sync + 'static, -{ - type Listener = TcpListener; +impl ToListener for (&str, u16) { + type Listener = TcpListener; fn to_listener(self) -> io::Result { Ok(TcpListener::from_addrs(self.to_socket_addrs()?.collect())) @@ -167,31 +134,22 @@ where } #[cfg(unix)] -impl ToListener for async_std::os::unix::net::UnixListener -where - State: Clone + Send + Sync + 'static, -{ - type Listener = UnixListener; +impl ToListener for async_std::os::unix::net::UnixListener { + type Listener = UnixListener; fn to_listener(self) -> io::Result { Ok(UnixListener::from_listener(self)) } } #[cfg(unix)] -impl ToListener for std::os::unix::net::UnixListener -where - State: Clone + Send + Sync + 'static, -{ - type Listener = UnixListener; +impl ToListener for std::os::unix::net::UnixListener { + type Listener = UnixListener; fn to_listener(self) -> io::Result { Ok(UnixListener::from_listener(self)) } } -impl ToListener for TcpListener -where - State: Clone + Send + Sync + 'static, -{ +impl ToListener for TcpListener { type Listener = Self; fn to_listener(self) -> io::Result { Ok(self) @@ -199,62 +157,46 @@ where } #[cfg(unix)] -impl ToListener for UnixListener -where - State: Clone + Send + Sync + 'static, -{ +impl ToListener for UnixListener { type Listener = Self; fn to_listener(self) -> io::Result { Ok(self) } } -impl ToListener for ConcurrentListener -where - State: Clone + Send + Sync + 'static, -{ +impl ToListener for ConcurrentListener { type Listener = Self; fn to_listener(self) -> io::Result { Ok(self) } } -impl ToListener for ParsedListener -where - State: Clone + Send + Sync + 'static, -{ +impl ToListener for ParsedListener { type Listener = Self; fn to_listener(self) -> io::Result { Ok(self) } } -impl ToListener for FailoverListener -where - State: Clone + Send + Sync + 'static, -{ +impl ToListener for FailoverListener { type Listener = Self; fn to_listener(self) -> io::Result { Ok(self) } } -impl ToListener for std::net::SocketAddr -where - State: Clone + Send + Sync + 'static, -{ - type Listener = TcpListener; +impl ToListener for std::net::SocketAddr { + type Listener = TcpListener; fn to_listener(self) -> io::Result { Ok(TcpListener::from_addrs(vec![self])) } } -impl ToListener for Vec +impl ToListener for Vec where - L: ToListener, - State: Clone + Send + Sync + 'static, + L: ToListener, { - type Listener = ConcurrentListener; + type Listener = ConcurrentListener; fn to_listener(self) -> io::Result { let mut concurrent_listener = ConcurrentListener::new(); for listener in self { @@ -268,7 +210,7 @@ where mod parse_tests { use super::*; - fn listen>(listener: L) -> io::Result { + fn listen(listener: L) -> io::Result { listener.to_listener() } diff --git a/src/listener/unix_listener.rs b/src/listener/unix_listener.rs index d99a21d3..ddab3a02 100644 --- a/src/listener/unix_listener.rs +++ b/src/listener/unix_listener.rs @@ -18,14 +18,14 @@ use async_std::{io, task}; /// /// This is currently crate-visible only, and tide users are expected /// to create these through [ToListener](crate::ToListener) conversions. -pub struct UnixListener { +pub struct UnixListener { path: Option, listener: Option, - server: Option>, + server: Option, info: Option, } -impl UnixListener { +impl<'server> UnixListener { pub fn from_path(path: impl Into) -> Self { Self { path: Some(path.into()), @@ -45,7 +45,7 @@ impl UnixListener { } } -fn handle_unix(app: Server, stream: UnixStream) { +fn handle_unix<'listener>(app: Server, stream: UnixStream) { task::spawn(async move { let local_addr = unix_socket_addr_to_string(stream.local_addr()); let peer_addr = unix_socket_addr_to_string(stream.peer_addr()); @@ -63,11 +63,8 @@ fn handle_unix(app: Server, stream: } #[async_trait::async_trait] -impl Listener for UnixListener -where - State: Clone + Send + Sync + 'static, -{ - async fn bind(&mut self, server: Server) -> io::Result<()> { +impl Listener for UnixListener { + async fn bind(&mut self, server: Server) -> io::Result<()> { assert!(self.server.is_none(), "`bind` should only be called once"); self.server = Some(server); @@ -124,7 +121,7 @@ where } } -impl fmt::Debug for UnixListener { +impl fmt::Debug for UnixListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("UnixListener") .field("listener", &self.listener) @@ -141,7 +138,7 @@ impl fmt::Debug for UnixListener { } } -impl Display for UnixListener { +impl Display for UnixListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match &self.listener { Some(listener) => { diff --git a/src/request.rs b/src/request.rs index d69c777d..8a4fa95b 100644 --- a/src/request.rs +++ b/src/request.rs @@ -4,6 +4,7 @@ use routefinder::Captures; use std::ops::Index; use std::pin::Pin; +use std::sync::Arc; #[cfg(feature = "cookies")] use crate::cookies::CookieData; @@ -12,7 +13,7 @@ use crate::http::cookies::Cookie; use crate::http::format_err; use crate::http::headers::{self, HeaderName, HeaderValues, ToHeaderValues}; use crate::http::{self, Body, Method, Mime, StatusCode, Url, Version}; -use crate::Response; +use crate::{Response, State}; pin_project_lite::pin_project! { /// An HTTP request. @@ -24,6 +25,7 @@ pin_project_lite::pin_project! { /// communication between middleware and endpoints. #[derive(Debug)] pub struct Request { + pub(crate) app_state: Arc, #[pin] pub(crate) req: http::Request, pub(crate) route_params: Vec>, @@ -36,20 +38,29 @@ impl Request { req: http_types::Request, route_params: Vec>, ) -> Self { - Self { req, route_params } + Self { + app_state: Arc::new(State::default()), + req, + route_params, + } } /// Create a new `Request`. - pub(crate) fn with_state( - state: S, + pub(crate) fn with_state( + state: Arc, req: http_types::Request, route_params: Vec>, ) -> Self { let mut req = Request::new(req, route_params); - req.set_ext::(state); + req.app_state = state; req } + /// Returns the current app state + pub fn state(&self) -> &T { + &self.app_state.get::().unwrap() + } + /// Access the request's HTTP method. /// /// # Examples diff --git a/src/route.rs b/src/route.rs index 1374fb81..ff08bb96 100644 --- a/src/route.rs +++ b/src/route.rs @@ -121,10 +121,7 @@ impl<'a> Route<'a> { /// ``` /// /// [`Server`]: struct.Server.html - pub fn nest(&mut self, service: crate::Server) -> &mut Self - where - InnerState: Clone + Send + Sync + 'static, - { + pub fn nest(&mut self, service: crate::Server) -> &mut Self { let prefix = self.prefix; self.prefix = true; @@ -295,6 +292,7 @@ where let crate::Request { mut req, route_params, + app_state, } = req; let rest = route_params @@ -305,6 +303,12 @@ where req.url_mut().set_path(rest); - self.0.call(crate::Request { req, route_params }).await + self.0 + .call(crate::Request { + req, + route_params, + app_state, + }) + .await } } diff --git a/src/security/cors.rs b/src/security/cors.rs index 78e65b1d..2bdf2f96 100644 --- a/src/security/cors.rs +++ b/src/security/cors.rs @@ -282,7 +282,7 @@ mod test { .unwrap() } - fn app() -> crate::Server<()> { + fn app() -> crate::Server { let mut app = crate::Server::new(); app.at(ENDPOINT).get(|_| async { Ok("Hello World") }); diff --git a/src/server.rs b/src/server.rs index e62e658f..55b5c5b0 100644 --- a/src/server.rs +++ b/src/server.rs @@ -9,6 +9,7 @@ use crate::listener::{Listener, ToListener}; use crate::log; use crate::middleware::{Middleware, Next}; use crate::router::{Router, Selection}; +use crate::state::State; use crate::{Endpoint, Request, Route}; /// An HTTP server. @@ -26,9 +27,9 @@ use crate::{Endpoint, Request, Route}; /// - Middleware extends the base Tide framework with additional request or /// response processing, such as compression, default headers, or logging. To /// add middleware to an app, use the [`Server::with`] method. -pub struct Server { +pub struct Server { router: Arc, - state: State, + state: Arc, /// Holds the middleware stack. /// /// Note(Fishrock123): We do actually want this structure. @@ -40,7 +41,7 @@ pub struct Server { middleware: Arc>>, } -impl Server<()> { +impl Server { /// Create a new Tide server. /// /// # Examples @@ -61,16 +62,13 @@ impl Server<()> { } } -impl Default for Server<()> { +impl Default for Server { fn default() -> Self { Self::new() } } -impl Server -where - State: Clone + Send + Sync + 'static, -{ +impl Server { /// Create a new Tide server with shared application scoped state. /// /// Application scoped state is useful for storing items @@ -81,7 +79,7 @@ where /// # use async_std::task::block_on; /// # fn main() -> Result<(), std::io::Error> { block_on(async { /// # - /// use tide::{Request, RequestState}; + /// use tide::{Request}; /// /// /// The shared application state. /// #[derive(Clone)] @@ -94,29 +92,23 @@ where /// name: "Nori".to_string() /// }; /// - /// impl RequestState for Request { - /// fn state(&self) -> &State { - /// self.ext::().unwrap() - /// } - /// } - /// /// // Initialize the application with state. /// let mut app = tide::with_state(state); /// app.at("/").get(|req: Request| async move { - /// Ok(format!("Hello, {}!", &req.state().name)) + /// Ok(format!("Hello, {}!", &req.state::().name)) /// }); /// app.listen("127.0.0.1:8080").await?; /// # /// # Ok(()) }) } /// ``` - pub fn with_state(state: State) -> Self { + pub fn with_state(state: S) -> Self { Self { router: Arc::new(Router::new()), middleware: Arc::new(vec![ #[cfg(feature = "cookies")] Arc::new(cookies::CookiesMiddleware::new()), ]), - state, + state: Arc::new(State::with(state)), } } @@ -206,7 +198,7 @@ where /// # /// # Ok(()) }) } /// ``` - pub async fn listen>(self, listener: L) -> io::Result<()> { + pub async fn listen(self, listener: L) -> io::Result<()> { let mut listener = listener.to_listener()?; listener.bind(self).await?; for info in listener.info().iter() { @@ -245,10 +237,7 @@ where /// # /// # Ok(()) }) } /// ``` - pub async fn bind>( - self, - listener: L, - ) -> io::Result<>::Listener> { + pub async fn bind(self, listener: L) -> io::Result<::Listener> { let mut listener = listener.to_listener()?; listener.bind(self).await?; Ok(listener) @@ -283,18 +272,13 @@ where Res: From, { let req = req.into(); - let Self { - router, - state, - middleware, - } = self.clone(); let method = req.method().to_owned(); - let Selection { endpoint, params } = router.route(req.url().path(), method); + let Selection { endpoint, params } = self.router.route(req.url().path(), method); let route_params = vec![params]; - let req = Request::with_state(state, req, route_params); + let req = Request::with_state(self.state.clone(), req, route_params); - let next = Next::new(endpoint, middleware); + let next = Next::new(endpoint, self.middleware.clone()); let res = next.run(req).await; let res: http_types::Response = res.into(); Ok(res.into()) @@ -311,18 +295,12 @@ where /// admin.at("/").get(|_| async { Ok("nested app with cloned state") }); /// app.at("/").nest(admin); /// ``` - pub fn state(&self) -> &State { + pub fn state(&self) -> &Arc { &self.state } } -impl std::fmt::Debug for Server { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Server").finish() - } -} - -impl Clone for Server { +impl Clone for Server { fn clone(&self) -> Self { Self { router: self.router.clone(), @@ -332,8 +310,14 @@ impl Clone for Server { } } +impl std::fmt::Debug for Server { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Server").finish() + } +} + #[async_trait::async_trait] -impl Endpoint for Server { +impl Endpoint for Server { async fn call(&self, req: Request) -> crate::Result { let Request { req, @@ -342,27 +326,18 @@ impl Endpoint for Server } = req; let path = req.url().path().to_owned(); let method = req.method().to_owned(); - let router = self.router.clone(); - let middleware = self.middleware.clone(); - let state = self.state.clone(); - let Selection { endpoint, params } = router.route(&path, method); + let Selection { endpoint, params } = self.router.route(&path, method); route_params.push(params); - let req = Request::with_state(state, req, route_params); + let req = Request::with_state(self.state.clone(), req, route_params); - let next = Next::new(endpoint, middleware); + let next = Next::new(endpoint, self.middleware.clone()); Ok(next.run(req).await) } } -/// Request extension trait that returns a reference to the State -pub trait RequestState { - /// Extends the Request to be able to return a - fn state(&self) -> &State; -} - #[crate::utils::async_trait] -impl http_client::HttpClient for Server { +impl http_client::HttpClient for Server { async fn send(&self, req: crate::http::Request) -> crate::http::Result { self.respond(req).await } diff --git a/src/sessions/middleware.rs b/src/sessions/middleware.rs index 90de8f28..2fd01e3c 100644 --- a/src/sessions/middleware.rs +++ b/src/sessions/middleware.rs @@ -74,10 +74,9 @@ impl std::fmt::Debug for SessionMiddleware { } #[async_trait] -impl Middleware for SessionMiddleware +impl Middleware for SessionMiddleware where Store: SessionStore, - State: Clone + Send + Sync + 'static, { async fn handle(&self, mut request: Request, next: Next) -> crate::Result { let cookie = request.cookie(&self.cookie_name); diff --git a/src/sse/endpoint.rs b/src/sse/endpoint.rs index 929e7859..c8c3dc82 100644 --- a/src/sse/endpoint.rs +++ b/src/sse/endpoint.rs @@ -11,34 +11,29 @@ use std::marker::PhantomData; use std::sync::Arc; /// Create an endpoint that can handle SSE connections. -pub fn endpoint(handler: F) -> SseEndpoint +pub fn endpoint(handler: F) -> SseEndpoint where - State: Clone + Send + Sync + 'static, F: Fn(Request, Sender) -> Fut + Send + Sync + 'static, Fut: Future> + Send + 'static, { SseEndpoint { handler: Arc::new(handler), - __state: PhantomData, } } /// An endpoint that can handle SSE connections. #[derive(Debug)] -pub struct SseEndpoint +pub struct SseEndpoint where - State: Clone + Send + Sync + 'static, F: Fn(Request, Sender) -> Fut + Send + Sync + 'static, Fut: Future> + Send + 'static, { handler: Arc, - __state: PhantomData, } #[async_trait::async_trait] -impl Endpoint for SseEndpoint +impl Endpoint for SseEndpoint where - State: Clone + Send + Sync + 'static, F: Fn(Request, Sender) -> Fut + Send + Sync + 'static, Fut: Future> + Send + 'static, { diff --git a/src/sse/upgrade.rs b/src/sse/upgrade.rs index 4171d96b..5aa1287a 100644 --- a/src/sse/upgrade.rs +++ b/src/sse/upgrade.rs @@ -9,9 +9,8 @@ use async_std::io::BufReader; use async_std::task; /// Upgrade an existing HTTP connection to an SSE connection. -pub fn upgrade(req: Request, handler: F) -> Response +pub fn upgrade(req: Request, handler: F) -> Response where - State: Clone + Send + Sync + 'static, F: Fn(Request, Sender) -> Fut + Send + Sync + 'static, Fut: Future> + Send + 'static, { diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 00000000..00022df2 --- /dev/null +++ b/src/state.rs @@ -0,0 +1,76 @@ +// Originally from https://github.com/http-rs/http-types/blob/main/src/extensions.rs +// +// Implementation is based on +// - https://github.com/trillium-rs/trillium/blob/main/http/src/state_set.rs +// - https://github.com/hyperium/http/blob/master/src/extensions.rs +// - https://github.com/kardeiz/type-map/blob/master/src/lib.rs +use std::{ + any::{Any, TypeId}, + hash::{BuildHasherDefault, Hasher}, +}; + +use hashbrown::HashMap; + +/// Store and retrieve values by +/// [`TypeId`](https://doc.rust-lang.org/std/any/struct.TypeId.html). This +/// allows storing arbitrary data that implements `Sync + Send + +/// 'static`. +#[derive(Default, Debug)] +pub struct State(HashMap, BuildHasherDefault>); + +// With TypeIds as keys, there's no need to hash them. So we simply use an identy hasher. +#[derive(Default)] +struct IdHasher(u64); + +impl Hasher for IdHasher { + fn write(&mut self, _: &[u8]) { + unreachable!("TypeId calls write_u64"); + } + + #[inline] + fn write_u64(&mut self, id: u64) { + self.0 = id; + } + + #[inline] + fn finish(&self) -> u64 { + self.0 + } +} + +impl State { + /// Create an empty `StateSet`. + pub fn new() -> Self { + Self::default() + } + + /// Create a `State` with a default inserted value. + pub fn with(val: S) -> Self { + let mut state = State::new(); + state.insert(val); + state + } + + /// Insert a value into this `State`. + /// + /// If a value of this type already exists, it will be returned. + pub fn insert(&mut self, val: T) -> Option { + self.0 + .insert(TypeId::of::(), Box::new(val)) + .and_then(|boxed| (boxed as Box).downcast().ok().map(|boxed| *boxed)) + } + + /// Get a reference to a value previously inserted on this `State`. + pub fn get(&self) -> Option<&T> { + self.0 + .get(&TypeId::of::()) + .and_then(|boxed| (&**boxed as &(dyn Any)).downcast_ref()) + } + + /// Get a mutable reference to a value previously inserted on this `State`. + pub fn get_mut(&mut self) -> Option<&mut T> { + self.0 + .get_mut(&TypeId::of::()) + .and_then(|boxed| (&mut **boxed as &mut (dyn Any)).downcast_mut()) + } +} diff --git a/tests/nested.rs b/tests/nested.rs index fb37301f..106cab7c 100644 --- a/tests/nested.rs +++ b/tests/nested.rs @@ -1,6 +1,6 @@ mod test_utils; use test_utils::ServerTestingExt; -use tide::{Request, RequestState}; +use tide::Request; #[async_std::test] async fn nested() -> tide::Result<()> { @@ -48,7 +48,7 @@ async fn nested_middleware() -> tide::Result<()> { Ok(()) } -#[derive(Clone)] +#[derive(Clone, Debug)] struct Num(i32); #[async_std::test] @@ -56,7 +56,8 @@ async fn nested_with_different_state() -> tide::Result<()> { let mut outer = tide::new(); let mut inner = tide::with_state(Num(42)); inner.at("/").get(|req: Request| async move { - let num = req.state().0; + let num = req.state::().0; + println!("{:?}", req.state::()); Ok(format!("the number is {}", num)) }); outer.at("/").get(|_| async { Ok("Hello, world!") }); @@ -66,9 +67,3 @@ async fn nested_with_different_state() -> tide::Result<()> { assert_eq!(outer.get("/").recv_string().await?, "Hello, world!"); Ok(()) } - -impl RequestState for Request { - fn state(&self) -> &Num { - self.ext::().unwrap() - } -} diff --git a/tests/serve_dir.rs b/tests/serve_dir.rs index d05cce1b..62f09854 100644 --- a/tests/serve_dir.rs +++ b/tests/serve_dir.rs @@ -7,7 +7,7 @@ fn api() -> Box { Box::new(|_| async { Ok("api") }) } -fn app(tempdir: &tempfile::TempDir) -> Result> { +fn app(tempdir: &tempfile::TempDir) -> Result { let static_dir = tempdir.path().join("static"); fs::create_dir(&static_dir)?; diff --git a/tests/test_utils.rs b/tests/test_utils.rs index 9ea19d0f..0422a287 100644 --- a/tests/test_utils.rs +++ b/tests/test_utils.rs @@ -60,7 +60,7 @@ pub trait ServerTestingExt { } } -impl ServerTestingExt for tide::Server { +impl ServerTestingExt for tide::Server { fn client(&self) -> Client { let config = Config::new() .set_http_client(self.clone()) From 1dfecaef43e4ed747a4b3ec883a08625253eb680 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Mon, 18 Jul 2022 15:16:40 -0500 Subject: [PATCH 12/20] Update handle_unix to remove unused lifetime --- src/listener/unix_listener.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/listener/unix_listener.rs b/src/listener/unix_listener.rs index ddab3a02..b9550f99 100644 --- a/src/listener/unix_listener.rs +++ b/src/listener/unix_listener.rs @@ -45,7 +45,7 @@ impl<'server> UnixListener { } } -fn handle_unix<'listener>(app: Server, stream: UnixStream) { +fn handle_unix(app: Server, stream: UnixStream) { task::spawn(async move { let local_addr = unix_socket_addr_to_string(stream.local_addr()); let peer_addr = unix_socket_addr_to_string(stream.peer_addr()); From 74a25a1b945c764cc7aa1c0e366d23979e9d9b63 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Mon, 18 Jul 2022 17:13:30 -0500 Subject: [PATCH 13/20] Refactor state into state middleware with request/response extensions --- examples/graphql.rs | 2 +- src/lib.rs | 6 ++-- src/request.rs | 27 ++++----------- src/route.rs | 9 +---- src/server.rs | 61 +++++++++++---------------------- src/state.rs | 76 ----------------------------------------- src/state/middleware.rs | 24 +++++++++++++ src/state/mod.rs | 3 ++ 8 files changed, 59 insertions(+), 149 deletions(-) delete mode 100644 src/state.rs create mode 100644 src/state/middleware.rs create mode 100644 src/state/mod.rs diff --git a/examples/graphql.rs b/examples/graphql.rs index 13db866c..2daf8098 100644 --- a/examples/graphql.rs +++ b/examples/graphql.rs @@ -96,7 +96,7 @@ async fn handle_graphiql(_: Request) -> tide::Result> { #[async_std::main] async fn main() -> std::io::Result<()> { - let mut app = Server::with_state(State { + let mut app = tide::with_state(State { users: Arc::new(RwLock::new(Vec::new())), }); app.at("/").get(Redirect::permanent("/graphiql")); diff --git a/src/lib.rs b/src/lib.rs index c651bd37..8785a5d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -99,7 +99,7 @@ pub use response::Response; pub use response_builder::ResponseBuilder; pub use route::Route; pub use server::Server; -pub use state::State; +pub use state::StateMiddleware; pub use http_types::{self as http, Body, Error, Status, StatusCode}; @@ -158,7 +158,9 @@ pub fn with_state(state: State) -> server::Server where State: Clone + Send + Sync + 'static, { - Server::with_state(state) + let mut server = Server::new(); + server.with_state(state); + server } /// A specialized Result type for Tide. diff --git a/src/request.rs b/src/request.rs index 8a4fa95b..f8f72f1c 100644 --- a/src/request.rs +++ b/src/request.rs @@ -4,7 +4,6 @@ use routefinder::Captures; use std::ops::Index; use std::pin::Pin; -use std::sync::Arc; #[cfg(feature = "cookies")] use crate::cookies::CookieData; @@ -13,7 +12,7 @@ use crate::http::cookies::Cookie; use crate::http::format_err; use crate::http::headers::{self, HeaderName, HeaderValues, ToHeaderValues}; use crate::http::{self, Body, Method, Mime, StatusCode, Url, Version}; -use crate::{Response, State}; +use crate::Response; pin_project_lite::pin_project! { /// An HTTP request. @@ -25,7 +24,6 @@ pin_project_lite::pin_project! { /// communication between middleware and endpoints. #[derive(Debug)] pub struct Request { - pub(crate) app_state: Arc, #[pin] pub(crate) req: http::Request, pub(crate) route_params: Vec>, @@ -38,27 +36,14 @@ impl Request { req: http_types::Request, route_params: Vec>, ) -> Self { - Self { - app_state: Arc::new(State::default()), - req, - route_params, - } - } - - /// Create a new `Request`. - pub(crate) fn with_state( - state: Arc, - req: http_types::Request, - route_params: Vec>, - ) -> Self { - let mut req = Request::new(req, route_params); - req.app_state = state; - req + Self { req, route_params } } /// Returns the current app state - pub fn state(&self) -> &T { - &self.app_state.get::().unwrap() + pub fn state(&self) -> &T { + &self + .ext::() + .expect("request state not set for type, did you call app.with_state?") } /// Access the request's HTTP method. diff --git a/src/route.rs b/src/route.rs index ff08bb96..1e34187c 100644 --- a/src/route.rs +++ b/src/route.rs @@ -292,7 +292,6 @@ where let crate::Request { mut req, route_params, - app_state, } = req; let rest = route_params @@ -303,12 +302,6 @@ where req.url_mut().set_path(rest); - self.0 - .call(crate::Request { - req, - route_params, - app_state, - }) - .await + self.0.call(crate::Request { req, route_params }).await } } diff --git a/src/server.rs b/src/server.rs index 55b5c5b0..513565e2 100644 --- a/src/server.rs +++ b/src/server.rs @@ -9,7 +9,7 @@ use crate::listener::{Listener, ToListener}; use crate::log; use crate::middleware::{Middleware, Next}; use crate::router::{Router, Selection}; -use crate::state::State; +use crate::state::StateMiddleware; use crate::{Endpoint, Request, Route}; /// An HTTP server. @@ -29,7 +29,6 @@ use crate::{Endpoint, Request, Route}; /// add middleware to an app, use the [`Server::with`] method. pub struct Server { router: Arc, - state: Arc, /// Holds the middleware stack. /// /// Note(Fishrock123): We do actually want this structure. @@ -41,6 +40,12 @@ pub struct Server { middleware: Arc>>, } +impl Default for Server { + fn default() -> Self { + Self::new() + } +} + impl Server { /// Create a new Tide server. /// @@ -58,17 +63,15 @@ impl Server { /// ``` #[must_use] pub fn new() -> Self { - Self::with_state(()) - } -} - -impl Default for Server { - fn default() -> Self { - Self::new() + Self { + router: Arc::new(Router::new()), + middleware: Arc::new(vec![ + #[cfg(feature = "cookies")] + Arc::new(cookies::CookiesMiddleware::new()), + ]), + } } -} -impl Server { /// Create a new Tide server with shared application scoped state. /// /// Application scoped state is useful for storing items @@ -93,7 +96,8 @@ impl Server { /// }; /// /// // Initialize the application with state. - /// let mut app = tide::with_state(state); + /// let mut app = tide::new(); + /// app.with_state(state); /// app.at("/").get(|req: Request| async move { /// Ok(format!("Hello, {}!", &req.state::().name)) /// }); @@ -101,15 +105,8 @@ impl Server { /// # /// # Ok(()) }) } /// ``` - pub fn with_state(state: S) -> Self { - Self { - router: Arc::new(Router::new()), - middleware: Arc::new(vec![ - #[cfg(feature = "cookies")] - Arc::new(cookies::CookiesMiddleware::new()), - ]), - state: Arc::new(State::with(state)), - } + pub fn with_state(&mut self, state: S) { + self.with(StateMiddleware::new(state)); } /// Add a new route at the given `path`, relative to root. @@ -276,35 +273,18 @@ impl Server { let method = req.method().to_owned(); let Selection { endpoint, params } = self.router.route(req.url().path(), method); let route_params = vec![params]; - let req = Request::with_state(self.state.clone(), req, route_params); - + let req = Request::new(req, route_params); let next = Next::new(endpoint, self.middleware.clone()); let res = next.run(req).await; let res: http_types::Response = res.into(); Ok(res.into()) } - - /// Gets a reference to the server's state. This is useful for testing and nesting: - /// - /// # Example - /// - /// ```rust - /// # #[derive(Clone)] struct SomeAppState; - /// let mut app = tide::with_state(SomeAppState); - /// let mut admin = tide::with_state(app.state().clone()); - /// admin.at("/").get(|_| async { Ok("nested app with cloned state") }); - /// app.at("/").nest(admin); - /// ``` - pub fn state(&self) -> &Arc { - &self.state - } } impl Clone for Server { fn clone(&self) -> Self { Self { router: self.router.clone(), - state: self.state.clone(), middleware: self.middleware.clone(), } } @@ -329,8 +309,7 @@ impl Endpoint for Server { let Selection { endpoint, params } = self.router.route(&path, method); route_params.push(params); - let req = Request::with_state(self.state.clone(), req, route_params); - + let req = Request::new(req, route_params); let next = Next::new(endpoint, self.middleware.clone()); Ok(next.run(req).await) } diff --git a/src/state.rs b/src/state.rs deleted file mode 100644 index 00022df2..00000000 --- a/src/state.rs +++ /dev/null @@ -1,76 +0,0 @@ -// Originally from https://github.com/http-rs/http-types/blob/main/src/extensions.rs -// -// Implementation is based on -// - https://github.com/trillium-rs/trillium/blob/main/http/src/state_set.rs -// - https://github.com/hyperium/http/blob/master/src/extensions.rs -// - https://github.com/kardeiz/type-map/blob/master/src/lib.rs -use std::{ - any::{Any, TypeId}, - hash::{BuildHasherDefault, Hasher}, -}; - -use hashbrown::HashMap; - -/// Store and retrieve values by -/// [`TypeId`](https://doc.rust-lang.org/std/any/struct.TypeId.html). This -/// allows storing arbitrary data that implements `Sync + Send + -/// 'static`. -#[derive(Default, Debug)] -pub struct State(HashMap, BuildHasherDefault>); - -// With TypeIds as keys, there's no need to hash them. So we simply use an identy hasher. -#[derive(Default)] -struct IdHasher(u64); - -impl Hasher for IdHasher { - fn write(&mut self, _: &[u8]) { - unreachable!("TypeId calls write_u64"); - } - - #[inline] - fn write_u64(&mut self, id: u64) { - self.0 = id; - } - - #[inline] - fn finish(&self) -> u64 { - self.0 - } -} - -impl State { - /// Create an empty `StateSet`. - pub fn new() -> Self { - Self::default() - } - - /// Create a `State` with a default inserted value. - pub fn with(val: S) -> Self { - let mut state = State::new(); - state.insert(val); - state - } - - /// Insert a value into this `State`. - /// - /// If a value of this type already exists, it will be returned. - pub fn insert(&mut self, val: T) -> Option { - self.0 - .insert(TypeId::of::(), Box::new(val)) - .and_then(|boxed| (boxed as Box).downcast().ok().map(|boxed| *boxed)) - } - - /// Get a reference to a value previously inserted on this `State`. - pub fn get(&self) -> Option<&T> { - self.0 - .get(&TypeId::of::()) - .and_then(|boxed| (&**boxed as &(dyn Any)).downcast_ref()) - } - - /// Get a mutable reference to a value previously inserted on this `State`. - pub fn get_mut(&mut self) -> Option<&mut T> { - self.0 - .get_mut(&TypeId::of::()) - .and_then(|boxed| (&mut **boxed as &mut (dyn Any)).downcast_mut()) - } -} diff --git a/src/state/middleware.rs b/src/state/middleware.rs new file mode 100644 index 00000000..4cc4f3d0 --- /dev/null +++ b/src/state/middleware.rs @@ -0,0 +1,24 @@ +use crate::{utils::async_trait, Middleware, Next, Request}; + +/// Sets data onto the request extensions +#[derive(Debug)] +pub struct StateMiddleware { + data: T, +} + +impl StateMiddleware { + /// Creates a new state middleware with the provided state + pub fn new(data: T) -> Self { + Self { data } + } +} + +#[async_trait] +impl Middleware for StateMiddleware { + async fn handle(&self, mut request: Request, next: Next) -> crate::Result { + request.set_ext(self.data.clone()); + let mut response = next.run(request).await; + response.set_ext(self.data.clone()); + Ok(response) + } +} diff --git a/src/state/mod.rs b/src/state/mod.rs new file mode 100644 index 00000000..b0f78900 --- /dev/null +++ b/src/state/mod.rs @@ -0,0 +1,3 @@ +mod middleware; + +pub use middleware::StateMiddleware; From 5c1bb1bf52d764cf4d5683c19ff7aea0de3ee783 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Mon, 18 Jul 2022 17:15:08 -0500 Subject: [PATCH 14/20] Add response state function when expecting state middleware --- src/response.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/response.rs b/src/response.rs index c375e302..15386063 100644 --- a/src/response.rs +++ b/src/response.rs @@ -433,6 +433,13 @@ impl Response { self.res.ext_mut().insert(val) } + /// Returns the current app state set from StateMiddleware + pub fn state(&self) -> &T { + &self + .ext::() + .expect("response state not set for type, did you call app.with_state?") + } + /// Create a `tide::Response` from a type that can be converted into an /// `http_types::Response`. pub fn from_res(value: T) -> Self From b96d774638c770772b8b445f033ee5a50943f4f3 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Mon, 18 Jul 2022 17:17:21 -0500 Subject: [PATCH 15/20] Fix import on graphql example --- examples/graphql.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/graphql.rs b/examples/graphql.rs index 2daf8098..6e4a020b 100644 --- a/examples/graphql.rs +++ b/examples/graphql.rs @@ -2,7 +2,7 @@ use std::sync::{Arc, RwLock}; use juniper::{http::graphiql, http::GraphQLRequest, RootNode}; use lazy_static::lazy_static; -use tide::{http::mime, Body, Redirect, Request, Response, Server, StatusCode}; +use tide::{http::mime, Body, Redirect, Request, Response, StatusCode}; #[derive(Clone)] struct User { From d101601ddb0ca03ccf6e39d8498461b3b2db5f3e Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Mon, 18 Jul 2022 17:29:53 -0500 Subject: [PATCH 16/20] Cleanup request/response clippy --- src/listener/unix_listener.rs | 2 +- src/request.rs | 3 +-- src/response.rs | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/listener/unix_listener.rs b/src/listener/unix_listener.rs index b9550f99..ec32bb6d 100644 --- a/src/listener/unix_listener.rs +++ b/src/listener/unix_listener.rs @@ -25,7 +25,7 @@ pub struct UnixListener { info: Option, } -impl<'server> UnixListener { +impl UnixListener { pub fn from_path(path: impl Into) -> Self { Self { path: Some(path.into()), diff --git a/src/request.rs b/src/request.rs index f8f72f1c..1f8f8123 100644 --- a/src/request.rs +++ b/src/request.rs @@ -41,8 +41,7 @@ impl Request { /// Returns the current app state pub fn state(&self) -> &T { - &self - .ext::() + self.ext::() .expect("request state not set for type, did you call app.with_state?") } diff --git a/src/response.rs b/src/response.rs index 15386063..3ba1466b 100644 --- a/src/response.rs +++ b/src/response.rs @@ -435,8 +435,7 @@ impl Response { /// Returns the current app state set from StateMiddleware pub fn state(&self) -> &T { - &self - .ext::() + self.ext::() .expect("response state not set for type, did you call app.with_state?") } From dc75e0011c4e7edd5cbdce051bd6ddcbe8af8469 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Mon, 18 Jul 2022 17:33:22 -0500 Subject: [PATCH 17/20] Add back primitive state test in nested --- tests/nested.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/nested.rs b/tests/nested.rs index 106cab7c..c5169ad2 100644 --- a/tests/nested.rs +++ b/tests/nested.rs @@ -48,16 +48,13 @@ async fn nested_middleware() -> tide::Result<()> { Ok(()) } -#[derive(Clone, Debug)] -struct Num(i32); - #[async_std::test] async fn nested_with_different_state() -> tide::Result<()> { let mut outer = tide::new(); - let mut inner = tide::with_state(Num(42)); + let mut inner = tide::with_state(42); inner.at("/").get(|req: Request| async move { - let num = req.state::().0; - println!("{:?}", req.state::()); + let num = req.state::(); + println!("{:?}", req.state::()); Ok(format!("the number is {}", num)) }); outer.at("/").get(|_| async { Ok("Hello, world!") }); From e21b4edf02acf623a5da99800e4dc8b97e9d18d6 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Mon, 18 Jul 2022 22:22:41 -0500 Subject: [PATCH 18/20] Refactor selection/routing to just use Next as an endpoint --- src/endpoint.rs | 60 +---------------------------------------------- src/middleware.rs | 41 ++++++++++++++++++++++++++++++-- src/route.rs | 29 +++++++---------------- src/router.rs | 22 ++++++++--------- src/server.rs | 12 +++++----- 5 files changed, 65 insertions(+), 99 deletions(-) diff --git a/src/endpoint.rs b/src/endpoint.rs index 6b3b2ca2..fffac3e9 100644 --- a/src/endpoint.rs +++ b/src/endpoint.rs @@ -1,10 +1,8 @@ use async_std::future::Future; -use async_std::sync::Arc; use async_trait::async_trait; use http_types::Result; -use crate::middleware::Next; -use crate::{Middleware, Request, Response}; +use crate::{Request, Response}; /// An HTTP request handler. /// @@ -50,8 +48,6 @@ pub trait Endpoint: Send + Sync + 'static { async fn call(&self, req: Request) -> crate::Result; } -pub(crate) type DynEndpoint = dyn Endpoint; - #[async_trait] impl Endpoint for F where @@ -66,60 +62,6 @@ where } } -pub(crate) struct MiddlewareEndpoint { - endpoint: Arc, - middleware: Arc>>, -} - -impl Clone for MiddlewareEndpoint { - fn clone(&self) -> Self { - Self { - endpoint: self.endpoint.clone(), - middleware: self.middleware.clone(), - } - } -} - -impl std::fmt::Debug for MiddlewareEndpoint { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - fmt, - "MiddlewareEndpoint (length: {})", - self.middleware.len(), - ) - } -} - -impl MiddlewareEndpoint -where - E: Endpoint, -{ - pub(crate) fn wrap_with_middleware( - ep: E, - middleware: Vec>, - ) -> Arc { - if middleware.is_empty() { - Arc::new(ep) - } else { - Arc::new(Self { - endpoint: Arc::new(ep), - middleware: Arc::new(middleware), - }) - } - } -} - -#[async_trait] -impl Endpoint for MiddlewareEndpoint -where - E: Endpoint, -{ - async fn call(&self, req: Request) -> crate::Result { - let next = Next::new(self.endpoint.clone(), self.middleware.clone()); - Ok(next.run(req).await) - } -} - #[async_trait] impl Endpoint for Box { async fn call(&self, request: Request) -> crate::Result { diff --git a/src/middleware.rs b/src/middleware.rs index b6fb3b8c..f2620744 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -42,10 +42,28 @@ pub struct Next { impl Next { /// Creates a new Next middleware with an arc to the endpoint and middleware - pub fn new(endpoint: Arc, middleware: Arc>>) -> Self { + pub fn new(endpoint: impl Endpoint, middleware: Vec>) -> Self { Self { cursor: 0, - endpoint, + endpoint: Arc::new(endpoint), + middleware: Arc::new(middleware), + } + } + + /// Creates a new Next middleware from the given endpoint with empty middleware + pub fn from_endpoint(endpoint: impl Endpoint) -> Self { + Self { + cursor: 0, + endpoint: Arc::new(endpoint), + middleware: Arc::default(), + } + } + + /// Creates a new Next middleware from an existing next middleware (as an endpoint) + pub fn from_next(endpoint: Next, middleware: Arc>>) -> Self { + Self { + cursor: 0, + endpoint: Arc::new(endpoint), middleware, } } @@ -66,3 +84,22 @@ impl Next { } } } + +#[async_trait] +impl Endpoint for Next { + async fn call(&self, req: Request) -> crate::Result { + let next = self.clone(); + let response = next.run(req).await; + Ok(response) + } +} + +impl Clone for Next { + fn clone(&self) -> Self { + Next { + cursor: 0, + endpoint: self.endpoint.clone(), + middleware: self.middleware.clone(), + } + } +} diff --git a/src/route.rs b/src/route.rs index 1e34187c..989a24dc 100644 --- a/src/route.rs +++ b/src/route.rs @@ -3,9 +3,8 @@ use std::io; use std::path::Path; use std::sync::Arc; -use crate::endpoint::MiddlewareEndpoint; use crate::fs::{ServeDir, ServeFile}; -use crate::log; +use crate::{log, Next}; use crate::{router::Router, Endpoint, Middleware}; /// A handle to a route. @@ -178,17 +177,11 @@ impl<'a> Route<'a> { if self.prefix { let ep = StripPrefixEndpoint::new(ep); let wildcard = self.at("*"); - wildcard.router.add( - &wildcard.path, - method, - MiddlewareEndpoint::wrap_with_middleware(ep, wildcard.middleware.clone()), - ); + let next = Next::new(ep, wildcard.middleware.clone()); + wildcard.router.add(&wildcard.path, method, next); } else { - self.router.add( - &self.path, - method, - MiddlewareEndpoint::wrap_with_middleware(ep, self.middleware.clone()), - ); + let next = Next::new(ep, self.middleware.clone()); + self.router.add(&self.path, method, next); } self } @@ -200,15 +193,11 @@ impl<'a> Route<'a> { if self.prefix { let ep = StripPrefixEndpoint::new(ep); let wildcard = self.at("*"); - wildcard.router.add_all( - &wildcard.path, - MiddlewareEndpoint::wrap_with_middleware(ep, wildcard.middleware.clone()), - ); + let next = Next::new(ep, wildcard.middleware.clone()); + wildcard.router.add_all(&wildcard.path, next); } else { - self.router.add_all( - &self.path, - MiddlewareEndpoint::wrap_with_middleware(ep, self.middleware.clone()), - ); + let next = Next::new(ep, self.middleware.clone()); + self.router.add_all(&self.path, next); } self } diff --git a/src/router.rs b/src/router.rs index 3334eb61..fe40b061 100644 --- a/src/router.rs +++ b/src/router.rs @@ -1,9 +1,7 @@ use routefinder::{Captures, Router as MethodRouter}; use std::collections::HashMap; -use std::sync::Arc; -use crate::endpoint::DynEndpoint; -use crate::{Request, Response, StatusCode}; +use crate::{Next, Request, Response, StatusCode}; /// The routing table used by `Server` /// @@ -11,8 +9,8 @@ use crate::{Request, Response, StatusCode}; /// by the method first allows the table itself to be more efficient. #[allow(missing_debug_implementations)] pub(crate) struct Router { - method_map: HashMap>>, - all_method_router: MethodRouter>, + method_map: HashMap>, + all_method_router: MethodRouter, } impl std::fmt::Debug for Router { @@ -26,7 +24,7 @@ impl std::fmt::Debug for Router { /// The result of routing a URL pub(crate) struct Selection { - pub(crate) endpoint: Arc, + pub(crate) next: Next, pub(crate) params: Captures<'static, 'static>, } @@ -38,7 +36,7 @@ impl Router { } } - pub(crate) fn add(&mut self, path: &str, method: http_types::Method, ep: Arc) { + pub(crate) fn add(&mut self, path: &str, method: http_types::Method, ep: Next) { self.method_map .entry(method) .or_insert_with(MethodRouter::new) @@ -46,7 +44,7 @@ impl Router { .unwrap() } - pub(crate) fn add_all(&mut self, path: &str, ep: Arc) { + pub(crate) fn add_all(&mut self, path: &str, ep: Next) { self.all_method_router.add(path, ep).unwrap() } @@ -57,12 +55,12 @@ impl Router { .and_then(|r| r.best_match(path)) { Selection { - endpoint: m.handler().to_owned(), + next: m.handler().clone(), params: m.captures().into_owned(), } } else if let Some(m) = self.all_method_router.best_match(path) { Selection { - endpoint: m.handler().to_owned(), + next: m.handler().clone(), params: m.captures().into_owned(), } } else if method == http_types::Method::Head { @@ -79,12 +77,12 @@ impl Router { // If this `path` can be handled by a callback registered with a different HTTP method // should return 405 Method Not Allowed Selection { - endpoint: Arc::new(method_not_allowed), + next: Next::from_endpoint(method_not_allowed), params: Captures::default(), } } else { Selection { - endpoint: Arc::new(not_found_endpoint), + next: Next::from_endpoint(not_found_endpoint), params: Captures::default(), } } diff --git a/src/server.rs b/src/server.rs index 513565e2..68bcb4c9 100644 --- a/src/server.rs +++ b/src/server.rs @@ -6,10 +6,10 @@ use async_std::sync::Arc; #[cfg(feature = "cookies")] use crate::cookies; use crate::listener::{Listener, ToListener}; -use crate::log; -use crate::middleware::{Middleware, Next}; +use crate::middleware::Middleware; use crate::router::{Router, Selection}; use crate::state::StateMiddleware; +use crate::{log, Next}; use crate::{Endpoint, Request, Route}; /// An HTTP server. @@ -271,10 +271,10 @@ impl Server { let req = req.into(); let method = req.method().to_owned(); - let Selection { endpoint, params } = self.router.route(req.url().path(), method); + let Selection { next, params } = self.router.route(req.url().path(), method); let route_params = vec![params]; let req = Request::new(req, route_params); - let next = Next::new(endpoint, self.middleware.clone()); + let next = Next::from_next(next, self.middleware.clone()); let res = next.run(req).await; let res: http_types::Response = res.into(); Ok(res.into()) @@ -307,10 +307,10 @@ impl Endpoint for Server { let path = req.url().path().to_owned(); let method = req.method().to_owned(); - let Selection { endpoint, params } = self.router.route(&path, method); + let Selection { next, params } = self.router.route(&path, method); route_params.push(params); let req = Request::new(req, route_params); - let next = Next::new(endpoint, self.middleware.clone()); + let next = Next::from_next(next, self.middleware.clone()); Ok(next.run(req).await) } } From 2e314ff5e79088f5dedb396b0299873be1a409a9 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Mon, 18 Jul 2022 22:32:43 -0500 Subject: [PATCH 19/20] Remove hashbrown dependency --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7da6f38b..3a0f7ee9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,6 @@ serde = "1.0.117" serde_json = "1.0.59" routefinder = "0.5.0" regex = "1.5.5" -hashbrown = "0.12.3" [dev-dependencies] async-std = { version = "1.6.5", features = ["unstable", "attributes"] } From b8ba6c9ae26ffceae2104dc2693472c9ff565ff2 Mon Sep 17 00:00:00 2001 From: Thomas Holloway Date: Mon, 18 Jul 2022 23:09:45 -0500 Subject: [PATCH 20/20] Re-enable log start on tests/examples --- examples/catflap.rs | 2 +- examples/chunked.rs | 2 +- examples/concurrent_listeners.rs | 2 +- examples/cookies.rs | 2 +- examples/error_handling.rs | 2 +- examples/hello.rs | 2 +- examples/json.rs | 2 +- examples/middleware.rs | 2 +- examples/nested.rs | 2 +- examples/redirect.rs | 2 +- examples/sessions.rs | 2 +- examples/sse.rs | 2 +- examples/state.rs | 2 +- examples/static_file.rs | 2 +- examples/upload.rs | 2 +- src/lib.rs | 2 +- src/listener/concurrent_listener.rs | 2 +- src/listener/failover_listener.rs | 2 +- src/log/mod.rs | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/examples/catflap.rs b/examples/catflap.rs index 486d565a..c3be1e67 100644 --- a/examples/catflap.rs +++ b/examples/catflap.rs @@ -2,7 +2,7 @@ #[async_std::main] async fn main() -> Result<(), std::io::Error> { use std::{env, net::TcpListener, os::unix::io::FromRawFd}; - // tide::log::start(); + tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); app.at("/").get(|_| async { Ok(CHANGE_THIS_TEXT) }); diff --git a/examples/chunked.rs b/examples/chunked.rs index b3250951..6ea80539 100644 --- a/examples/chunked.rs +++ b/examples/chunked.rs @@ -2,7 +2,7 @@ use tide::Body; #[async_std::main] async fn main() -> Result<(), std::io::Error> { - // tide::log::start(); + tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); app.at("/").get(|_| async { diff --git a/examples/concurrent_listeners.rs b/examples/concurrent_listeners.rs index 1ee93af3..2cb9bdb0 100644 --- a/examples/concurrent_listeners.rs +++ b/examples/concurrent_listeners.rs @@ -2,7 +2,7 @@ use tide::Request; #[async_std::main] async fn main() -> Result<(), std::io::Error> { - // tide::log::start(); + tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); diff --git a/examples/cookies.rs b/examples/cookies.rs index 975465d7..34a1cbd3 100644 --- a/examples/cookies.rs +++ b/examples/cookies.rs @@ -21,7 +21,7 @@ async fn remove_cookie(_req: Request) -> tide::Result { #[async_std::main] async fn main() -> Result<(), std::io::Error> { - // tide::log::start(); + tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); diff --git a/examples/error_handling.rs b/examples/error_handling.rs index 90b47eb4..f5eecb47 100644 --- a/examples/error_handling.rs +++ b/examples/error_handling.rs @@ -5,7 +5,7 @@ use tide::{Body, Request, Response, Result, StatusCode}; #[async_std::main] async fn main() -> Result<()> { - // tide::log::start(); + tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); diff --git a/examples/hello.rs b/examples/hello.rs index 232bfb37..d6647531 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -1,6 +1,6 @@ #[async_std::main] async fn main() -> Result<(), std::io::Error> { - // tide::log::start(); + tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); diff --git a/examples/json.rs b/examples/json.rs index 0b077a2e..e1c4030a 100644 --- a/examples/json.rs +++ b/examples/json.rs @@ -9,7 +9,7 @@ struct Cat { #[async_std::main] async fn main() -> tide::Result<()> { - // tide::log::start(); + tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); diff --git a/examples/middleware.rs b/examples/middleware.rs index 3e0589b8..17eec31b 100644 --- a/examples/middleware.rs +++ b/examples/middleware.rs @@ -84,7 +84,7 @@ const INTERNAL_SERVER_ERROR_HTML_PAGE: &str = " #[async_std::main] async fn main() -> Result<()> { - // tide::log::start(); + tide::log::start(); let mut app = tide::with_state(UserDatabase::default()); app.with(After(|response: Response| async move { diff --git a/examples/nested.rs b/examples/nested.rs index 4b1b027b..b9eece73 100644 --- a/examples/nested.rs +++ b/examples/nested.rs @@ -1,6 +1,6 @@ #[async_std::main] async fn main() -> Result<(), std::io::Error> { - // tide::log::start(); + tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); app.at("/").get(|_| async { Ok("Root") }); diff --git a/examples/redirect.rs b/examples/redirect.rs index 50b461c9..c0ab4c64 100644 --- a/examples/redirect.rs +++ b/examples/redirect.rs @@ -2,7 +2,7 @@ use tide::{Redirect, Response, StatusCode}; #[async_std::main] async fn main() -> Result<(), std::io::Error> { - // tide::log::start(); + tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); app.at("/").get(|_| async { Ok("Root") }); diff --git a/examples/sessions.rs b/examples/sessions.rs index 196b41df..c27c8ecc 100644 --- a/examples/sessions.rs +++ b/examples/sessions.rs @@ -1,6 +1,6 @@ #[async_std::main] async fn main() -> Result<(), std::io::Error> { - // tide::log::start(); + tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); diff --git a/examples/sse.rs b/examples/sse.rs index 90fb5a70..2f7d9fd7 100644 --- a/examples/sse.rs +++ b/examples/sse.rs @@ -2,7 +2,7 @@ use tide::sse; #[async_std::main] async fn main() -> Result<(), std::io::Error> { - // tide::log::start(); + tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); app.at("/sse").get(sse::endpoint(|_req, sender| async move { diff --git a/examples/state.rs b/examples/state.rs index c25aac40..d188dacc 100644 --- a/examples/state.rs +++ b/examples/state.rs @@ -16,7 +16,7 @@ impl State { #[async_std::main] async fn main() -> tide::Result<()> { - // tide::log::start(); + tide::log::start(); let mut app = tide::with_state(State::new()); app.with(tide::log::LogMiddleware::new()); app.at("/").get(|req: tide::Request| async move { diff --git a/examples/static_file.rs b/examples/static_file.rs index ab1a3b6d..6cd80a6a 100644 --- a/examples/static_file.rs +++ b/examples/static_file.rs @@ -1,6 +1,6 @@ #[async_std::main] async fn main() -> Result<(), std::io::Error> { - // tide::log::start(); + tide::log::start(); let mut app = tide::new(); app.with(tide::log::LogMiddleware::new()); app.at("/").get(|_| async { Ok("visit /src/*") }); diff --git a/examples/upload.rs b/examples/upload.rs index 42d44c88..5824f1dc 100644 --- a/examples/upload.rs +++ b/examples/upload.rs @@ -26,7 +26,7 @@ impl TempDirState { #[async_std::main] async fn main() -> Result<(), IoError> { - // tide::log::start(); + tide::log::start(); let mut app = tide::with_state(TempDirState::try_new()?); app.with(tide::log::LogMiddleware::new()); diff --git a/src/lib.rs b/src/lib.rs index 8785a5d1..d6e980af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,7 +32,7 @@ //! //! #[async_std::main] //! async fn main() -> tide::Result<()> { -//! // tide::log::start(); +//! tide::log::start(); //! let mut app = tide::new(); //! app.with(tide::log::LogMiddleware::new()); //! app.at("/orders/shoes").post(order_shoes); diff --git a/src/listener/concurrent_listener.rs b/src/listener/concurrent_listener.rs index 5d59cfb2..1a774b96 100644 --- a/src/listener/concurrent_listener.rs +++ b/src/listener/concurrent_listener.rs @@ -13,7 +13,7 @@ use futures_util::stream::{futures_unordered::FuturesUnordered, StreamExt}; /// ```rust /// fn main() -> Result<(), std::io::Error> { /// async_std::task::block_on(async { -/// // tide::log::start(); +/// tide::log::start(); /// let mut app = tide::new(); /// app.at("/").get(|_| async { Ok("Hello, world!") }); /// diff --git a/src/listener/failover_listener.rs b/src/listener/failover_listener.rs index 4fe37c2a..cceb2d23 100644 --- a/src/listener/failover_listener.rs +++ b/src/listener/failover_listener.rs @@ -15,7 +15,7 @@ use crate::listener::ListenInfo; /// ```rust /// fn main() -> Result<(), std::io::Error> { /// async_std::task::block_on(async { -/// // tide::log::start(); +/// tide::log::start(); /// let mut app = tide::new(); /// app.at("/").get(|_| async { Ok("Hello, world!") }); /// diff --git a/src/log/mod.rs b/src/log/mod.rs index 306687b1..8f74ac2d 100644 --- a/src/log/mod.rs +++ b/src/log/mod.rs @@ -5,7 +5,7 @@ //! ```no_run //! use tide::log; //! -//! // log::start(); +//! log::start(); //! //! log::info!("Hello cats"); //! log::debug!("{} wants tuna", "Nori");