aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml4
-rw-r--r--examples/windmark.rs11
-rw-r--r--src/lib.rs2
-rw-r--r--src/module.rs16
-rw-r--r--src/router.rs96
5 files changed, 109 insertions, 20 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 7074dc4..0fade46 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,7 +24,9 @@ response-macros = []
openssl = "0.10.38"
tokio-openssl = "0.6.3"
-tokio = { version = "1.26.0", features = ["full"] } # Non-blocking I/O
+# Non-blocking I/O
+tokio = { version = "1.26.0", features = ["full"] }
+async-trait = "0.1.68"
# Logging
pretty_env_logger = { version = "0.4.0", optional = true }
diff --git a/examples/windmark.rs b/examples/windmark.rs
index 5ddf7a3..9c19cc3 100644
--- a/examples/windmark.rs
+++ b/examples/windmark.rs
@@ -33,12 +33,13 @@ struct Clicker {
clicks: isize,
}
-impl windmark::Module for Clicker {
- fn on_attach(&mut self, _: &mut Router) {
+#[async_trait::async_trait]
+impl windmark::AsyncModule for Clicker {
+ async fn on_attach(&mut self, _: &mut Router) {
println!("clicker has been attached!");
}
- fn on_pre_route(&mut self, context: HookContext<'_>) {
+ async fn on_pre_route(&mut self, context: HookContext<'_>) {
self.clicks += 1;
info!(
@@ -48,7 +49,7 @@ impl windmark::Module for Clicker {
);
}
- fn on_post_route(&mut self, context: HookContext<'_>) {
+ async fn on_post_route(&mut self, context: HookContext<'_>) {
info!(
"clicker has been called post-route on {} with {} clicks!",
context.url.path(),
@@ -78,7 +79,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
router.attach_stateless(|r| {
r.mount("/module", success!("This is a module!"));
});
- router.attach(Clicker::default());
+ router.attach_async(Clicker::default());
router.set_pre_route_callback(|context| {
info!(
"accepted connection from {} to {}",
diff --git a/src/lib.rs b/src/lib.rs
index 7d4a80b..fd31430 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -40,7 +40,7 @@ pub mod utilities;
#[macro_use]
extern crate log;
-pub use module::Module;
+pub use module::{AsyncModule, Module};
pub use response::Response;
pub use router::Router;
pub use tokio::main;
diff --git a/src/module.rs b/src/module.rs
index 0c2a67b..e9ac743 100644
--- a/src/module.rs
+++ b/src/module.rs
@@ -16,15 +16,9 @@
// Copyright (C) 2022-2022 Fuwn <[email protected]>
// SPDX-License-Identifier: GPL-3.0-only
-use crate::{context::HookContext, Router};
+mod asynchronous;
+mod sync;
-pub trait Module {
- /// Called right after the module is attached.
- fn on_attach(&mut self, _: &mut Router) {}
-
- /// Called before a route is mounted.
- fn on_pre_route(&mut self, _: HookContext<'_>) {}
-
- /// Called after a route is mounted.
- fn on_post_route(&mut self, _: HookContext<'_>) {}
-}
+#[allow(clippy::module_name_repetitions)]
+pub use asynchronous::AsyncModule;
+pub use sync::Module;
diff --git a/src/router.rs b/src/router.rs
index 2744d4a..e1785dc 100644
--- a/src/router.rs
+++ b/src/router.rs
@@ -25,7 +25,10 @@ use std::{
};
use openssl::ssl::{self, SslAcceptor, SslMethod};
-use tokio::io::{AsyncReadExt, AsyncWriteExt};
+use tokio::{
+ io::{AsyncReadExt, AsyncWriteExt},
+ sync::Mutex as AsyncMutex,
+};
use url::Url;
use crate::{
@@ -37,7 +40,7 @@ use crate::{
PreRouteHook,
RouteResponse,
},
- module::Module,
+ module::{AsyncModule, Module},
response::Response,
};
@@ -76,6 +79,7 @@ pub struct Router {
character_set: String,
languages: Vec<String>,
port: i32,
+ async_modules: Arc<AsyncMutex<Vec<Box<dyn AsyncModule + Send>>>>,
modules: Arc<Mutex<Vec<Box<dyn Module + Send>>>>,
fix_path: bool,
}
@@ -352,6 +356,17 @@ impl Router {
};
let route = &mut self.routes.at(&fixed_path);
+ for module in &mut *self.async_modules.lock().await {
+ module
+ .on_pre_route(HookContext::new(
+ stream.get_ref(),
+ &url,
+ route.as_ref().map_or(None, |route| Some(&route.params)),
+ &stream.ssl().peer_certificate(),
+ ))
+ .await;
+ }
+
for module in &mut *self.modules.lock().unwrap() {
module.on_pre_route(HookContext::new(
stream.get_ref(),
@@ -420,6 +435,17 @@ impl Router {
))
};
+ for module in &mut *self.async_modules.lock().await {
+ module
+ .on_post_route(HookContext::new(
+ stream.get_ref(),
+ &url,
+ route.as_ref().map_or(None, |route| Some(&route.params)),
+ &stream.ssl().peer_certificate(),
+ ))
+ .await;
+ }
+
for module in &mut *self.modules.lock().unwrap() {
module.on_post_route(HookContext::new(
stream.get_ref(),
@@ -678,6 +704,71 @@ impl Router {
self
}
+ /// Attach a stateful module to a `Router`; with async support
+ ///
+ /// Like a stateless module is an extension or middleware to a `Router`.
+ /// Modules get full access to the `Router` and can be extended by a third
+ /// party, but also, can create hooks will be executed through various parts
+ /// of a routes' lifecycle. Stateful modules also have state, so variables can
+ /// be stored for further access.
+ ///
+ /// # Panics
+ ///
+ /// May panic if the stateful module cannot be attached.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use log::info;
+ /// use windmark::{context::HookContext, Response, Router};
+ ///
+ /// #[derive(Default)]
+ /// struct Clicker {
+ /// clicks: isize,
+ /// }
+ ///
+ /// #[async_trait::async_trait]
+ /// impl windmark::AsyncModule for Clicker {
+ /// async fn on_attach(&mut self, _: &mut Router) {
+ /// info!("clicker has been attached!");
+ /// }
+ ///
+ /// async fn on_pre_route(&mut self, context: HookContext<'_>) {
+ /// self.clicks += 1;
+ ///
+ /// info!(
+ /// "clicker has been called pre-route on {} with {} clicks!",
+ /// context.url.path(),
+ /// self.clicks
+ /// );
+ /// }
+ ///
+ /// async fn on_post_route(&mut self, context: HookContext<'_>) {
+ /// info!(
+ /// "clicker has been called post-route on {} with {} clicks!",
+ /// context.url.path(),
+ /// self.clicks
+ /// );
+ /// }
+ /// }
+ ///
+ /// Router::new().attach_async(Clicker::default());
+ /// ```
+ pub fn attach_async(
+ &mut self,
+ mut module: impl AsyncModule + 'static + Send,
+ ) -> &mut Self {
+ tokio::task::block_in_place(|| {
+ tokio::runtime::Handle::current().block_on(async {
+ module.on_attach(self).await;
+
+ (*self.async_modules.lock().await).push(Box::new(module));
+ });
+ });
+
+ self
+ }
+
/// Attach a stateful module to a `Router`.
///
/// Like a stateless module is an extension or middleware to a `Router`.
@@ -835,6 +926,7 @@ impl Default for Router {
languages: vec!["en".to_string()],
port: 1965,
modules: Arc::new(Mutex::new(vec![])),
+ async_modules: Arc::new(AsyncMutex::new(vec![])),
fix_path: false,
}
}