From 6ba899709758adb88145f09b208c81d35b1f915d Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Wed, 7 Aug 2024 08:32:36 +0800 Subject: [PATCH 01/49] =?UTF-8?q?refactor:=20exception=5Fhandler=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/exception_handler/src/main.rs | 11 +-- silent/src/error/exception_handler_trait.rs | 7 -- silent/src/error/exception_handler_wrapper.rs | 75 ------------------- silent/src/error/mod.rs | 5 -- .../middlewares/exception_handler.rs | 46 ++++++++++++ silent/src/middleware/middlewares/mod.rs | 2 + silent/src/route/root.rs | 20 +---- 7 files changed, 53 insertions(+), 113 deletions(-) delete mode 100644 silent/src/error/exception_handler_trait.rs delete mode 100644 silent/src/error/exception_handler_wrapper.rs create mode 100644 silent/src/middleware/middlewares/exception_handler.rs diff --git a/examples/exception_handler/src/main.rs b/examples/exception_handler/src/main.rs index 181ef18..4eb9698 100644 --- a/examples/exception_handler/src/main.rs +++ b/examples/exception_handler/src/main.rs @@ -1,4 +1,6 @@ use serde::{Deserialize, Serialize}; + +use silent::middlewares::ExceptionHandler; use silent::prelude::*; #[derive(Deserialize, Serialize, Debug)] @@ -10,13 +12,8 @@ struct Exception { fn main() { logger::fmt().with_max_level(Level::INFO).init(); let route = Route::new("") + .root_hook(ExceptionHandler::new(|res, _| async move { res })) .get(|mut req| async move { req.params_parse::() }) - .route() - .set_exception_handler(|e, _| async move { - Exception { - code: e.status().as_u16(), - msg: e.to_string(), - } - }); + .route(); Server::new().run(route); } diff --git a/silent/src/error/exception_handler_trait.rs b/silent/src/error/exception_handler_trait.rs deleted file mode 100644 index a1ebde3..0000000 --- a/silent/src/error/exception_handler_trait.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::{Configs, Response, SilentError}; -use async_trait::async_trait; - -#[async_trait] -pub trait ExceptionHandler: Send + Sync + 'static { - async fn call(&self, err: SilentError, configs: Configs) -> Response; -} diff --git a/silent/src/error/exception_handler_wrapper.rs b/silent/src/error/exception_handler_wrapper.rs deleted file mode 100644 index 8d4236d..0000000 --- a/silent/src/error/exception_handler_wrapper.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::error::exception_handler_trait::ExceptionHandler; -use crate::{Configs, Response, SilentError}; -use async_trait::async_trait; -use std::future::Future; -use std::sync::Arc; - -/// 处理器包装结构体 -/// 包含 -/// 请求类型: Option -/// 请求方法: Handler -/// 其中请求类型可为空,用来定义中间件 -/// 请求方法不可为空,用来定义处理器 -/// 处理器为返回值为 Into + From的异步函数或者闭包函数 -pub struct ExceptionHandlerWrapper { - handler: F, -} - -#[allow(dead_code)] -impl ExceptionHandlerWrapper -where - Fut: Future + Send + 'static, - F: Fn(SilentError, Configs) -> Fut, - T: Into, -{ - pub fn new(handler: F) -> Self { - Self { handler } - } - - pub async fn handle(&self, err: SilentError, configs: Configs) -> T { - (self.handler)(err, configs).await - } - - pub fn arc(self) -> Arc { - Arc::new(self) - } -} - -/// 为HandlerWrapper实现Handler -#[async_trait] -impl ExceptionHandler for ExceptionHandlerWrapper -where - Fut: Future + Send + 'static, - F: Fn(SilentError, Configs) -> Fut + Send + Sync + 'static, - T: Into, -{ - async fn call(&self, err: SilentError, configs: Configs) -> Response { - self.handle(err, configs).await.into() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::StatusCode; - - async fn exception_handler(err: SilentError, _configs: Configs) -> SilentError { - err - } - - #[tokio::test] - async fn handler_wrapper_match_req_works() { - let handler_wrapper = ExceptionHandlerWrapper::new(exception_handler); - let configs = Configs::default(); - assert_eq!( - handler_wrapper - .call( - SilentError::business_error(StatusCode::OK, "".to_string()), - configs - ) - .await - .status, - StatusCode::OK - ); - } -} diff --git a/silent/src/error/mod.rs b/silent/src/error/mod.rs index 3c02528..16bceab 100644 --- a/silent/src/error/mod.rs +++ b/silent/src/error/mod.rs @@ -1,10 +1,5 @@ -mod exception_handler_trait; -mod exception_handler_wrapper; - use crate::headers::ContentType; use crate::{Response, StatusCode}; -pub(crate) use exception_handler_trait::ExceptionHandler; -pub(crate) use exception_handler_wrapper::ExceptionHandlerWrapper; use serde::Serialize; use serde_json::Value; use std::backtrace::Backtrace; diff --git a/silent/src/middleware/middlewares/exception_handler.rs b/silent/src/middleware/middlewares/exception_handler.rs new file mode 100644 index 0000000..bbf6e6f --- /dev/null +++ b/silent/src/middleware/middlewares/exception_handler.rs @@ -0,0 +1,46 @@ +use std::future::Future; +use std::sync::Arc; + +use async_trait::async_trait; + +use crate::middleware::middleware_trait::Next; +use crate::{Configs, MiddleWareHandler, Request, Response, Result}; + +/// ExceptionHandler 中间件 +/// ```rust +/// use silent::prelude::*; +/// use silent::middlewares::{ExceptionHandler}; +/// // Define a custom error handler function +/// let _ = ExceptionHandler::new(|res, _configs| async {res}); +#[derive(Default, Clone)] +pub struct ExceptionHandler { + handler: Arc, +} + +impl ExceptionHandler +where + Fut: Future> + Send + 'static, + F: Fn(Result, Configs) -> Fut + Send + Sync + 'static, + T: Into, +{ + pub fn new(handler: F) -> Self { + Self { + handler: Arc::new(handler), + } + } +} + +#[async_trait] +impl MiddleWareHandler for ExceptionHandler +where + Fut: Future> + Send + 'static, + F: Fn(Result, Configs) -> Fut + Send + Sync + 'static, + T: Into, +{ + async fn handle(&self, req: Request, next: &Next) -> Result { + let configs = req.configs(); + self.handler.clone()(next.call(req).await, configs) + .await + .map(|r| r.into()) + } +} diff --git a/silent/src/middleware/middlewares/mod.rs b/silent/src/middleware/middlewares/mod.rs index 9d1ac49..ec7060a 100644 --- a/silent/src/middleware/middlewares/mod.rs +++ b/silent/src/middleware/middlewares/mod.rs @@ -1,3 +1,5 @@ mod cors; +mod exception_handler; pub use cors::{Cors, CorsType}; +pub use exception_handler::ExceptionHandler; diff --git a/silent/src/route/root.rs b/silent/src/route/root.rs index d4e7f99..fe72c20 100644 --- a/silent/src/route/root.rs +++ b/silent/src/route/root.rs @@ -1,4 +1,3 @@ -use crate::error::{ExceptionHandler, ExceptionHandlerWrapper}; #[cfg(feature = "grpc")] use crate::grpc::GrpcHandler; use crate::middleware::middleware_trait::Next; @@ -20,7 +19,6 @@ use chrono::Utc; #[cfg(feature = "grpc")] use http::Method; use std::fmt; -use std::future::Future; use std::sync::Arc; #[cfg(feature = "scheduler")] use tokio::sync::Mutex; @@ -35,7 +33,6 @@ use tonic::server::NamedService; pub struct RootRoute { pub(crate) children: Vec, pub(crate) middlewares: Vec>, - pub(crate) exception_handler: Option>, #[cfg(feature = "session")] pub(crate) session_set: bool, pub(crate) configs: Option, @@ -60,7 +57,6 @@ impl RootRoute { Self { children: vec![], middlewares: vec![], - exception_handler: None, #[cfg(feature = "session")] session_set: false, configs: None, @@ -121,16 +117,6 @@ impl RootRoute { .iter_mut() .for_each(|r| r.middleware_hook_first(handler.clone())); } - - pub fn set_exception_handler(mut self, handler: F) -> Self - where - Fut: Future + Send + 'static, - F: Fn(SilentError, Configs) -> Fut + Send + Sync + 'static, - T: Into, - { - self.exception_handler = Some(ExceptionHandlerWrapper::new(handler).arc()); - self - } pub(crate) fn set_configs(&mut self, configs: Option) { self.configs = configs; } @@ -140,7 +126,6 @@ impl RootRoute { impl Handler for RootRoute { async fn call(&self, mut req: Request) -> Result { tracing::debug!("{:?}", req); - let exception_handler = self.exception_handler.clone(); let configs = self.configs.clone().unwrap_or_default(); req.configs = configs.clone(); let method = req.method().clone(); @@ -193,10 +178,7 @@ impl Handler for RootRoute { req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0, e.to_string() ); - match exception_handler { - Some(handler) => handler.call(e, configs.clone()).await, - None => e.into(), - } + e.into() } }) } From 27dc0dadf737083e7806a0bc1cb37ef6eed80614 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Wed, 7 Aug 2024 09:28:34 +0800 Subject: [PATCH 02/49] =?UTF-8?q?refactor:=20next=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/middleware/src/main.rs | 1 - silent/src/core/mod.rs | 1 + silent/src/core/next.rs | 57 ++++++++++++++ silent/src/grpc/handler.rs | 21 +++++- silent/src/lib.rs | 6 +- silent/src/middleware/middleware_trait.rs | 60 +-------------- silent/src/middleware/middlewares/cors.rs | 3 +- silent/src/middleware/middlewares/mod.rs | 2 + .../middlewares/request_time_logger.rs | 74 +++++++++++++++++++ silent/src/middleware/mod.rs | 2 +- silent/src/route/mod.rs | 17 ++++- silent/src/route/root.rs | 3 +- silent/src/session/mod.rs | 3 +- silent/src/templates/middleware.rs | 3 +- 14 files changed, 177 insertions(+), 76 deletions(-) create mode 100644 silent/src/core/next.rs create mode 100644 silent/src/middleware/middlewares/request_time_logger.rs diff --git a/examples/middleware/src/main.rs b/examples/middleware/src/main.rs index 09b7d1a..ea83f01 100644 --- a/examples/middleware/src/main.rs +++ b/examples/middleware/src/main.rs @@ -1,5 +1,4 @@ use async_trait::async_trait; -use silent::middleware::middleware_trait::Next; use silent::prelude::*; use std::sync::atomic::AtomicUsize; diff --git a/silent/src/core/mod.rs b/silent/src/core/mod.rs index e2ca601..447e661 100644 --- a/silent/src/core/mod.rs +++ b/silent/src/core/mod.rs @@ -2,6 +2,7 @@ pub mod adapt; #[cfg(feature = "multipart")] pub(crate) mod form; +pub(crate) mod next; pub(crate) mod path_param; pub(crate) mod req_body; pub(crate) mod request; diff --git a/silent/src/core/next.rs b/silent/src/core/next.rs new file mode 100644 index 0000000..e594902 --- /dev/null +++ b/silent/src/core/next.rs @@ -0,0 +1,57 @@ +use crate::{Handler, MiddleWareHandler, Request, Response}; +use async_trait::async_trait; +use std::sync::Arc; + +/// The `Next` struct is used to chain multiple middlewares and endpoints together. +pub struct Next { + inner: NextInstance, + next: Option>, +} + +pub(crate) enum NextInstance { + Middleware(Arc), + EndPoint(Arc), +} + +impl Next { + pub(crate) fn build( + endpoint: Arc, + mut middlewares: Vec>, + ) -> Self { + let end_point = Next { + inner: NextInstance::EndPoint(endpoint), + next: None, + }; + if middlewares.is_empty() { + end_point + } else { + let next = Next { + inner: NextInstance::Middleware(middlewares.pop().unwrap()), + next: Some(Arc::new(end_point)), + }; + middlewares.into_iter().fold(next, |next, mw| Next { + inner: NextInstance::Middleware(mw), + next: Some(Arc::new(next)), + }) + } + } +} + +#[async_trait] +impl Handler for Next { + async fn call(&self, req: Request) -> crate::Result { + match &self.inner { + NextInstance::Middleware(mw) => { + mw.handle(req, self.next.clone().unwrap().as_ref()).await + } + NextInstance::EndPoint(ep) => ep.call(req).await, + } + } +} + +#[async_trait] +impl MiddleWareHandler for Next { + async fn handle(&self, req: Request, next: &Next) -> crate::Result { + next.call(req).await + } +} diff --git a/silent/src/grpc/handler.rs b/silent/src/grpc/handler.rs index e010083..ad6f145 100644 --- a/silent/src/grpc/handler.rs +++ b/silent/src/grpc/handler.rs @@ -1,9 +1,12 @@ use std::sync::Arc; +use super::utils::merge_grpc_response; use crate::grpc::service::GrpcService; +use crate::prelude::HandlerGetter; +use crate::route::Route; use crate::{Handler, Response, SilentError}; use async_trait::async_trait; -use http::{header, HeaderValue, StatusCode}; +use http::{header, HeaderValue, Method, StatusCode}; use http_body_util::BodyExt; use hyper::upgrade::OnUpgrade; use hyper_util::rt::TokioExecutor; @@ -14,8 +17,6 @@ use tonic::server::NamedService; use tonic::Status; use tracing::{error, info}; -use super::utils::merge_grpc_response; - trait GrpcRequestAdapter { fn into_grpc_request(self) -> http::Request; } @@ -54,6 +55,20 @@ where pub fn path(&self) -> &str { S::NAME } + + pub fn service(self) -> Route { + let path = self.path().to_string(); + let handler = Arc::new(self); + Route::new(path.as_str()).append( + Route::new("") + .insert_handler(Method::POST, handler.clone()) + .insert_handler(Method::GET, handler), + ) + } + + pub fn register(self, route: &mut Route) { + route.extend(self.service()); + } } impl From for GrpcHandler diff --git a/silent/src/lib.rs b/silent/src/lib.rs index 20ec701..4f999a6 100644 --- a/silent/src/lib.rs +++ b/silent/src/lib.rs @@ -31,7 +31,7 @@ mod ws; use multer; pub use crate::configs::Configs; -pub use crate::core::{request::Request, response::Response}; +pub use crate::core::{next::Next, request::Request, response::Response}; pub use crate::middleware::{middlewares, MiddleWareHandler}; pub use error::SilentError; pub use error::SilentResult as Result; @@ -47,7 +47,7 @@ pub mod prelude { #[cfg(feature = "multipart")] pub use crate::core::form::{FilePart, FormData}; pub use crate::core::{ - path_param::PathParam, req_body::ReqBody, request::Request, res_body::full, + next::Next, path_param::PathParam, req_body::ReqBody, request::Request, res_body::full, res_body::stream_body, res_body::ResBody, response::Response, }; pub use crate::error::{SilentError, SilentResult as Result}; @@ -56,7 +56,7 @@ pub mod prelude { pub use crate::handler::Handler; pub use crate::handler::HandlerWrapper; pub use crate::log::*; - pub use crate::middleware::{MiddleWareHandler, Next}; + pub use crate::middleware::MiddleWareHandler; #[cfg(feature = "upgrade")] pub use crate::route::handler_append::WSHandlerAppend; pub use crate::route::handler_append::{HandlerAppend, HandlerGetter}; diff --git a/silent/src/middleware/middleware_trait.rs b/silent/src/middleware/middleware_trait.rs index f7f4dc6..403083f 100644 --- a/silent/src/middleware/middleware_trait.rs +++ b/silent/src/middleware/middleware_trait.rs @@ -1,53 +1,7 @@ -use std::sync::Arc; - +use crate::core::next::Next; +use crate::{Request, Response, Result}; use async_trait::async_trait; -use crate::{Handler, Request, Response, Result}; - -pub struct Next { - inner: NextInstance, - next: Option>, -} - -pub(crate) enum NextInstance { - Middleware(Arc), - EndPoint(Arc), -} - -impl Next { - pub(crate) fn build( - endpoint: Arc, - mut middlewares: Vec>, - ) -> Self { - let end_point = Next { - inner: NextInstance::EndPoint(endpoint), - next: None, - }; - if middlewares.is_empty() { - end_point - } else { - let next = Next { - inner: NextInstance::Middleware(middlewares.pop().unwrap()), - next: Some(Arc::new(end_point)), - }; - middlewares.into_iter().fold(next, |next, mw| Next { - inner: NextInstance::Middleware(mw), - next: Some(Arc::new(next)), - }) - } - } -} - -impl Next { - pub async fn call(&self, req: Request) -> Result { - match &self.inner { - NextInstance::Middleware(mw) => { - mw.handle(req, self.next.clone().unwrap().as_ref()).await - } - NextInstance::EndPoint(ep) => ep.call(req).await, - } - } -} #[async_trait] pub trait MiddleWareHandler: Send + Sync + 'static { async fn match_req(&self, _req: &Request) -> bool { @@ -56,16 +10,10 @@ pub trait MiddleWareHandler: Send + Sync + 'static { async fn handle(&self, _req: Request, _next: &Next) -> Result; } -#[async_trait] -impl MiddleWareHandler for Next { - async fn handle(&self, req: Request, next: &Next) -> Result { - next.call(req).await - } -} - #[cfg(test)] mod tests { - use crate::HandlerWrapper; + use crate::{Handler, HandlerWrapper}; + use std::sync::Arc; use tracing::info; use super::*; diff --git a/silent/src/middleware/middlewares/cors.rs b/silent/src/middleware/middlewares/cors.rs index 880ac3e..bb62b1f 100644 --- a/silent/src/middleware/middlewares/cors.rs +++ b/silent/src/middleware/middlewares/cors.rs @@ -1,5 +1,4 @@ -use crate::middleware::middleware_trait::Next; -use crate::{MiddleWareHandler, Request, Response, Result, SilentError}; +use crate::{Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError}; use async_trait::async_trait; use http::{header, Method}; diff --git a/silent/src/middleware/middlewares/mod.rs b/silent/src/middleware/middlewares/mod.rs index 9d1ac49..ba2ab3d 100644 --- a/silent/src/middleware/middlewares/mod.rs +++ b/silent/src/middleware/middlewares/mod.rs @@ -1,3 +1,5 @@ mod cors; +mod request_time_logger; pub use cors::{Cors, CorsType}; +pub use request_time_logger::RequestTimeLogger; diff --git a/silent/src/middleware/middlewares/request_time_logger.rs b/silent/src/middleware/middlewares/request_time_logger.rs new file mode 100644 index 0000000..b179057 --- /dev/null +++ b/silent/src/middleware/middlewares/request_time_logger.rs @@ -0,0 +1,74 @@ +use crate::{Handler, MiddleWareHandler, Next, Request, Response, Result}; +use async_trait::async_trait; +use chrono::Utc; + +/// ExceptionHandler 中间件 +/// ```rust +/// use silent::prelude::*; +/// use silent::middlewares::{RequestTimeLogger}; +/// // Define a request time logger middleware +/// let _ = RequestTimeLogger::new(); +#[derive(Default, Clone)] +pub struct RequestTimeLogger; + +impl RequestTimeLogger { + pub fn new() -> Self { + Self {} + } +} + +#[async_trait] +impl MiddleWareHandler for RequestTimeLogger { + async fn handle(&self, req: Request, next: &Next) -> Result { + let method = req.method().clone(); + let url = req.uri().to_string().clone(); + let http_version = req.version(); + let peer_addr = req.remote(); + let start_time = Utc::now().time(); + let res = next.call(req).await; + let end_time = Utc::now().time(); + let req_time = end_time - start_time; + Ok(match res { + Ok(res) => { + if res.status.as_u16() >= 400 { + tracing::info!( + "{} {} {} {:?} {} {:?} {}", + peer_addr, + method, + url, + http_version, + res.status.as_u16(), + res.content_length().lower(), + req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0 + ); + } else { + tracing::warn!( + "{} {} {} {:?} {} {:?} {}", + peer_addr, + method, + url, + http_version, + res.status.as_u16(), + res.content_length().lower(), + req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0 + ); + } + res + } + Err(e) => { + tracing::error!( + "{} {} {} {:?} {} {:?} {} {}", + peer_addr, + method, + url, + http_version, + e.status().as_u16(), + 0, + req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0, + e.to_string() + ); + e.into() + } + }) + } +} diff --git a/silent/src/middleware/mod.rs b/silent/src/middleware/mod.rs index d87d004..7999e11 100644 --- a/silent/src/middleware/mod.rs +++ b/silent/src/middleware/mod.rs @@ -1,4 +1,4 @@ pub mod middleware_trait; pub mod middlewares; -pub use middleware_trait::{MiddleWareHandler, Next}; +pub use middleware_trait::MiddleWareHandler; diff --git a/silent/src/route/mod.rs b/silent/src/route/mod.rs index a959acf..1c024b5 100644 --- a/silent/src/route/mod.rs +++ b/silent/src/route/mod.rs @@ -11,10 +11,10 @@ use std::sync::Arc; #[cfg(feature = "static")] use crate::handler::static_handler; use crate::handler::Handler; -use crate::middleware::{MiddleWareHandler, Next}; +use crate::middleware::MiddleWareHandler; #[cfg(feature = "static")] use crate::prelude::HandlerGetter; -use crate::{Method, Request, Response, SilentError}; +use crate::{Method, Next, Request, Response, SilentError}; pub(crate) mod handler_append; mod handler_match; @@ -125,6 +125,16 @@ impl Route { real_route.children.push(route); self } + pub fn extend(&mut self, route: R) { + let mut route = route.into_router(); + self.root_middlewares.extend(route.root_middlewares.clone()); + self.middlewares + .iter() + .cloned() + .for_each(|m| route.middleware_hook(m.clone())); + let real_route = self.get_append_real_route(&self.create_path.clone()); + real_route.children.push(route); + } pub fn root_hook(mut self, handler: impl MiddleWareHandler + 'static) -> Self { self.root_middlewares.push(Arc::new(handler)); self @@ -205,8 +215,7 @@ impl Handler for Route { #[cfg(test)] mod tests { - use crate::middleware::middleware_trait::Next; - use crate::{Request, Response}; + use crate::{Next, Request, Response}; use super::*; diff --git a/silent/src/route/root.rs b/silent/src/route/root.rs index d4e7f99..46fc58a 100644 --- a/silent/src/route/root.rs +++ b/silent/src/route/root.rs @@ -1,7 +1,6 @@ use crate::error::{ExceptionHandler, ExceptionHandlerWrapper}; #[cfg(feature = "grpc")] use crate::grpc::GrpcHandler; -use crate::middleware::middleware_trait::Next; #[cfg(feature = "grpc")] use crate::prelude::HandlerGetter; use crate::route::handler_match::{Match, RouteMatched}; @@ -12,7 +11,7 @@ use crate::session::SessionMiddleware; use crate::templates::TemplateMiddleware; #[cfg(feature = "scheduler")] use crate::Scheduler; -use crate::{Configs, Handler, MiddleWareHandler, Request, Response, SilentError}; +use crate::{Configs, Handler, MiddleWareHandler, Next, Request, Response, SilentError}; #[cfg(feature = "session")] use async_session::SessionStore; use async_trait::async_trait; diff --git a/silent/src/session/mod.rs b/silent/src/session/mod.rs index 24a8825..e6ca309 100644 --- a/silent/src/session/mod.rs +++ b/silent/src/session/mod.rs @@ -1,5 +1,4 @@ -use crate::middleware::middleware_trait::Next; -use crate::{MiddleWareHandler, Request, Response, Result, SilentError, StatusCode}; +use crate::{Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError, StatusCode}; use async_session::{MemoryStore, Session, SessionStore}; use async_trait::async_trait; use cookie::Cookie; diff --git a/silent/src/templates/middleware.rs b/silent/src/templates/middleware.rs index 45be9c8..fea7804 100644 --- a/silent/src/templates/middleware.rs +++ b/silent/src/templates/middleware.rs @@ -1,5 +1,4 @@ -use crate::middleware::middleware_trait::Next; -use crate::{MiddleWareHandler, Request, Response, Result, SilentError, StatusCode}; +use crate::{Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError, StatusCode}; use async_trait::async_trait; use serde::Serialize; use serde_json::Value; From c353332a37f5f201a18050ae4cf78945e51503bb Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Wed, 7 Aug 2024 09:56:27 +0800 Subject: [PATCH 03/49] =?UTF-8?q?refactor:=20next=E9=87=8D=E6=9E=84,=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9Erequest=5Ftime=5Flogger=E4=B8=AD=E9=97=B4?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/exception_handler/src/main.rs | 8 +- silent/src/error/exception_handler_trait.rs | 7 -- silent/src/error/exception_handler_wrapper.rs | 75 ----------------- silent/src/error/mod.rs | 5 -- .../middlewares/request_time_logger.rs | 4 +- silent/src/route/handler_match.rs | 1 + silent/src/route/root.rs | 83 +++++-------------- 7 files changed, 27 insertions(+), 156 deletions(-) delete mode 100644 silent/src/error/exception_handler_trait.rs delete mode 100644 silent/src/error/exception_handler_wrapper.rs diff --git a/examples/exception_handler/src/main.rs b/examples/exception_handler/src/main.rs index 181ef18..98e73b1 100644 --- a/examples/exception_handler/src/main.rs +++ b/examples/exception_handler/src/main.rs @@ -11,12 +11,6 @@ fn main() { logger::fmt().with_max_level(Level::INFO).init(); let route = Route::new("") .get(|mut req| async move { req.params_parse::() }) - .route() - .set_exception_handler(|e, _| async move { - Exception { - code: e.status().as_u16(), - msg: e.to_string(), - } - }); + .route(); Server::new().run(route); } diff --git a/silent/src/error/exception_handler_trait.rs b/silent/src/error/exception_handler_trait.rs deleted file mode 100644 index a1ebde3..0000000 --- a/silent/src/error/exception_handler_trait.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::{Configs, Response, SilentError}; -use async_trait::async_trait; - -#[async_trait] -pub trait ExceptionHandler: Send + Sync + 'static { - async fn call(&self, err: SilentError, configs: Configs) -> Response; -} diff --git a/silent/src/error/exception_handler_wrapper.rs b/silent/src/error/exception_handler_wrapper.rs deleted file mode 100644 index 8d4236d..0000000 --- a/silent/src/error/exception_handler_wrapper.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::error::exception_handler_trait::ExceptionHandler; -use crate::{Configs, Response, SilentError}; -use async_trait::async_trait; -use std::future::Future; -use std::sync::Arc; - -/// 处理器包装结构体 -/// 包含 -/// 请求类型: Option -/// 请求方法: Handler -/// 其中请求类型可为空,用来定义中间件 -/// 请求方法不可为空,用来定义处理器 -/// 处理器为返回值为 Into + From的异步函数或者闭包函数 -pub struct ExceptionHandlerWrapper { - handler: F, -} - -#[allow(dead_code)] -impl ExceptionHandlerWrapper -where - Fut: Future + Send + 'static, - F: Fn(SilentError, Configs) -> Fut, - T: Into, -{ - pub fn new(handler: F) -> Self { - Self { handler } - } - - pub async fn handle(&self, err: SilentError, configs: Configs) -> T { - (self.handler)(err, configs).await - } - - pub fn arc(self) -> Arc { - Arc::new(self) - } -} - -/// 为HandlerWrapper实现Handler -#[async_trait] -impl ExceptionHandler for ExceptionHandlerWrapper -where - Fut: Future + Send + 'static, - F: Fn(SilentError, Configs) -> Fut + Send + Sync + 'static, - T: Into, -{ - async fn call(&self, err: SilentError, configs: Configs) -> Response { - self.handle(err, configs).await.into() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::StatusCode; - - async fn exception_handler(err: SilentError, _configs: Configs) -> SilentError { - err - } - - #[tokio::test] - async fn handler_wrapper_match_req_works() { - let handler_wrapper = ExceptionHandlerWrapper::new(exception_handler); - let configs = Configs::default(); - assert_eq!( - handler_wrapper - .call( - SilentError::business_error(StatusCode::OK, "".to_string()), - configs - ) - .await - .status, - StatusCode::OK - ); - } -} diff --git a/silent/src/error/mod.rs b/silent/src/error/mod.rs index 3c02528..16bceab 100644 --- a/silent/src/error/mod.rs +++ b/silent/src/error/mod.rs @@ -1,10 +1,5 @@ -mod exception_handler_trait; -mod exception_handler_wrapper; - use crate::headers::ContentType; use crate::{Response, StatusCode}; -pub(crate) use exception_handler_trait::ExceptionHandler; -pub(crate) use exception_handler_wrapper::ExceptionHandlerWrapper; use serde::Serialize; use serde_json::Value; use std::backtrace::Backtrace; diff --git a/silent/src/middleware/middlewares/request_time_logger.rs b/silent/src/middleware/middlewares/request_time_logger.rs index b179057..d158ece 100644 --- a/silent/src/middleware/middlewares/request_time_logger.rs +++ b/silent/src/middleware/middlewares/request_time_logger.rs @@ -31,7 +31,7 @@ impl MiddleWareHandler for RequestTimeLogger { Ok(match res { Ok(res) => { if res.status.as_u16() >= 400 { - tracing::info!( + tracing::warn!( "{} {} {} {:?} {} {:?} {}", peer_addr, method, @@ -42,7 +42,7 @@ impl MiddleWareHandler for RequestTimeLogger { req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0 ); } else { - tracing::warn!( + tracing::info!( "{} {} {} {:?} {} {:?} {}", peer_addr, method, diff --git a/silent/src/route/handler_match.rs b/silent/src/route/handler_match.rs index 12b45e8..ae49750 100644 --- a/silent/src/route/handler_match.rs +++ b/silent/src/route/handler_match.rs @@ -2,6 +2,7 @@ use super::{RootRoute, Route}; use crate::core::path_param::PathParam; use crate::Request; +#[derive(Debug, Clone)] pub(crate) enum RouteMatched { Matched(Route), Unmatched, diff --git a/silent/src/route/root.rs b/silent/src/route/root.rs index 46fc58a..69b23d4 100644 --- a/silent/src/route/root.rs +++ b/silent/src/route/root.rs @@ -1,6 +1,6 @@ -use crate::error::{ExceptionHandler, ExceptionHandlerWrapper}; #[cfg(feature = "grpc")] use crate::grpc::GrpcHandler; +use crate::middlewares::RequestTimeLogger; #[cfg(feature = "grpc")] use crate::prelude::HandlerGetter; use crate::route::handler_match::{Match, RouteMatched}; @@ -15,11 +15,9 @@ use crate::{Configs, Handler, MiddleWareHandler, Next, Request, Response, Silent #[cfg(feature = "session")] use async_session::SessionStore; use async_trait::async_trait; -use chrono::Utc; #[cfg(feature = "grpc")] use http::Method; use std::fmt; -use std::future::Future; use std::sync::Arc; #[cfg(feature = "scheduler")] use tokio::sync::Mutex; @@ -34,7 +32,6 @@ use tonic::server::NamedService; pub struct RootRoute { pub(crate) children: Vec, pub(crate) middlewares: Vec>, - pub(crate) exception_handler: Option>, #[cfg(feature = "session")] pub(crate) session_set: bool, pub(crate) configs: Option, @@ -59,7 +56,6 @@ impl RootRoute { Self { children: vec![], middlewares: vec![], - exception_handler: None, #[cfg(feature = "session")] session_set: false, configs: None, @@ -121,32 +117,34 @@ impl RootRoute { .for_each(|r| r.middleware_hook_first(handler.clone())); } - pub fn set_exception_handler(mut self, handler: F) -> Self - where - Fut: Future + Send + 'static, - F: Fn(SilentError, Configs) -> Fut + Send + Sync + 'static, - T: Into, - { - self.exception_handler = Some(ExceptionHandlerWrapper::new(handler).arc()); - self - } pub(crate) fn set_configs(&mut self, configs: Option) { self.configs = configs; } } +struct RootHandler { + inner: RouteMatched, + middlewares: Vec>, +} +#[async_trait] +impl Handler for RootHandler { + async fn call(&self, req: Request) -> Result { + match self.inner.clone() { + RouteMatched::Matched(route) => { + let next = Next::build(Arc::new(route), self.middlewares.clone()); + next.call(req).await + } + RouteMatched::Unmatched => Err(SilentError::NotFound), + } + } +} + #[async_trait] impl Handler for RootRoute { async fn call(&self, mut req: Request) -> Result { tracing::debug!("{:?}", req); - let exception_handler = self.exception_handler.clone(); let configs = self.configs.clone().unwrap_or_default(); req.configs = configs.clone(); - let method = req.method().clone(); - let url = req.uri().to_string().clone(); - let http_version = req.version(); - let peer_addr = req.remote(); - let start_time = Utc::now().time(); #[cfg(feature = "scheduler")] req.extensions_mut().insert(self.scheduler.clone()); @@ -157,47 +155,12 @@ impl Handler for RootRoute { } } let (mut req, path) = req.split_url(); - let res = match self.handler_match(&mut req, path.as_str()) { - RouteMatched::Matched(route) => { - let next = Next::build(Arc::new(route), root_middlewares); - next.call(req).await - } - RouteMatched::Unmatched => Err(SilentError::NotFound), + let handler = RootHandler { + inner: self.handler_match(&mut req, &path), + middlewares: root_middlewares, }; - let end_time = Utc::now().time(); - let req_time = end_time - start_time; - Ok(match res { - Ok(res) => { - tracing::info!( - "{} {} {} {:?} {} {:?} {}", - peer_addr, - method, - url, - http_version, - res.status.as_u16(), - res.content_length().lower(), - req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0 - ); - res - } - Err(e) => { - tracing::error!( - "{} {} {} {:?} {} {:?} {} {}", - peer_addr, - method, - url, - http_version, - e.status().as_u16(), - 0, - req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0, - e.to_string() - ); - match exception_handler { - Some(handler) => handler.call(e, configs.clone()).await, - None => e.into(), - } - } - }) + let next = Next::build(Arc::new(handler), vec![Arc::new(RequestTimeLogger::new())]); + next.call(req).await } } From dbeef2de28d8b1d07b2c55c319509d7f1573dde3 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Wed, 7 Aug 2024 08:32:36 +0800 Subject: [PATCH 04/49] =?UTF-8?q?refactor:=20exception=5Fhandler=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/exception_handler/src/main.rs | 3 ++ .../middlewares/exception_handler.rs | 46 +++++++++++++++++++ silent/src/middleware/middlewares/mod.rs | 2 + 3 files changed, 51 insertions(+) create mode 100644 silent/src/middleware/middlewares/exception_handler.rs diff --git a/examples/exception_handler/src/main.rs b/examples/exception_handler/src/main.rs index 98e73b1..4eb9698 100644 --- a/examples/exception_handler/src/main.rs +++ b/examples/exception_handler/src/main.rs @@ -1,4 +1,6 @@ use serde::{Deserialize, Serialize}; + +use silent::middlewares::ExceptionHandler; use silent::prelude::*; #[derive(Deserialize, Serialize, Debug)] @@ -10,6 +12,7 @@ struct Exception { fn main() { logger::fmt().with_max_level(Level::INFO).init(); let route = Route::new("") + .root_hook(ExceptionHandler::new(|res, _| async move { res })) .get(|mut req| async move { req.params_parse::() }) .route(); Server::new().run(route); diff --git a/silent/src/middleware/middlewares/exception_handler.rs b/silent/src/middleware/middlewares/exception_handler.rs new file mode 100644 index 0000000..bbf6e6f --- /dev/null +++ b/silent/src/middleware/middlewares/exception_handler.rs @@ -0,0 +1,46 @@ +use std::future::Future; +use std::sync::Arc; + +use async_trait::async_trait; + +use crate::middleware::middleware_trait::Next; +use crate::{Configs, MiddleWareHandler, Request, Response, Result}; + +/// ExceptionHandler 中间件 +/// ```rust +/// use silent::prelude::*; +/// use silent::middlewares::{ExceptionHandler}; +/// // Define a custom error handler function +/// let _ = ExceptionHandler::new(|res, _configs| async {res}); +#[derive(Default, Clone)] +pub struct ExceptionHandler { + handler: Arc, +} + +impl ExceptionHandler +where + Fut: Future> + Send + 'static, + F: Fn(Result, Configs) -> Fut + Send + Sync + 'static, + T: Into, +{ + pub fn new(handler: F) -> Self { + Self { + handler: Arc::new(handler), + } + } +} + +#[async_trait] +impl MiddleWareHandler for ExceptionHandler +where + Fut: Future> + Send + 'static, + F: Fn(Result, Configs) -> Fut + Send + Sync + 'static, + T: Into, +{ + async fn handle(&self, req: Request, next: &Next) -> Result { + let configs = req.configs(); + self.handler.clone()(next.call(req).await, configs) + .await + .map(|r| r.into()) + } +} diff --git a/silent/src/middleware/middlewares/mod.rs b/silent/src/middleware/middlewares/mod.rs index ba2ab3d..3a9c052 100644 --- a/silent/src/middleware/middlewares/mod.rs +++ b/silent/src/middleware/middlewares/mod.rs @@ -1,5 +1,7 @@ mod cors; +mod exception_handler; mod request_time_logger; pub use cors::{Cors, CorsType}; +pub use exception_handler::ExceptionHandler; pub use request_time_logger::RequestTimeLogger; From 3c68909892f8f083e7e41306b3e925865a68911e Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Wed, 7 Aug 2024 16:58:14 +0800 Subject: [PATCH 05/49] =?UTF-8?q?exception=5Fhandler=20=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- silent/src/middleware/middlewares/exception_handler.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/silent/src/middleware/middlewares/exception_handler.rs b/silent/src/middleware/middlewares/exception_handler.rs index bbf6e6f..94c5440 100644 --- a/silent/src/middleware/middlewares/exception_handler.rs +++ b/silent/src/middleware/middlewares/exception_handler.rs @@ -3,8 +3,7 @@ use std::sync::Arc; use async_trait::async_trait; -use crate::middleware::middleware_trait::Next; -use crate::{Configs, MiddleWareHandler, Request, Response, Result}; +use crate::{Configs, Handler, MiddleWareHandler, Next, Request, Response, Result}; /// ExceptionHandler 中间件 /// ```rust From 1b559f61d9658be7b27fb03448c527555ec78288 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Wed, 7 Aug 2024 17:35:44 +0800 Subject: [PATCH 06/49] =?UTF-8?q?refactor:=20grpc=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/grpc/src/main.rs | 12 ++++---- examples/grpc_h2c/src/main.rs | 9 +++--- examples/grpc_streaming/src/main.rs | 6 ++-- silent/src/grpc/handler.rs | 18 +----------- silent/src/grpc/mod.rs | 2 ++ silent/src/grpc/register.rs | 41 +++++++++++++++++++++++++++ silent/src/lib.rs | 4 +++ silent/src/route/root.rs | 44 ----------------------------- 8 files changed, 63 insertions(+), 73 deletions(-) create mode 100644 silent/src/grpc/register.rs diff --git a/examples/grpc/src/main.rs b/examples/grpc/src/main.rs index 244f9c6..9db735e 100644 --- a/examples/grpc/src/main.rs +++ b/examples/grpc/src/main.rs @@ -3,7 +3,8 @@ use tonic::{Request, Response, Status}; use hello_world::greeter_server::{Greeter, GreeterServer}; use hello_world::{HelloReply, HelloRequest}; -use silent::prelude::{info, logger, HandlerAppend, Level, Route, RouteService, Server}; +use silent::prelude::{info, logger, HandlerAppend, Level, Route, Server}; +use silent::GrpcRegister; mod client; @@ -40,12 +41,13 @@ async fn main() -> Result<(), Box> { // // Wrap all services in the middleware stack // .add_service(greeter_server) // .into_router(); - let route = Route::new("").get(|_req| async { Ok("hello world") }); - let root = route.route().with_grpc(greeter_server.into()); - info!("route: \n{:?}", root); + let route = Route::new("") + .get(|_req| async { Ok("hello world") }) + .append(greeter_server.service()); + info!("route: \n{:?}", route); Server::new() .bind("0.0.0.0:50051".parse().unwrap()) - .serve(root) + .serve(route) .await; Ok(()) } diff --git a/examples/grpc_h2c/src/main.rs b/examples/grpc_h2c/src/main.rs index b247e04..8aecac6 100644 --- a/examples/grpc_h2c/src/main.rs +++ b/examples/grpc_h2c/src/main.rs @@ -3,7 +3,8 @@ use tonic::{Request, Response, Status}; use hello_world::greeter_server::{Greeter, GreeterServer}; use hello_world::{HelloReply, HelloRequest}; -use silent::prelude::{info, logger, HandlerAppend, Level, Route, RouteService}; +use silent::prelude::{info, logger, HandlerAppend, Level, Route}; +use silent::GrpcRegister; pub mod hello_world { tonic::include_proto!("helloworld"); @@ -32,11 +33,11 @@ async fn main() -> Result<(), Box> { let greeter = MyGreeter::default(); logger::fmt().with_max_level(Level::INFO).init(); - let route = Route::new("").get(|_req| async { Ok("hello world") }); - let root = route.route().with_grpc(GreeterServer::new(greeter).into()); + let mut route = Route::new("").get(|_req| async { Ok("hello world") }); + GreeterServer::new(greeter).register(&mut route); silent::prelude::Server::new() .bind("0.0.0.0:50051".parse().unwrap()) - .serve(root) + .serve(route) .await; Ok(()) } diff --git a/examples/grpc_streaming/src/main.rs b/examples/grpc_streaming/src/main.rs index a30abc1..e533559 100644 --- a/examples/grpc_streaming/src/main.rs +++ b/examples/grpc_streaming/src/main.rs @@ -6,6 +6,7 @@ use tonic::{Request, Response, Status, Streaming}; use pb::{EchoRequest, EchoResponse}; use silent::prelude::{info, logger, HandlerAppend, Level, Route, RouteService, Server}; +use silent::GrpcRegister; mod client; @@ -150,9 +151,8 @@ async fn main() -> Result<(), Box> { logger::fmt().with_max_level(Level::INFO).init(); let server = EchoServer {}; let route = Route::new("").get(|_req| async { Ok("hello world") }); - let root = route - .route() - .with_grpc(pb::echo_server::EchoServer::new(server).into()); + let mut root = route.route(); + root.push(pb::echo_server::EchoServer::new(server).service()); Server::new() .bind("0.0.0.0:50051".parse().unwrap()) .serve(root) diff --git a/silent/src/grpc/handler.rs b/silent/src/grpc/handler.rs index ad6f145..d0c962b 100644 --- a/silent/src/grpc/handler.rs +++ b/silent/src/grpc/handler.rs @@ -2,11 +2,9 @@ use std::sync::Arc; use super::utils::merge_grpc_response; use crate::grpc::service::GrpcService; -use crate::prelude::HandlerGetter; -use crate::route::Route; use crate::{Handler, Response, SilentError}; use async_trait::async_trait; -use http::{header, HeaderValue, Method, StatusCode}; +use http::{header, HeaderValue, StatusCode}; use http_body_util::BodyExt; use hyper::upgrade::OnUpgrade; use hyper_util::rt::TokioExecutor; @@ -55,20 +53,6 @@ where pub fn path(&self) -> &str { S::NAME } - - pub fn service(self) -> Route { - let path = self.path().to_string(); - let handler = Arc::new(self); - Route::new(path.as_str()).append( - Route::new("") - .insert_handler(Method::POST, handler.clone()) - .insert_handler(Method::GET, handler), - ) - } - - pub fn register(self, route: &mut Route) { - route.extend(self.service()); - } } impl From for GrpcHandler diff --git a/silent/src/grpc/mod.rs b/silent/src/grpc/mod.rs index 91c6c1a..cfbd1f9 100644 --- a/silent/src/grpc/mod.rs +++ b/silent/src/grpc/mod.rs @@ -1,6 +1,8 @@ mod handler; +mod register; mod service; mod utils; // mod stream; pub use handler::GrpcHandler; +pub use register::GrpcRegister; diff --git a/silent/src/grpc/register.rs b/silent/src/grpc/register.rs new file mode 100644 index 0000000..eb7b367 --- /dev/null +++ b/silent/src/grpc/register.rs @@ -0,0 +1,41 @@ +use crate::grpc::GrpcHandler; +use crate::prelude::HandlerGetter; +use crate::prelude::Route; +use http::Method; +use std::sync::Arc; +use tonic::body::BoxBody; +use tonic::codegen::Service; +use tonic::server::NamedService; + +pub trait GrpcRegister { + fn get_handler(self) -> GrpcHandler; + fn service(self) -> Route; + fn register(self, route: &mut Route); +} + +impl GrpcRegister for S +where + S: Service, Response = http::Response> + NamedService, + S: Clone + Send + 'static, + S: Sync + Send + 'static, + S::Future: Send + 'static, + S::Error: Into> + Send, +{ + fn get_handler(self) -> GrpcHandler { + GrpcHandler::new(self) + } + fn service(self) -> Route { + let handler = self.get_handler(); + let path = handler.path().to_string(); + let handler = Arc::new(handler); + Route::new(path.as_str()).append( + Route::new("") + .insert_handler(Method::POST, handler.clone()) + .insert_handler(Method::GET, handler), + ) + } + + fn register(self, route: &mut Route) { + route.extend(self.service()); + } +} diff --git a/silent/src/lib.rs b/silent/src/lib.rs index 4f999a6..59debe3 100644 --- a/silent/src/lib.rs +++ b/silent/src/lib.rs @@ -32,6 +32,8 @@ use multer; pub use crate::configs::Configs; pub use crate::core::{next::Next, request::Request, response::Response}; +#[cfg(feature = "grpc")] +pub use crate::grpc::{GrpcHandler, GrpcRegister}; pub use crate::middleware::{middlewares, MiddleWareHandler}; pub use error::SilentError; pub use error::SilentResult as Result; @@ -51,6 +53,8 @@ pub mod prelude { res_body::stream_body, res_body::ResBody, response::Response, }; pub use crate::error::{SilentError, SilentResult as Result}; + #[cfg(feature = "grpc")] + pub use crate::grpc::{GrpcHandler, GrpcRegister}; #[cfg(feature = "static")] pub use crate::handler::static_handler; pub use crate::handler::Handler; diff --git a/silent/src/route/root.rs b/silent/src/route/root.rs index 69b23d4..9b0981e 100644 --- a/silent/src/route/root.rs +++ b/silent/src/route/root.rs @@ -1,8 +1,4 @@ -#[cfg(feature = "grpc")] -use crate::grpc::GrpcHandler; use crate::middlewares::RequestTimeLogger; -#[cfg(feature = "grpc")] -use crate::prelude::HandlerGetter; use crate::route::handler_match::{Match, RouteMatched}; use crate::route::Route; #[cfg(feature = "session")] @@ -15,18 +11,10 @@ use crate::{Configs, Handler, MiddleWareHandler, Next, Request, Response, Silent #[cfg(feature = "session")] use async_session::SessionStore; use async_trait::async_trait; -#[cfg(feature = "grpc")] -use http::Method; use std::fmt; use std::sync::Arc; #[cfg(feature = "scheduler")] use tokio::sync::Mutex; -#[cfg(feature = "grpc")] -use tonic::body::BoxBody; -#[cfg(feature = "grpc")] -use tonic::codegen::Service; -#[cfg(feature = "grpc")] -use tonic::server::NamedService; #[derive(Clone, Default)] pub struct RootRoute { @@ -64,38 +52,6 @@ impl RootRoute { } } - #[cfg(feature = "grpc")] - pub fn grpc(&mut self, grpc: GrpcHandler) - where - S: Service, Response = http::Response> + NamedService, - S: Clone + Send + 'static, - S: Sync + Send + 'static, - S::Future: Send + 'static, - S::Error: Into> + Send, - { - let path = grpc.path().to_string(); - let handler = Arc::new(grpc); - let route = Route::new(path.as_str()).append( - Route::new("") - .insert_handler(Method::POST, handler.clone()) - .insert_handler(Method::GET, handler), - ); - self.push(route) - } - - #[cfg(feature = "grpc")] - pub fn with_grpc(mut self, grpc: GrpcHandler) -> Self - where - S: Service, Response = http::Response> + NamedService, - S: Clone + Send + 'static, - S: Sync + Send + 'static, - S::Future: Send + 'static, - S::Error: Into> + Send, - { - self.grpc(grpc); - self - } - pub fn push(&mut self, route: Route) { self.middlewares.extend(route.root_middlewares.clone()); self.children.push(route); From ff39f0457b18463080202f76f99796b6ccf126ae Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Mon, 12 Aug 2024 16:29:15 +0800 Subject: [PATCH 07/49] =?UTF-8?q?request=E6=B7=BB=E5=8A=A0form=5Fparse?= =?UTF-8?q?=E3=80=81json=5Ffield=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- silent/src/core/request.rs | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/silent/src/core/request.rs b/silent/src/core/request.rs index 3adb214..930cc2d 100644 --- a/silent/src/core/request.rs +++ b/silent/src/core/request.rs @@ -97,7 +97,7 @@ impl Request { let mut body = self.take_body(); builder - .body(body.frame().await.unwrap().unwrap().into_data().unwrap()) + .body(body.frame().await.unwrap()?.into_data().unwrap()) .map_err(|e| SilentError::business_error(StatusCode::BAD_REQUEST, e.to_string())) } } @@ -243,11 +243,6 @@ impl Request { &mut self.configs } - /// 获取可变原请求体 - // pub fn req_mut(&mut self) -> &mut BaseRequest { - // &mut self.req - // } - /// 获取路径参数集合 pub fn path_params(&self) -> &HashMap { &self.path_params @@ -321,6 +316,18 @@ impl Request { .await } + /// 获取请求form_data + #[cfg(feature = "multipart")] + #[inline] + pub async fn form_parse(&mut self) -> Result + where + for<'de> T: Deserialize<'de>, + { + let form_data = self.form_data().await?; + let value = serde_json::to_value(form_data.fields.clone())?; + serde_json::from_value(value).map_err(Into::into) + } + /// 转换body参数 #[cfg(feature = "multipart")] pub async fn form_field(&mut self, key: &str) -> Option @@ -385,6 +392,21 @@ impl Request { } } + /// 转换body参数按Json匹配 + pub async fn json_field(&mut self, key: &str) -> Result + where + for<'de> T: Deserialize<'de>, + { + let value: Value = self.json_parse().await?; + serde_json::from_value( + value + .get(key) + .ok_or(SilentError::ParamsNotFound)? + .to_owned(), + ) + .map_err(Into::into) + } + /// 获取请求body #[inline] pub fn replace_extensions(&mut self, extensions: Extensions) -> Extensions { From 0c0e680d8bf72553f3e703a674f9cf4493a7f442 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Thu, 5 Sep 2024 17:15:19 +0800 Subject: [PATCH 08/49] =?UTF-8?q?feat[plugin]:=20=E6=B7=BB=E5=8A=A0plugin?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/plugin_test/Cargo.toml | 14 +++ examples/plugin_test/README.MD | 6 + .../plugin_test/libexamples_plugins.dylib | Bin 0 -> 541512 bytes examples/plugin_test/src/main.rs | 21 ++++ examples/plugins/.cargo/config.toml | 2 + examples/plugins/Cargo.toml | 15 +++ examples/plugins/src/lib.rs | 6 + silent/src/ws/route.rs | 115 ++++++++++++++++++ 8 files changed, 179 insertions(+) create mode 100644 examples/plugin_test/Cargo.toml create mode 100644 examples/plugin_test/README.MD create mode 100755 examples/plugin_test/libexamples_plugins.dylib create mode 100644 examples/plugin_test/src/main.rs create mode 100644 examples/plugins/.cargo/config.toml create mode 100644 examples/plugins/Cargo.toml create mode 100644 examples/plugins/src/lib.rs create mode 100644 silent/src/ws/route.rs diff --git a/examples/plugin_test/Cargo.toml b/examples/plugin_test/Cargo.toml new file mode 100644 index 0000000..0eef40c --- /dev/null +++ b/examples/plugin_test/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "examples_plugin_test" +edition.workspace = true +authors.workspace = true +homepage.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +silent = { path = "../../silent" } +crossbeam-epoch = "0.9.18" +dlopen2 = { version = "0.7.0", features = ["derive"] } diff --git a/examples/plugin_test/README.MD b/examples/plugin_test/README.MD new file mode 100644 index 0000000..bf295dc --- /dev/null +++ b/examples/plugin_test/README.MD @@ -0,0 +1,6 @@ +## where is lib from? + +Compiled from example project plugins + +this example lib is compiled by MacOS, if your system is different, you need to compile it by yourself and edit +lib_path. diff --git a/examples/plugin_test/libexamples_plugins.dylib b/examples/plugin_test/libexamples_plugins.dylib new file mode 100755 index 0000000000000000000000000000000000000000..c165da4efe4df9aa195b14a290c8b9ed9e7acd6f GIT binary patch literal 541512 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%0A@fllUe*@1r?k8D!0PU~2j1?s9{{fxyduOF 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 &TX$~46#{1j}d>4w# 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 PluginMetadata, + #[allow(improper_ctypes_definitions)] + get_route: unsafe extern "C" fn() -> Route, +} + +fn main() -> Result<(), Box> { + logger::fmt().with_max_level(Level::INFO).init(); + let lib_path = "./libexamples_plugins.dylib"; + let container: Container = + unsafe { Container::load(lib_path) }.expect("无法加载插件"); + let route = unsafe { container.get_route() }; + Server::new().run(route); + Ok(()) +} diff --git a/examples/plugins/.cargo/config.toml b/examples/plugins/.cargo/config.toml new file mode 100644 index 0000000..a6b014e --- /dev/null +++ b/examples/plugins/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target-dir = "target" diff --git a/examples/plugins/Cargo.toml b/examples/plugins/Cargo.toml new file mode 100644 index 0000000..b88673c --- /dev/null +++ b/examples/plugins/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "examples-plugins" +edition.workspace = true +authors.workspace = true +homepage.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[lib] +crate-type = ['cdylib'] + +[dependencies] +silent = { path = "../../silent" } diff --git a/examples/plugins/src/lib.rs b/examples/plugins/src/lib.rs new file mode 100644 index 0000000..ecb2f36 --- /dev/null +++ b/examples/plugins/src/lib.rs @@ -0,0 +1,6 @@ +use silent::prelude::{HandlerAppend, Route}; +#[no_mangle] +#[allow(improper_ctypes_definitions)] +extern "C" fn get_route() -> Route { + Route::new("hello").get(|_req| async { Ok("hello world") }) +} diff --git a/silent/src/ws/route.rs b/silent/src/ws/route.rs new file mode 100644 index 0000000..25c823d --- /dev/null +++ b/silent/src/ws/route.rs @@ -0,0 +1,115 @@ +use crate::prelude::{HandlerGetter, Message, Result, WebSocketParts}; +use crate::route::Route; +use crate::ws::{HandlerWrapperWebSocket, WebSocketHandler}; +use http::Method; +use std::future::Future; +use std::sync::Arc; +use tokio::sync::mpsc::UnboundedSender; +use tokio::sync::RwLock; +use tokio_tungstenite::tungstenite::protocol::WebSocketConfig; + +pub trait WSHandlerAppend< + FnOnConnect, + FnOnConnectFut, + FnOnSend, + FnOnSendFut, + FnOnReceive, + FnOnReceiveFut, + FnOnClose, + FnOnCloseFut, +>: HandlerGetter where + FnOnConnect: Fn(Arc>, UnboundedSender) -> FnOnConnectFut + + Send + + Sync + + 'static, + FnOnConnectFut: Future> + Send + Sync + 'static, + FnOnSend: Fn(Message, Arc>) -> FnOnSendFut + Send + Sync + 'static, + FnOnSendFut: Future> + Send + Sync + 'static, + FnOnReceive: Fn(Message, Arc>) -> FnOnReceiveFut + Send + Sync + 'static, + FnOnReceiveFut: Future> + Send + Sync + 'static, + FnOnClose: Fn(Arc>) -> FnOnCloseFut + Send + Sync + 'static, + FnOnCloseFut: Future + Send + Sync + 'static, +{ + fn ws( + self, + config: Option, + handler: WebSocketHandler< + FnOnConnect, + FnOnConnectFut, + FnOnSend, + FnOnSendFut, + FnOnReceive, + FnOnReceiveFut, + FnOnClose, + FnOnCloseFut, + >, + ) -> Self; + fn ws_handler_append( + &mut self, + handler: HandlerWrapperWebSocket< + FnOnConnect, + FnOnConnectFut, + FnOnSend, + FnOnSendFut, + FnOnReceive, + FnOnReceiveFut, + FnOnClose, + FnOnCloseFut, + >, + ) { + let handler = Arc::new(handler); + self.get_handler_mut().insert(Method::GET, handler); + } +} +impl< + FnOnConnect, + FnOnConnectFut, + FnOnSend, + FnOnSendFut, + FnOnReceive, + FnOnReceiveFut, + FnOnClose, + FnOnCloseFut, + > + WSHandlerAppend< + FnOnConnect, + FnOnConnectFut, + FnOnSend, + FnOnSendFut, + FnOnReceive, + FnOnReceiveFut, + FnOnClose, + FnOnCloseFut, + > for Route +where + FnOnConnect: Fn(Arc>, UnboundedSender) -> FnOnConnectFut + + Send + + Sync + + 'static, + FnOnConnectFut: Future> + Send + Sync + 'static, + FnOnSend: Fn(Message, Arc>) -> FnOnSendFut + Send + Sync + 'static, + FnOnSendFut: Future> + Send + Sync + 'static, + FnOnReceive: Fn(Message, Arc>) -> FnOnReceiveFut + Send + Sync + 'static, + FnOnReceiveFut: Future> + Send + Sync + 'static, + FnOnClose: Fn(Arc>) -> FnOnCloseFut + Send + Sync + 'static, + FnOnCloseFut: Future + Send + Sync + 'static, +{ + fn ws( + mut self, + config: Option, + handler: WebSocketHandler< + FnOnConnect, + FnOnConnectFut, + FnOnSend, + FnOnSendFut, + FnOnReceive, + FnOnReceiveFut, + FnOnClose, + FnOnCloseFut, + >, + ) -> Self { + let handler = HandlerWrapperWebSocket::new(config).set_handler(handler); + self.ws_handler_append(handler); + self + } +} From 0dcbd523bf7ef2dc50df868df254d0cfecc2e7de Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Fri, 6 Sep 2024 13:46:54 +0800 Subject: [PATCH 09/49] =?UTF-8?q?feat:=20cookie=20scheduler=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/configs/Cargo.toml | 2 +- examples/custom_handler/Cargo.toml | 2 +- examples/custom_tokio_listener/Cargo.toml | 4 +- examples/custom_tokio_runtime/Cargo.toml | 4 +- examples/exception_handler/Cargo.toml | 2 +- examples/form/Cargo.toml | 2 +- examples/grpc/Cargo.toml | 8 +-- examples/grpc_h2c/Cargo.toml | 14 ++-- examples/grpc_streaming/Cargo.toml | 12 ++-- examples/middleware/Cargo.toml | 2 +- examples/multipart-form/Cargo.toml | 2 +- examples/sse-chat/Cargo.toml | 2 +- examples/templates/Cargo.toml | 2 +- examples/todo/Cargo.toml | 6 +- examples/websocket-chat/Cargo.toml | 4 +- examples/websocket/Cargo.toml | 4 +- silent/Cargo.toml | 28 ++++---- silent/src/core/adapt.rs | 27 ++------ silent/src/core/cookie.rs | 78 +++++++++++++++++++++++ silent/src/core/mod.rs | 2 + silent/src/core/request.rs | 40 ------------ silent/src/core/response.rs | 41 ------------ silent/src/lib.rs | 4 +- silent/src/scheduler/mod.rs | 2 + silent/src/scheduler/traits.rs | 16 +++++ 25 files changed, 156 insertions(+), 154 deletions(-) create mode 100644 silent/src/core/cookie.rs create mode 100644 silent/src/scheduler/traits.rs diff --git a/examples/configs/Cargo.toml b/examples/configs/Cargo.toml index 122382a..56c69a2 100644 --- a/examples/configs/Cargo.toml +++ b/examples/configs/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -async-trait = "0.1.81" +async-trait = "0.1.82" diff --git a/examples/custom_handler/Cargo.toml b/examples/custom_handler/Cargo.toml index 1e23cf9..8c5926f 100644 --- a/examples/custom_handler/Cargo.toml +++ b/examples/custom_handler/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -async-trait = "0.1.81" +async-trait = "0.1.82" diff --git a/examples/custom_tokio_listener/Cargo.toml b/examples/custom_tokio_listener/Cargo.toml index a4ed39b..9109806 100644 --- a/examples/custom_tokio_listener/Cargo.toml +++ b/examples/custom_tokio_listener/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -async-trait = "0.1.81" -tokio = { version = "1.38.0", features = ["full"] } +async-trait = "0.1.82" +tokio = { version = "1.40.0", features = ["full"] } diff --git a/examples/custom_tokio_runtime/Cargo.toml b/examples/custom_tokio_runtime/Cargo.toml index 6b3d06f..2800238 100644 --- a/examples/custom_tokio_runtime/Cargo.toml +++ b/examples/custom_tokio_runtime/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -async-trait = "0.1.81" -tokio = { version = "1.38.0", features = ["full"] } +async-trait = "0.1.82" +tokio = { version = "1.40.0", features = ["full"] } diff --git a/examples/exception_handler/Cargo.toml b/examples/exception_handler/Cargo.toml index b59ea75..4eb0e6f 100644 --- a/examples/exception_handler/Cargo.toml +++ b/examples/exception_handler/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -serde = { version = "1.0.204", features = ["derive"] } +serde = { version = "1.0.209", features = ["derive"] } diff --git a/examples/form/Cargo.toml b/examples/form/Cargo.toml index 6e8c97e..e450f80 100644 --- a/examples/form/Cargo.toml +++ b/examples/form/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -serde = { version = "1.0.204", features = ["derive"] } +serde = { version = "1.0.209", features = ["derive"] } diff --git a/examples/grpc/Cargo.toml b/examples/grpc/Cargo.toml index 6cdc549..b3775f5 100644 --- a/examples/grpc/Cargo.toml +++ b/examples/grpc/Cargo.toml @@ -13,11 +13,11 @@ name = "example-grpc-client" path = "src/client.rs" [dependencies] -tonic = { version = "0.12.0" } +tonic = { version = "0.12.2" } prost = "0.13" -tokio = { version = "1.38", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] } silent = { path = "../../silent", features = ["grpc"] } -async-trait = "0.1.81" +async-trait = "0.1.82" [build-dependencies] -tonic-build = { version = "0.12.0" } +tonic-build = { version = "0.12.2" } diff --git a/examples/grpc_h2c/Cargo.toml b/examples/grpc_h2c/Cargo.toml index 5343d1e..535958c 100644 --- a/examples/grpc_h2c/Cargo.toml +++ b/examples/grpc_h2c/Cargo.toml @@ -13,15 +13,15 @@ name = "example-grpc-client" path = "src/client.rs" [dependencies] -tonic = { version = "0.12.0" } +tonic = { version = "0.12.2" } prost = "0.13" -tokio = { version = "1.38", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] } silent = { path = "../../silent", features = ["grpc"] } -async-trait = "0.1.81" +async-trait = "0.1.82" http = "1.1.0" -hyper-util = "0.1.6" -hyper = "1.4.0" -tower = "0.4.13" +hyper-util = "0.1.7" +hyper = "1.4.1" +tower = "0.5.0" [build-dependencies] -tonic-build = { version = "0.12.0" } +tonic-build = { version = "0.12.2" } diff --git a/examples/grpc_streaming/Cargo.toml b/examples/grpc_streaming/Cargo.toml index 7827562..b5a838f 100644 --- a/examples/grpc_streaming/Cargo.toml +++ b/examples/grpc_streaming/Cargo.toml @@ -13,13 +13,13 @@ name = "example-grpc-client" path = "src/client.rs" [dependencies] -tonic = { version = "0.12.0" } +tonic = { version = "0.12.2" } prost = "0.13" -tokio = { version = "1.38", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] } silent = { path = "../../silent", features = ["grpc"] } -async-trait = "0.1.81" -tokio-stream = "0.1.15" -h2 = "0.4.5" +async-trait = "0.1.82" +tokio-stream = "0.1.16" +h2 = "0.4.6" [build-dependencies] -tonic-build = { version = "0.12.0" } +tonic-build = { version = "0.12.2" } diff --git a/examples/middleware/Cargo.toml b/examples/middleware/Cargo.toml index d5491ff..6340e4a 100644 --- a/examples/middleware/Cargo.toml +++ b/examples/middleware/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-trait = "0.1.81" +async-trait = "0.1.82" silent = { path = "../../silent" } diff --git a/examples/multipart-form/Cargo.toml b/examples/multipart-form/Cargo.toml index 5d86711..e844134 100644 --- a/examples/multipart-form/Cargo.toml +++ b/examples/multipart-form/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] silent = { path = "../../silent", features = ["multipart"] } -serde = { version = "1.0.204", features = ["derive"] } +serde = { version = "1.0.209", features = ["derive"] } diff --git a/examples/sse-chat/Cargo.toml b/examples/sse-chat/Cargo.toml index ef13a5d..39d053d 100644 --- a/examples/sse-chat/Cargo.toml +++ b/examples/sse-chat/Cargo.toml @@ -11,4 +11,4 @@ once_cell = "1" parking_lot = "0.12" tokio = { version = "1", features = ["macros"] } tokio-stream = { version = "0.1", features = ["net"] } -serde = { version = "1.0.204", features = ["derive"] } +serde = { version = "1.0.209", features = ["derive"] } diff --git a/examples/templates/Cargo.toml b/examples/templates/Cargo.toml index 1490b1a..ed42d23 100644 --- a/examples/templates/Cargo.toml +++ b/examples/templates/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -serde = { version = "1.0.204", features = ["derive"] } +serde = { version = "1.0.209", features = ["derive"] } silent = { path = "../../silent", features = ["template"] } diff --git a/examples/todo/Cargo.toml b/examples/todo/Cargo.toml index aecf818..17555fd 100644 --- a/examples/todo/Cargo.toml +++ b/examples/todo/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -serde = { version = "1.0.204", features = ["derive"] } -uuid = { version = "1.9.1", features = ["serde", "v4"] } -async-trait = "0.1.81" +serde = { version = "1.0.209", features = ["derive"] } +uuid = { version = "1.10.0", features = ["serde", "v4"] } +async-trait = "0.1.82" diff --git a/examples/websocket-chat/Cargo.toml b/examples/websocket-chat/Cargo.toml index 2eb2460..cf7236b 100644 --- a/examples/websocket-chat/Cargo.toml +++ b/examples/websocket-chat/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] silent = { path = "../../silent", features = ["upgrade"] } -tokio = { version = "1.38.0", features = ["full"] } -tokio-stream = { version = "0.1.15", features = ["net"] } +tokio = { version = "1.40.0", features = ["full"] } +tokio-stream = { version = "0.1.16", features = ["net"] } futures-util = { version = "0.3.30", default-features = false } once_cell = "1.19.0" diff --git a/examples/websocket/Cargo.toml b/examples/websocket/Cargo.toml index 0f006a6..5dd0b51 100644 --- a/examples/websocket/Cargo.toml +++ b/examples/websocket/Cargo.toml @@ -6,9 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-trait = "0.1.81" +async-trait = "0.1.82" silent = { path = "../../silent", features = ["upgrade"] } -tokio = { version = "1.38.0", features = ["full"] } +tokio = { version = "1.40.0", features = ["full"] } tokio-tungstenite = "0.23.1" futures-util = "0.3.30" backtrace = "0.3.73" diff --git a/silent/Cargo.toml b/silent/Cargo.toml index 462cd97..1af8cec 100644 --- a/silent/Cargo.toml +++ b/silent/Cargo.toml @@ -34,18 +34,18 @@ grpc = ["upgrade", "dep:tonic", "dep:pin-project-lite", "dep:pin-project", "dep: [dependencies] # Basic dependencies -thiserror = "1.0.61" -hyper = { version = "1.4.0", features = ["full"] } -hyper-util = { version = "0.1.6", features = ["server-auto", "tokio"] } -tokio = { version = "1.38.0", optional = true } -bytes = "1.6.0" +thiserror = "1.0.63" +hyper = { version = "1.4.1", features = ["full"] } +hyper-util = { version = "0.1.7", features = ["server-auto", "tokio"] } +tokio = { version = "1.40.0", optional = true } +bytes = "1.7.1" http-body-util = "0.1.2" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["chrono"] } -async-trait = "0.1.81" -serde = { version = "1.0.204", features = ["derive"] } -serde_json = "1.0.120" -uuid = "1.9.1" +async-trait = "0.1.82" +serde = { version = "1.0.209", features = ["derive"] } +serde_json = "1.0.128" +uuid = "1.10.0" url = "2.5.2" serde_urlencoded = "0.7.1" mime = "0.3.17" @@ -53,13 +53,13 @@ futures-util = "0.3.30" chrono = { version = "0.4.38", default-features = false, features = ["clock"] } tokio-tungstenite = { version = "0.23.1", optional = true } headers = "0.4.0" -tokio-stream = { version = "0.1.15", features = ["net"], optional = true } +tokio-stream = { version = "0.1.16", features = ["net"], optional = true } pin-project = { version = "1.1", optional = true } pin-project-lite = { version = "0.2.14", optional = true } http = "1.1.0" -http-body = "1.0.0" +http-body = "1.0.1" futures = "0.3.30" -tokio-util = "0.7.11" +tokio-util = "0.7.12" anyhow = "1.0.86" # Scheduler @@ -68,7 +68,7 @@ cron = { version = "0.12.1", optional = true } # Multipart multer = { version = "3.1.0", optional = true } multimap = { version = "0.10.0", features = ["serde"], optional = true } -tempfile = { version = "3.10.1", optional = true } +tempfile = { version = "3.12.0", optional = true } textnonce = { version = "1.0.0", optional = true } @@ -80,7 +80,7 @@ async-session = { version = "3.0.0", optional = true } cookie = { version = "0.18.1", features = ["secure", "percent-encode"], optional = true } # Grpc -tonic = { version = "0.12.0", optional = true } +tonic = { version = "0.12.2", optional = true } # Security argon2 = { version = "0.5.3", optional = true } diff --git a/silent/src/core/adapt.rs b/silent/src/core/adapt.rs index f4530e4..b9303e5 100644 --- a/silent/src/core/adapt.rs +++ b/silent/src/core/adapt.rs @@ -44,35 +44,20 @@ fn get_cookie(req: &HyperRequest) -> Result { } impl RequestAdapt for HyperRequest { - #[cfg(feature = "cookie")] fn tran_to_request(self) -> Request { + #[cfg(feature = "cookie")] let cookies = get_cookie(&self).unwrap_or_default(); let (parts, body) = self.into_parts(); let mut req = Request::from_parts(parts, body); - *req.cookies_mut() = cookies; + #[cfg(feature = "cookie")] + req.extensions_mut().insert(cookies); req } - #[cfg(not(feature = "cookie"))] - fn tran_to_request(self) -> Request { - let (parts, body) = self.into_parts(); - Request::from_parts(parts, body) - } } impl ResponseAdapt for HyperResponse { type Body = B; fn tran_from_response(res: Response) -> Self { - #[cfg(feature = "cookie")] - let Response { - status, - headers, - body, - cookies, - version, - extensions, - .. - } = res; - #[cfg(not(feature = "cookie"))] let Response { status, headers, @@ -84,14 +69,14 @@ impl ResponseAdapt for HyperResponse { let mut res = hyper::Response::new(body); res.headers_mut().extend(headers); - res.extensions_mut().extend(extensions); - *res.version_mut() = version; #[cfg(feature = "cookie")] - for cookie in cookies.delta() { + for cookie in extensions.get::().unwrap().delta() { if let Ok(hv) = cookie.encoded().to_string().parse() { res.headers_mut().append(header::SET_COOKIE, hv); } } + res.extensions_mut().extend(extensions); + *res.version_mut() = version; *res.status_mut() = status; res diff --git a/silent/src/core/cookie.rs b/silent/src/core/cookie.rs new file mode 100644 index 0000000..c659d03 --- /dev/null +++ b/silent/src/core/cookie.rs @@ -0,0 +1,78 @@ +use crate::{Request, Response, Result, SilentError}; +use cookie::{Cookie, CookieJar}; +use http::StatusCode; + +pub trait CookieExt { + /// Get `CookieJar` reference. + fn cookies(&self) -> Result<&CookieJar>; + /// Get `CookieJar` mutable reference. + fn cookies_mut(&mut self) -> Result<&mut CookieJar>; + /// Get `Cookie` from cookies. + fn cookie>(&self, name: T) -> Result>>; +} + +impl CookieExt for Request { + fn cookies(&self) -> Result<&CookieJar> { + self.extensions().get().ok_or_else(|| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + "Request does not have cookie jar", + ) + }) + } + + fn cookies_mut(&mut self) -> Result<&mut CookieJar> { + self.extensions_mut().get_mut().ok_or_else(|| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + "Request does not have cookie jar", + ) + }) + } + + fn cookie>(&self, name: T) -> Result>> { + Ok(self + .extensions() + .get::() + .ok_or_else(|| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + "Request does not have cookie jar", + ) + })? + .get(name.as_ref())) + } +} + +impl CookieExt for Response { + fn cookies(&self) -> Result<&CookieJar> { + self.extensions().get().ok_or_else(|| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + "Request does not have cookie jar", + ) + }) + } + + fn cookies_mut(&mut self) -> Result<&mut CookieJar> { + self.extensions_mut().get_mut().ok_or_else(|| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + "Request does not have cookie jar", + ) + }) + } + + fn cookie>(&self, name: T) -> Result>> { + Ok(self + .extensions() + .get::() + .ok_or_else(|| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + "Request does not have cookie jar", + ) + })? + .get(name.as_ref())) + } +} diff --git a/silent/src/core/mod.rs b/silent/src/core/mod.rs index 447e661..56a8e38 100644 --- a/silent/src/core/mod.rs +++ b/silent/src/core/mod.rs @@ -1,5 +1,7 @@ pub mod adapt; +#[cfg(feature = "cookie")] +pub(crate) mod cookie; #[cfg(feature = "multipart")] pub(crate) mod form; pub(crate) mod next; diff --git a/silent/src/core/request.rs b/silent/src/core/request.rs index 930cc2d..b8a674c 100644 --- a/silent/src/core/request.rs +++ b/silent/src/core/request.rs @@ -5,12 +5,8 @@ use crate::core::req_body::ReqBody; #[cfg(feature = "multipart")] use crate::core::serde::from_str_multi_val; use crate::header::CONTENT_TYPE; -#[cfg(feature = "scheduler")] -use crate::Scheduler; use crate::{Configs, Result, SilentError}; use bytes::Bytes; -#[cfg(feature = "cookie")] -use cookie::{Cookie, CookieJar}; use http::request::Parts; use http::{Extensions, HeaderMap, HeaderValue, Method, Uri, Version}; use http::{Request as BaseRequest, StatusCode}; @@ -21,10 +17,6 @@ use serde::Deserialize; use serde_json::Value; use std::collections::HashMap; use std::net::{IpAddr, SocketAddr}; -#[cfg(feature = "scheduler")] -use std::sync::Arc; -#[cfg(feature = "scheduler")] -use tokio::sync::Mutex; use tokio::sync::OnceCell; use url::form_urlencoded; @@ -43,8 +35,6 @@ pub struct Request { #[cfg(feature = "multipart")] form_data: OnceCell, json_data: OnceCell, - #[cfg(feature = "cookie")] - pub(crate) cookies: CookieJar, pub(crate) configs: Configs, } @@ -128,8 +118,6 @@ impl Request { #[cfg(feature = "multipart")] form_data: OnceCell::new(), json_data: OnceCell::new(), - #[cfg(feature = "cookie")] - cookies: CookieJar::default(), configs: Configs::default(), } } @@ -424,32 +412,4 @@ impl Request { let url = self.uri().path().to_string(); (self, url) } - - #[cfg(feature = "cookie")] - /// Get `CookieJar` reference. - #[inline] - pub fn cookies(&self) -> &CookieJar { - &self.cookies - } - #[cfg(feature = "cookie")] - /// Get `CookieJar` mutable reference. - #[inline] - pub fn cookies_mut(&mut self) -> &mut CookieJar { - &mut self.cookies - } - #[cfg(feature = "cookie")] - /// Get `Cookie` from cookies. - #[inline] - pub fn cookie(&self, name: T) -> Option<&Cookie<'static>> - where - T: AsRef, - { - self.cookies.get(name.as_ref()) - } - #[cfg(feature = "scheduler")] - #[inline] - /// Get `Scheduler` from extensions. - pub fn scheduler(&self) -> &Arc> { - self.extensions().get().unwrap() - } } diff --git a/silent/src/core/response.rs b/silent/src/core/response.rs index 44b0fea..cefc0a3 100644 --- a/silent/src/core/response.rs +++ b/silent/src/core/response.rs @@ -4,8 +4,6 @@ use std::fmt::{Display, Formatter}; use crate::core::res_body::{full, ResBody}; use crate::headers::{ContentType, Header, HeaderMap, HeaderMapExt}; use crate::{header, Configs, Result, SilentError, StatusCode}; -#[cfg(feature = "cookie")] -use cookie::{Cookie, CookieJar}; use http::{Extensions, Version}; use http_body::{Body, SizeHint}; use serde::Serialize; @@ -24,8 +22,6 @@ pub struct Response { /// The HTTP headers. pub(crate) headers: HeaderMap, pub(crate) body: B, - #[cfg(feature = "cookie")] - pub(crate) cookies: CookieJar, pub(crate) extensions: Extensions, pub(crate) configs: Configs, } @@ -52,8 +48,6 @@ impl Response { headers: HeaderMap::new(), version: Version::default(), body: ResBody::None, - #[cfg(feature = "cookie")] - cookies: CookieJar::default(), extensions: Extensions::default(), configs: Configs::default(), } @@ -206,41 +200,6 @@ impl Response { self } - #[cfg(feature = "cookie")] - /// Get `CookieJar` reference. - #[inline] - pub fn cookies(&self) -> &CookieJar { - &self.cookies - } - #[cfg(feature = "cookie")] - /// Get `CookieJar` mutable reference. - #[inline] - pub fn cookies_mut(&mut self) -> &mut CookieJar { - &mut self.cookies - } - #[cfg(feature = "cookie")] - /// Get `Cookie` from cookies. - #[inline] - pub fn cookie(&self, name: T) -> Option<&Cookie<'static>> - where - T: AsRef, - { - self.cookies.get(name.as_ref()) - } - - #[cfg(feature = "cookie")] - /// move response to from another response - pub fn copy_from_response(&mut self, res: Response) { - self.headers.extend(res.headers); - res.cookies.delta().for_each(|cookie| { - self.cookies.add(cookie.clone()); - }); - self.status = res.status; - self.extensions.extend(res.extensions); - self.set_body(res.body); - } - - #[cfg(not(feature = "cookie"))] /// move response to from another response pub fn copy_from_response(&mut self, res: Response) { self.headers.extend(res.headers); diff --git a/silent/src/lib.rs b/silent/src/lib.rs index 59debe3..278b884 100644 --- a/silent/src/lib.rs +++ b/silent/src/lib.rs @@ -42,7 +42,7 @@ pub use handler::HandlerWrapper; pub use headers; pub use hyper::{header, Method, StatusCode}; #[cfg(feature = "scheduler")] -pub use scheduler::{ProcessTime, Scheduler, Task}; +pub use scheduler::{ProcessTime, Scheduler, SchedulerExt, Task}; pub mod prelude { pub use crate::configs::Configs; @@ -66,7 +66,7 @@ pub mod prelude { pub use crate::route::handler_append::{HandlerAppend, HandlerGetter}; pub use crate::route::{RootRoute, Route, RouteService, RouterAdapt}; #[cfg(feature = "scheduler")] - pub use crate::scheduler::Task; + pub use crate::scheduler::{SchedulerExt, Task}; #[cfg(feature = "security")] pub use crate::security::{argon2, pbkdf2}; #[cfg(feature = "server")] diff --git a/silent/src/scheduler/mod.rs b/silent/src/scheduler/mod.rs index 0de6867..c49c33b 100644 --- a/silent/src/scheduler/mod.rs +++ b/silent/src/scheduler/mod.rs @@ -1,6 +1,7 @@ mod process_time; mod storage; mod task; +pub mod traits; use anyhow::{anyhow, Result}; use std::sync::Arc; @@ -10,6 +11,7 @@ use tracing::{debug, error, info}; pub use process_time::ProcessTime; pub use task::Task; +pub use traits::SchedulerExt; #[derive(Debug, Clone)] pub struct Scheduler { diff --git a/silent/src/scheduler/traits.rs b/silent/src/scheduler/traits.rs new file mode 100644 index 0000000..cf06e6f --- /dev/null +++ b/silent/src/scheduler/traits.rs @@ -0,0 +1,16 @@ +use crate::{Request, Result, Scheduler, SilentError}; +use http::StatusCode; +use std::sync::Arc; +use tokio::sync::Mutex; + +pub trait SchedulerExt { + fn scheduler(&self) -> Result<&Arc>>; +} + +impl SchedulerExt for Request { + fn scheduler(&self) -> Result<&Arc>> { + self.extensions().get().ok_or_else(|| { + SilentError::business_error(StatusCode::INTERNAL_SERVER_ERROR, "No scheduler found") + }) + } +} From a3ab9ddb574d082bd00735d1789845fa9061d161 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Fri, 6 Sep 2024 15:15:54 +0800 Subject: [PATCH 10/49] =?UTF-8?q?=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/grpc_h2c/src/main.rs | 18 ++++----- examples/scheduler/src/main.rs | 2 +- silent/src/core/adapt.rs | 6 ++- silent/src/core/cookie.rs | 74 +++++++++------------------------- silent/src/lib.rs | 5 ++- silent/src/route/mod.rs | 4 +- silent/src/session/mod.rs | 4 +- 7 files changed, 43 insertions(+), 70 deletions(-) diff --git a/examples/grpc_h2c/src/main.rs b/examples/grpc_h2c/src/main.rs index 8aecac6..b115f12 100644 --- a/examples/grpc_h2c/src/main.rs +++ b/examples/grpc_h2c/src/main.rs @@ -30,14 +30,14 @@ impl Greeter for MyGreeter { #[tokio::main] async fn main() -> Result<(), Box> { - let greeter = MyGreeter::default(); - logger::fmt().with_max_level(Level::INFO).init(); - - let mut route = Route::new("").get(|_req| async { Ok("hello world") }); - GreeterServer::new(greeter).register(&mut route); - silent::prelude::Server::new() - .bind("0.0.0.0:50051".parse().unwrap()) - .serve(route) - .await; + let _greeter = MyGreeter::default(); + // logger::fmt().with_max_level(Level::INFO).init(); + // + // let mut route = Route::new("").get(|_req| async { Ok("hello world") }); + // GreeterServer::new(greeter).register(&mut route); + // silent::prelude::Server::new() + // .bind("0.0.0.0:50051".parse().unwrap()) + // .serve(route) + // .await; Ok(()) } diff --git a/examples/scheduler/src/main.rs b/examples/scheduler/src/main.rs index 556309d..4150d04 100644 --- a/examples/scheduler/src/main.rs +++ b/examples/scheduler/src/main.rs @@ -17,7 +17,7 @@ fn main() { }) }), ); - req.scheduler().lock().await.add_task(task)?; + req.scheduler()?.lock().await.add_task(task)?; Ok("hello world") }); Server::new().run(route); diff --git a/silent/src/core/adapt.rs b/silent/src/core/adapt.rs index b9303e5..d392405 100644 --- a/silent/src/core/adapt.rs +++ b/silent/src/core/adapt.rs @@ -8,6 +8,8 @@ use hyper::Response as HyperResponse; use crate::core::req_body::ReqBody; #[cfg(feature = "cookie")] +use crate::CookieExt; +#[cfg(feature = "cookie")] use crate::SilentError; use crate::{Request, Response}; @@ -58,6 +60,8 @@ impl RequestAdapt for HyperRequest { impl ResponseAdapt for HyperResponse { type Body = B; fn tran_from_response(res: Response) -> Self { + #[cfg(feature = "cookie")] + let cookies = res.cookies(); let Response { status, headers, @@ -70,7 +74,7 @@ impl ResponseAdapt for HyperResponse { let mut res = hyper::Response::new(body); res.headers_mut().extend(headers); #[cfg(feature = "cookie")] - for cookie in extensions.get::().unwrap().delta() { + for cookie in cookies.delta() { if let Ok(hv) = cookie.encoded().to_string().parse() { res.headers_mut().append(header::SET_COOKIE, hv); } diff --git a/silent/src/core/cookie.rs b/silent/src/core/cookie.rs index c659d03..6fd387c 100644 --- a/silent/src/core/cookie.rs +++ b/silent/src/core/cookie.rs @@ -1,78 +1,40 @@ -use crate::{Request, Response, Result, SilentError}; +use crate::{Request, Response}; use cookie::{Cookie, CookieJar}; -use http::StatusCode; +use http_body::Body; pub trait CookieExt { /// Get `CookieJar` reference. - fn cookies(&self) -> Result<&CookieJar>; + fn cookies(&self) -> CookieJar; /// Get `CookieJar` mutable reference. - fn cookies_mut(&mut self) -> Result<&mut CookieJar>; + fn cookies_mut(&mut self) -> &mut CookieJar; /// Get `Cookie` from cookies. - fn cookie>(&self, name: T) -> Result>>; + fn cookie>(&self, name: T) -> Option<&Cookie<'static>>; } impl CookieExt for Request { - fn cookies(&self) -> Result<&CookieJar> { - self.extensions().get().ok_or_else(|| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - "Request does not have cookie jar", - ) - }) + fn cookies(&self) -> CookieJar { + self.extensions().get().cloned().unwrap_or_default() } - fn cookies_mut(&mut self) -> Result<&mut CookieJar> { - self.extensions_mut().get_mut().ok_or_else(|| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - "Request does not have cookie jar", - ) - }) + fn cookies_mut(&mut self) -> &mut CookieJar { + self.extensions_mut().get_mut().unwrap() } - fn cookie>(&self, name: T) -> Result>> { - Ok(self - .extensions() - .get::() - .ok_or_else(|| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - "Request does not have cookie jar", - ) - })? - .get(name.as_ref())) + fn cookie>(&self, name: T) -> Option<&Cookie<'static>> { + self.extensions().get::()?.get(name.as_ref()) } } -impl CookieExt for Response { - fn cookies(&self) -> Result<&CookieJar> { - self.extensions().get().ok_or_else(|| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - "Request does not have cookie jar", - ) - }) +impl CookieExt for Response { + fn cookies(&self) -> CookieJar { + self.extensions().get().cloned().unwrap_or_default() } - fn cookies_mut(&mut self) -> Result<&mut CookieJar> { - self.extensions_mut().get_mut().ok_or_else(|| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - "Request does not have cookie jar", - ) - }) + fn cookies_mut(&mut self) -> &mut CookieJar { + self.extensions_mut().get_mut().unwrap() } - fn cookie>(&self, name: T) -> Result>> { - Ok(self - .extensions() - .get::() - .ok_or_else(|| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - "Request does not have cookie jar", - ) - })? - .get(name.as_ref())) + fn cookie>(&self, name: T) -> Option<&Cookie<'static>> { + self.extensions().get::()?.get(name.as_ref()) } } diff --git a/silent/src/lib.rs b/silent/src/lib.rs index 278b884..a6a1b90 100644 --- a/silent/src/lib.rs +++ b/silent/src/lib.rs @@ -1,6 +1,5 @@ mod configs; /// The `silent` library. -#[warn(missing_docs)] mod core; mod error; #[cfg(feature = "grpc")] @@ -31,6 +30,8 @@ mod ws; use multer; pub use crate::configs::Configs; +#[cfg(feature = "cookie")] +pub use crate::core::cookie::CookieExt; pub use crate::core::{next::Next, request::Request, response::Response}; #[cfg(feature = "grpc")] pub use crate::grpc::{GrpcHandler, GrpcRegister}; @@ -46,6 +47,8 @@ pub use scheduler::{ProcessTime, Scheduler, SchedulerExt, Task}; pub mod prelude { pub use crate::configs::Configs; + #[cfg(feature = "cookie")] + pub use crate::core::cookie::CookieExt; #[cfg(feature = "multipart")] pub use crate::core::form::{FilePart, FormData}; pub use crate::core::{ diff --git a/silent/src/route/mod.rs b/silent/src/route/mod.rs index 1c024b5..dc40ccf 100644 --- a/silent/src/route/mod.rs +++ b/silent/src/route/mod.rs @@ -14,6 +14,8 @@ use crate::handler::Handler; use crate::middleware::MiddleWareHandler; #[cfg(feature = "static")] use crate::prelude::HandlerGetter; +#[cfg(feature = "cookie")] +use crate::CookieExt; use crate::{Method, Next, Request, Response, SilentError}; pub(crate) mod handler_append; @@ -191,7 +193,7 @@ impl Handler for Route { pre_res.configs = configs; #[cfg(feature = "cookie")] { - *pre_res.cookies_mut() = req.cookies().clone(); + pre_res.extensions_mut().insert(req.cookies().clone()); } #[cfg(feature = "session")] let session = req.extensions().get::().cloned(); diff --git a/silent/src/session/mod.rs b/silent/src/session/mod.rs index e6ca309..d5f498b 100644 --- a/silent/src/session/mod.rs +++ b/silent/src/session/mod.rs @@ -1,4 +1,6 @@ -use crate::{Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError, StatusCode}; +use crate::{ + CookieExt, Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError, StatusCode, +}; use async_session::{MemoryStore, Session, SessionStore}; use async_trait::async_trait; use cookie::Cookie; From a608f373b4b8682184229dba7dc9f8332ab8abee Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Sat, 14 Sep 2024 13:46:46 +0800 Subject: [PATCH 11/49] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4prelude?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- silent/src/lib.rs | 53 ++----------------------------------------- silent/src/prelude.rs | 44 +++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 51 deletions(-) create mode 100644 silent/src/prelude.rs diff --git a/silent/src/lib.rs b/silent/src/lib.rs index a6a1b90..6f40e86 100644 --- a/silent/src/lib.rs +++ b/silent/src/lib.rs @@ -7,6 +7,7 @@ mod grpc; mod handler; mod log; pub mod middleware; +pub mod prelude; mod route; #[cfg(feature = "scheduler")] mod scheduler; @@ -29,10 +30,7 @@ mod ws; #[allow(clippy::single_component_path_imports)] use multer; -pub use crate::configs::Configs; -#[cfg(feature = "cookie")] -pub use crate::core::cookie::CookieExt; -pub use crate::core::{next::Next, request::Request, response::Response}; +pub use crate::core::{request::Request, response::Response}; #[cfg(feature = "grpc")] pub use crate::grpc::{GrpcHandler, GrpcRegister}; pub use crate::middleware::{middlewares, MiddleWareHandler}; @@ -44,50 +42,3 @@ pub use headers; pub use hyper::{header, Method, StatusCode}; #[cfg(feature = "scheduler")] pub use scheduler::{ProcessTime, Scheduler, SchedulerExt, Task}; - -pub mod prelude { - pub use crate::configs::Configs; - #[cfg(feature = "cookie")] - pub use crate::core::cookie::CookieExt; - #[cfg(feature = "multipart")] - pub use crate::core::form::{FilePart, FormData}; - pub use crate::core::{ - next::Next, path_param::PathParam, req_body::ReqBody, request::Request, res_body::full, - res_body::stream_body, res_body::ResBody, response::Response, - }; - pub use crate::error::{SilentError, SilentResult as Result}; - #[cfg(feature = "grpc")] - pub use crate::grpc::{GrpcHandler, GrpcRegister}; - #[cfg(feature = "static")] - pub use crate::handler::static_handler; - pub use crate::handler::Handler; - pub use crate::handler::HandlerWrapper; - pub use crate::log::*; - pub use crate::middleware::MiddleWareHandler; - #[cfg(feature = "upgrade")] - pub use crate::route::handler_append::WSHandlerAppend; - pub use crate::route::handler_append::{HandlerAppend, HandlerGetter}; - pub use crate::route::{RootRoute, Route, RouteService, RouterAdapt}; - #[cfg(feature = "scheduler")] - pub use crate::scheduler::{SchedulerExt, Task}; - #[cfg(feature = "security")] - pub use crate::security::{argon2, pbkdf2}; - #[cfg(feature = "server")] - pub use crate::service::Server; - #[cfg(feature = "sse")] - pub use crate::sse::{sse_reply, SSEEvent}; - #[cfg(feature = "template")] - pub use crate::templates::*; - #[cfg(feature = "upgrade")] - pub use crate::ws::{ - FnOnClose, FnOnConnect, FnOnNoneResultFut, FnOnReceive, FnOnSend, FnOnSendFut, - }; - #[cfg(feature = "upgrade")] - pub use crate::ws::{Message, WebSocket, WebSocketHandler, WebSocketParts}; - #[cfg(feature = "session")] - pub use async_session::{Session, SessionStore}; - #[cfg(feature = "cookie")] - pub use cookie::{time as CookieTime, Cookie}; - pub use headers; - pub use hyper::{header, upgrade, Method, StatusCode}; -} diff --git a/silent/src/prelude.rs b/silent/src/prelude.rs new file mode 100644 index 0000000..c44f7ba --- /dev/null +++ b/silent/src/prelude.rs @@ -0,0 +1,44 @@ +pub use crate::configs::Configs; +#[cfg(feature = "cookie")] +pub use crate::core::cookie::CookieExt; +#[cfg(feature = "multipart")] +pub use crate::core::form::{FilePart, FormData}; +pub use crate::core::{ + next::Next, path_param::PathParam, req_body::ReqBody, request::Request, res_body::full, + res_body::stream_body, res_body::ResBody, response::Response, +}; +pub use crate::error::{SilentError, SilentResult as Result}; +#[cfg(feature = "grpc")] +pub use crate::grpc::{GrpcHandler, GrpcRegister}; +#[cfg(feature = "static")] +pub use crate::handler::static_handler; +pub use crate::handler::Handler; +pub use crate::handler::HandlerWrapper; +pub use crate::log::*; +pub use crate::middleware::MiddleWareHandler; +#[cfg(feature = "upgrade")] +pub use crate::route::handler_append::WSHandlerAppend; +pub use crate::route::handler_append::{HandlerAppend, HandlerGetter}; +pub use crate::route::{RootRoute, Route, RouteService, RouterAdapt}; +#[cfg(feature = "scheduler")] +pub use crate::scheduler::{SchedulerExt, Task}; +#[cfg(feature = "security")] +pub use crate::security::{argon2, pbkdf2}; +#[cfg(feature = "server")] +pub use crate::service::Server; +#[cfg(feature = "sse")] +pub use crate::sse::{sse_reply, SSEEvent}; +#[cfg(feature = "template")] +pub use crate::templates::*; +#[cfg(feature = "upgrade")] +pub use crate::ws::{ + FnOnClose, FnOnConnect, FnOnNoneResultFut, FnOnReceive, FnOnSend, FnOnSendFut, +}; +#[cfg(feature = "upgrade")] +pub use crate::ws::{Message, WebSocket, WebSocketHandler, WebSocketParts}; +#[cfg(feature = "session")] +pub use async_session::{Session, SessionStore}; +#[cfg(feature = "cookie")] +pub use cookie::{time as CookieTime, Cookie}; +pub use headers; +pub use hyper::{header, upgrade, Method, StatusCode}; From e8f42f125504d98f4f31856702ab5b19ddf9597b Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Sat, 14 Sep 2024 13:51:30 +0800 Subject: [PATCH 12/49] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4prelude?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- silent/src/core/request.rs | 2 +- silent/src/lib.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/silent/src/core/request.rs b/silent/src/core/request.rs index b8a674c..d3ac2c8 100644 --- a/silent/src/core/request.rs +++ b/silent/src/core/request.rs @@ -5,7 +5,7 @@ use crate::core::req_body::ReqBody; #[cfg(feature = "multipart")] use crate::core::serde::from_str_multi_val; use crate::header::CONTENT_TYPE; -use crate::{Configs, Result, SilentError}; +use crate::{Result, SilentError}; use bytes::Bytes; use http::request::Parts; use http::{Extensions, HeaderMap, HeaderValue, Method, Uri, Version}; diff --git a/silent/src/lib.rs b/silent/src/lib.rs index 6f40e86..e6a452e 100644 --- a/silent/src/lib.rs +++ b/silent/src/lib.rs @@ -30,7 +30,10 @@ mod ws; #[allow(clippy::single_component_path_imports)] use multer; -pub use crate::core::{request::Request, response::Response}; +pub use crate::configs::Configs; +#[cfg(feature = "cookie")] +pub use crate::core::cookie::CookieExt; +pub use crate::core::{next::Next, request::Request, response::Response}; #[cfg(feature = "grpc")] pub use crate::grpc::{GrpcHandler, GrpcRegister}; pub use crate::middleware::{middlewares, MiddleWareHandler}; From d4904dd01bff3c00c7e56cf2d791759e62c8da51 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Sat, 14 Sep 2024 13:51:56 +0800 Subject: [PATCH 13/49] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4prelude?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- silent/src/core/request.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/silent/src/core/request.rs b/silent/src/core/request.rs index d3ac2c8..b8a674c 100644 --- a/silent/src/core/request.rs +++ b/silent/src/core/request.rs @@ -5,7 +5,7 @@ use crate::core::req_body::ReqBody; #[cfg(feature = "multipart")] use crate::core::serde::from_str_multi_val; use crate::header::CONTENT_TYPE; -use crate::{Result, SilentError}; +use crate::{Configs, Result, SilentError}; use bytes::Bytes; use http::request::Parts; use http::{Extensions, HeaderMap, HeaderValue, Method, Uri, Version}; From be5117e7fbaf8995480636334c98106fdcdf9966 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Sat, 14 Sep 2024 14:37:48 +0800 Subject: [PATCH 14/49] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E4=B8=AD=E9=97=B4=E4=BB=B6=E5=8F=8A=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/timeout/Cargo.lock | 7 ++++ examples/timeout/Cargo.toml | 10 ++++++ examples/timeout/src/main.rs | 16 +++++++++ silent/src/core/adapt.rs | 1 + silent/src/middleware/middlewares/mod.rs | 2 ++ silent/src/middleware/middlewares/timeout.rs | 38 ++++++++++++++++++++ 6 files changed, 74 insertions(+) create mode 100644 examples/timeout/Cargo.lock create mode 100644 examples/timeout/Cargo.toml create mode 100644 examples/timeout/src/main.rs create mode 100644 silent/src/middleware/middlewares/timeout.rs diff --git a/examples/timeout/Cargo.lock b/examples/timeout/Cargo.lock new file mode 100644 index 0000000..e7cecae --- /dev/null +++ b/examples/timeout/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "hello-world" +version = "0.1.0" diff --git a/examples/timeout/Cargo.toml b/examples/timeout/Cargo.toml new file mode 100644 index 0000000..cc8abff --- /dev/null +++ b/examples/timeout/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "example-timeout" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +silent = { path = "../../silent" } +tokio = { version = "1", features = ["time"] } diff --git a/examples/timeout/src/main.rs b/examples/timeout/src/main.rs new file mode 100644 index 0000000..259204d --- /dev/null +++ b/examples/timeout/src/main.rs @@ -0,0 +1,16 @@ +use silent::middlewares::Timeout; +use silent::prelude::*; +use std::time::Duration; + +fn main() { + logger::fmt().with_max_level(Level::INFO).init(); + let route = Route::new("") + .hook(Timeout::new(Duration::from_secs(1))) + .get(|_req| async { + println!("sleeping for 2 seconds"); + tokio::time::sleep(Duration::from_secs(2)).await; + println!("done sleeping"); + Ok("hello world") + }); + Server::new().run(route); +} diff --git a/silent/src/core/adapt.rs b/silent/src/core/adapt.rs index d392405..de805d3 100644 --- a/silent/src/core/adapt.rs +++ b/silent/src/core/adapt.rs @@ -50,6 +50,7 @@ impl RequestAdapt for HyperRequest { #[cfg(feature = "cookie")] let cookies = get_cookie(&self).unwrap_or_default(); let (parts, body) = self.into_parts(); + #[allow(unused_mut)] let mut req = Request::from_parts(parts, body); #[cfg(feature = "cookie")] req.extensions_mut().insert(cookies); diff --git a/silent/src/middleware/middlewares/mod.rs b/silent/src/middleware/middlewares/mod.rs index 3a9c052..a667b83 100644 --- a/silent/src/middleware/middlewares/mod.rs +++ b/silent/src/middleware/middlewares/mod.rs @@ -1,7 +1,9 @@ mod cors; mod exception_handler; mod request_time_logger; +mod timeout; pub use cors::{Cors, CorsType}; pub use exception_handler::ExceptionHandler; pub use request_time_logger::RequestTimeLogger; +pub use timeout::Timeout; diff --git a/silent/src/middleware/middlewares/timeout.rs b/silent/src/middleware/middlewares/timeout.rs new file mode 100644 index 0000000..5de0ecc --- /dev/null +++ b/silent/src/middleware/middlewares/timeout.rs @@ -0,0 +1,38 @@ +use crate::{Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError}; +use async_trait::async_trait; +use http::StatusCode; +use std::time::Duration; + +/// ExceptionHandler 中间件 +/// ```rust +/// use silent::prelude::*; +/// use silent::middlewares::{RequestTimeLogger}; +/// // Define a request time logger middleware +/// let _ = RequestTimeLogger::new(); +#[derive(Default, Clone)] +pub struct Timeout { + timeout: Duration, +} + +impl Timeout { + pub fn new(timeout: Duration) -> Self { + Self { timeout } + } +} + +#[async_trait] +impl MiddleWareHandler for Timeout { + async fn handle(&self, req: Request, next: &Next) -> Result { + match tokio::time::timeout(self.timeout, next.call(req)) + .await + .map_err(|_| { + SilentError::business_error( + StatusCode::REQUEST_TIMEOUT, + "Request timed out".to_string(), + ) + }) { + Ok(res) => res, + Err(err) => Err(err), + } + } +} From bcbac198620e81ae9f367e6833a28b476c9b3fcf Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Sat, 14 Sep 2024 16:45:13 +0800 Subject: [PATCH 15/49] =?UTF-8?q?feat:=20websocket=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/websocket-chat/src/main.rs | 4 +- examples/websocket/src/main.rs | 8 +- silent/src/prelude.rs | 4 +- silent/src/route/handler_append.rs | 117 ---------------------------- silent/src/ws/mod.rs | 2 + 5 files changed, 10 insertions(+), 125 deletions(-) diff --git a/examples/websocket-chat/src/main.rs b/examples/websocket-chat/src/main.rs index 9a9f04e..e505fc0 100644 --- a/examples/websocket-chat/src/main.rs +++ b/examples/websocket-chat/src/main.rs @@ -82,8 +82,8 @@ async fn on_close(parts: Arc>) { ONLINE_USERS.write().await.remove(my_id); } -async fn index<'a>(_res: Request) -> Result<&'a str> { - Ok(INDEX_HTML) +async fn index(_res: Request) -> Result { + Ok(Response::html(INDEX_HTML)) } static INDEX_HTML: &str = r#" diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs index 4697748..3cf63ac 100644 --- a/examples/websocket/src/main.rs +++ b/examples/websocket/src/main.rs @@ -15,8 +15,9 @@ fn main() { Server::new().run(route); } -async fn show_form(_req: Request) -> Result<&'static str> { - Ok(r#" +async fn show_form(_req: Request) -> Result { + Ok(Response::html( + r#" WS @@ -38,5 +39,6 @@ async fn show_form(_req: Request) -> Result<&'static str> { -"#) +"#, + )) } diff --git a/silent/src/prelude.rs b/silent/src/prelude.rs index c44f7ba..0f0a1e9 100644 --- a/silent/src/prelude.rs +++ b/silent/src/prelude.rs @@ -16,8 +16,6 @@ pub use crate::handler::Handler; pub use crate::handler::HandlerWrapper; pub use crate::log::*; pub use crate::middleware::MiddleWareHandler; -#[cfg(feature = "upgrade")] -pub use crate::route::handler_append::WSHandlerAppend; pub use crate::route::handler_append::{HandlerAppend, HandlerGetter}; pub use crate::route::{RootRoute, Route, RouteService, RouterAdapt}; #[cfg(feature = "scheduler")] @@ -32,7 +30,7 @@ pub use crate::sse::{sse_reply, SSEEvent}; pub use crate::templates::*; #[cfg(feature = "upgrade")] pub use crate::ws::{ - FnOnClose, FnOnConnect, FnOnNoneResultFut, FnOnReceive, FnOnSend, FnOnSendFut, + FnOnClose, FnOnConnect, FnOnNoneResultFut, FnOnReceive, FnOnSend, FnOnSendFut, WSHandlerAppend, }; #[cfg(feature = "upgrade")] pub use crate::ws::{Message, WebSocket, WebSocketHandler, WebSocketParts}; diff --git a/silent/src/route/handler_append.rs b/silent/src/route/handler_append.rs index d73302e..bee78a3 100644 --- a/silent/src/route/handler_append.rs +++ b/silent/src/route/handler_append.rs @@ -1,16 +1,8 @@ use super::Route; -#[cfg(feature = "upgrade")] -use crate::ws::{HandlerWrapperWebSocket, Message, WebSocketHandler, WebSocketParts}; use crate::{Handler, HandlerWrapper, Method, Request, Response, Result}; use std::collections::HashMap; use std::future::Future; use std::sync::Arc; -#[cfg(feature = "upgrade")] -use tokio::sync::mpsc::UnboundedSender; -#[cfg(feature = "upgrade")] -use tokio::sync::RwLock; -#[cfg(feature = "upgrade")] -use tokio_tungstenite::tungstenite::protocol::WebSocketConfig; pub trait HandlerGetter { fn get_handler_mut(&mut self) -> &mut HashMap>; @@ -36,61 +28,6 @@ where } } -#[cfg(feature = "upgrade")] -pub trait WSHandlerAppend< - FnOnConnect, - FnOnConnectFut, - FnOnSend, - FnOnSendFut, - FnOnReceive, - FnOnReceiveFut, - FnOnClose, - FnOnCloseFut, ->: HandlerGetter where - FnOnConnect: Fn(Arc>, UnboundedSender) -> FnOnConnectFut - + Send - + Sync - + 'static, - FnOnConnectFut: Future> + Send + Sync + 'static, - FnOnSend: Fn(Message, Arc>) -> FnOnSendFut + Send + Sync + 'static, - FnOnSendFut: Future> + Send + Sync + 'static, - FnOnReceive: Fn(Message, Arc>) -> FnOnReceiveFut + Send + Sync + 'static, - FnOnReceiveFut: Future> + Send + Sync + 'static, - FnOnClose: Fn(Arc>) -> FnOnCloseFut + Send + Sync + 'static, - FnOnCloseFut: Future + Send + Sync + 'static, -{ - fn ws( - self, - config: Option, - handler: WebSocketHandler< - FnOnConnect, - FnOnConnectFut, - FnOnSend, - FnOnSendFut, - FnOnReceive, - FnOnReceiveFut, - FnOnClose, - FnOnCloseFut, - >, - ) -> Self; - fn ws_handler_append( - &mut self, - handler: HandlerWrapperWebSocket< - FnOnConnect, - FnOnConnectFut, - FnOnSend, - FnOnSendFut, - FnOnReceive, - FnOnReceiveFut, - FnOnClose, - FnOnCloseFut, - >, - ) { - let handler = Arc::new(handler); - self.get_handler_mut().insert(Method::GET, handler); - } -} - impl HandlerGetter for Route { fn get_handler_mut(&mut self) -> &mut HashMap> { if self.path == self.create_path { @@ -154,57 +91,3 @@ where self } } - -#[cfg(feature = "upgrade")] -impl< - FnOnConnect, - FnOnConnectFut, - FnOnSend, - FnOnSendFut, - FnOnReceive, - FnOnReceiveFut, - FnOnClose, - FnOnCloseFut, - > - WSHandlerAppend< - FnOnConnect, - FnOnConnectFut, - FnOnSend, - FnOnSendFut, - FnOnReceive, - FnOnReceiveFut, - FnOnClose, - FnOnCloseFut, - > for Route -where - FnOnConnect: Fn(Arc>, UnboundedSender) -> FnOnConnectFut - + Send - + Sync - + 'static, - FnOnConnectFut: Future> + Send + Sync + 'static, - FnOnSend: Fn(Message, Arc>) -> FnOnSendFut + Send + Sync + 'static, - FnOnSendFut: Future> + Send + Sync + 'static, - FnOnReceive: Fn(Message, Arc>) -> FnOnReceiveFut + Send + Sync + 'static, - FnOnReceiveFut: Future> + Send + Sync + 'static, - FnOnClose: Fn(Arc>) -> FnOnCloseFut + Send + Sync + 'static, - FnOnCloseFut: Future + Send + Sync + 'static, -{ - fn ws( - mut self, - config: Option, - handler: WebSocketHandler< - FnOnConnect, - FnOnConnectFut, - FnOnSend, - FnOnSendFut, - FnOnReceive, - FnOnReceiveFut, - FnOnClose, - FnOnCloseFut, - >, - ) -> Self { - let handler = HandlerWrapperWebSocket::new(config).set_handler(handler); - self.ws_handler_append(handler); - self - } -} diff --git a/silent/src/ws/mod.rs b/silent/src/ws/mod.rs index d99e922..ccd4ac4 100644 --- a/silent/src/ws/mod.rs +++ b/silent/src/ws/mod.rs @@ -1,6 +1,7 @@ mod handler; mod handler_wrapper_websocket; mod message; +mod route; mod types; mod upgrade; mod websocket; @@ -8,6 +9,7 @@ mod websocket_handler; pub use handler_wrapper_websocket::HandlerWrapperWebSocket; pub use message::Message; +pub use route::WSHandlerAppend; pub use types::{FnOnClose, FnOnConnect, FnOnNoneResultFut, FnOnReceive, FnOnSend, FnOnSendFut}; pub use upgrade::WebSocketParts; pub use websocket::WebSocket; From 4a98ade0665ef7037dbe9cfab64ed75960988b9b Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Fri, 11 Oct 2024 16:48:18 +0800 Subject: [PATCH 16/49] chore: deps update --- examples/configs/Cargo.toml | 2 +- examples/custom_handler/Cargo.toml | 2 +- examples/custom_tokio_listener/Cargo.toml | 2 +- examples/custom_tokio_runtime/Cargo.toml | 2 +- examples/exception_handler/Cargo.toml | 2 +- examples/form/Cargo.toml | 2 +- examples/grpc/Cargo.toml | 6 +++--- examples/grpc_h2c/Cargo.toml | 10 +++++----- examples/grpc_streaming/Cargo.toml | 6 +++--- examples/middleware/Cargo.toml | 2 +- examples/multipart-form/Cargo.toml | 2 +- examples/sse-chat/Cargo.toml | 2 +- examples/templates/Cargo.toml | 2 +- examples/todo/Cargo.toml | 4 ++-- examples/websocket-chat/Cargo.toml | 4 ++-- examples/websocket/Cargo.toml | 8 ++++---- silent/Cargo.toml | 22 +++++++++++----------- 17 files changed, 40 insertions(+), 40 deletions(-) diff --git a/examples/configs/Cargo.toml b/examples/configs/Cargo.toml index 56c69a2..bcd60cb 100644 --- a/examples/configs/Cargo.toml +++ b/examples/configs/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -async-trait = "0.1.82" +async-trait = "0.1.83" diff --git a/examples/custom_handler/Cargo.toml b/examples/custom_handler/Cargo.toml index 8c5926f..c9845c6 100644 --- a/examples/custom_handler/Cargo.toml +++ b/examples/custom_handler/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -async-trait = "0.1.82" +async-trait = "0.1.83" diff --git a/examples/custom_tokio_listener/Cargo.toml b/examples/custom_tokio_listener/Cargo.toml index 9109806..a688374 100644 --- a/examples/custom_tokio_listener/Cargo.toml +++ b/examples/custom_tokio_listener/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -async-trait = "0.1.82" +async-trait = "0.1.83" tokio = { version = "1.40.0", features = ["full"] } diff --git a/examples/custom_tokio_runtime/Cargo.toml b/examples/custom_tokio_runtime/Cargo.toml index 2800238..56b4432 100644 --- a/examples/custom_tokio_runtime/Cargo.toml +++ b/examples/custom_tokio_runtime/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -async-trait = "0.1.82" +async-trait = "0.1.83" tokio = { version = "1.40.0", features = ["full"] } diff --git a/examples/exception_handler/Cargo.toml b/examples/exception_handler/Cargo.toml index 4eb0e6f..8b11ebb 100644 --- a/examples/exception_handler/Cargo.toml +++ b/examples/exception_handler/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -serde = { version = "1.0.209", features = ["derive"] } +serde = { version = "1.0.210", features = ["derive"] } diff --git a/examples/form/Cargo.toml b/examples/form/Cargo.toml index e450f80..cf8e409 100644 --- a/examples/form/Cargo.toml +++ b/examples/form/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -serde = { version = "1.0.209", features = ["derive"] } +serde = { version = "1.0.210", features = ["derive"] } diff --git a/examples/grpc/Cargo.toml b/examples/grpc/Cargo.toml index b3775f5..bfd86fe 100644 --- a/examples/grpc/Cargo.toml +++ b/examples/grpc/Cargo.toml @@ -13,11 +13,11 @@ name = "example-grpc-client" path = "src/client.rs" [dependencies] -tonic = { version = "0.12.2" } +tonic = { version = "0.12.3" } prost = "0.13" tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] } silent = { path = "../../silent", features = ["grpc"] } -async-trait = "0.1.82" +async-trait = "0.1.83" [build-dependencies] -tonic-build = { version = "0.12.2" } +tonic-build = { version = "0.12.3" } diff --git a/examples/grpc_h2c/Cargo.toml b/examples/grpc_h2c/Cargo.toml index 535958c..e753f6d 100644 --- a/examples/grpc_h2c/Cargo.toml +++ b/examples/grpc_h2c/Cargo.toml @@ -13,15 +13,15 @@ name = "example-grpc-client" path = "src/client.rs" [dependencies] -tonic = { version = "0.12.2" } +tonic = { version = "0.12.3" } prost = "0.13" tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] } silent = { path = "../../silent", features = ["grpc"] } -async-trait = "0.1.82" +async-trait = "0.1.83" http = "1.1.0" -hyper-util = "0.1.7" +hyper-util = "0.1.9" hyper = "1.4.1" -tower = "0.5.0" +tower = "0.5.1" [build-dependencies] -tonic-build = { version = "0.12.2" } +tonic-build = { version = "0.12.3" } diff --git a/examples/grpc_streaming/Cargo.toml b/examples/grpc_streaming/Cargo.toml index b5a838f..57e75b3 100644 --- a/examples/grpc_streaming/Cargo.toml +++ b/examples/grpc_streaming/Cargo.toml @@ -13,13 +13,13 @@ name = "example-grpc-client" path = "src/client.rs" [dependencies] -tonic = { version = "0.12.2" } +tonic = { version = "0.12.3" } prost = "0.13" tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] } silent = { path = "../../silent", features = ["grpc"] } -async-trait = "0.1.82" +async-trait = "0.1.83" tokio-stream = "0.1.16" h2 = "0.4.6" [build-dependencies] -tonic-build = { version = "0.12.2" } +tonic-build = { version = "0.12.3" } diff --git a/examples/middleware/Cargo.toml b/examples/middleware/Cargo.toml index 6340e4a..a4e02fa 100644 --- a/examples/middleware/Cargo.toml +++ b/examples/middleware/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-trait = "0.1.82" +async-trait = "0.1.83" silent = { path = "../../silent" } diff --git a/examples/multipart-form/Cargo.toml b/examples/multipart-form/Cargo.toml index e844134..53d960f 100644 --- a/examples/multipart-form/Cargo.toml +++ b/examples/multipart-form/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] silent = { path = "../../silent", features = ["multipart"] } -serde = { version = "1.0.209", features = ["derive"] } +serde = { version = "1.0.210", features = ["derive"] } diff --git a/examples/sse-chat/Cargo.toml b/examples/sse-chat/Cargo.toml index 39d053d..ff8dfbc 100644 --- a/examples/sse-chat/Cargo.toml +++ b/examples/sse-chat/Cargo.toml @@ -11,4 +11,4 @@ once_cell = "1" parking_lot = "0.12" tokio = { version = "1", features = ["macros"] } tokio-stream = { version = "0.1", features = ["net"] } -serde = { version = "1.0.209", features = ["derive"] } +serde = { version = "1.0.210", features = ["derive"] } diff --git a/examples/templates/Cargo.toml b/examples/templates/Cargo.toml index ed42d23..1377d6a 100644 --- a/examples/templates/Cargo.toml +++ b/examples/templates/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -serde = { version = "1.0.209", features = ["derive"] } +serde = { version = "1.0.210", features = ["derive"] } silent = { path = "../../silent", features = ["template"] } diff --git a/examples/todo/Cargo.toml b/examples/todo/Cargo.toml index 17555fd..4f3b64e 100644 --- a/examples/todo/Cargo.toml +++ b/examples/todo/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -serde = { version = "1.0.209", features = ["derive"] } +serde = { version = "1.0.210", features = ["derive"] } uuid = { version = "1.10.0", features = ["serde", "v4"] } -async-trait = "0.1.82" +async-trait = "0.1.83" diff --git a/examples/websocket-chat/Cargo.toml b/examples/websocket-chat/Cargo.toml index cf7236b..8a28232 100644 --- a/examples/websocket-chat/Cargo.toml +++ b/examples/websocket-chat/Cargo.toml @@ -9,5 +9,5 @@ publish = false silent = { path = "../../silent", features = ["upgrade"] } tokio = { version = "1.40.0", features = ["full"] } tokio-stream = { version = "0.1.16", features = ["net"] } -futures-util = { version = "0.3.30", default-features = false } -once_cell = "1.19.0" +futures-util = { version = "0.3.31", default-features = false } +once_cell = "1.20.2" diff --git a/examples/websocket/Cargo.toml b/examples/websocket/Cargo.toml index 5dd0b51..f59d943 100644 --- a/examples/websocket/Cargo.toml +++ b/examples/websocket/Cargo.toml @@ -6,10 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-trait = "0.1.82" +async-trait = "0.1.83" silent = { path = "../../silent", features = ["upgrade"] } tokio = { version = "1.40.0", features = ["full"] } -tokio-tungstenite = "0.23.1" -futures-util = "0.3.30" -backtrace = "0.3.73" +tokio-tungstenite = "0.24.0" +futures-util = "0.3.31" +backtrace = "0.3.74" headers = "0.4.0" diff --git a/silent/Cargo.toml b/silent/Cargo.toml index 1af8cec..cadd4d9 100644 --- a/silent/Cargo.toml +++ b/silent/Cargo.toml @@ -34,33 +34,33 @@ grpc = ["upgrade", "dep:tonic", "dep:pin-project-lite", "dep:pin-project", "dep: [dependencies] # Basic dependencies -thiserror = "1.0.63" +thiserror = "1.0.64" hyper = { version = "1.4.1", features = ["full"] } -hyper-util = { version = "0.1.7", features = ["server-auto", "tokio"] } +hyper-util = { version = "0.1.9", features = ["server-auto", "tokio"] } tokio = { version = "1.40.0", optional = true } -bytes = "1.7.1" +bytes = "1.7.2" http-body-util = "0.1.2" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["chrono"] } -async-trait = "0.1.82" -serde = { version = "1.0.209", features = ["derive"] } +async-trait = "0.1.83" +serde = { version = "1.0.210", features = ["derive"] } serde_json = "1.0.128" uuid = "1.10.0" url = "2.5.2" serde_urlencoded = "0.7.1" mime = "0.3.17" -futures-util = "0.3.30" +futures-util = "0.3.31" chrono = { version = "0.4.38", default-features = false, features = ["clock"] } -tokio-tungstenite = { version = "0.23.1", optional = true } +tokio-tungstenite = { version = "0.24.0", optional = true } headers = "0.4.0" tokio-stream = { version = "0.1.16", features = ["net"], optional = true } pin-project = { version = "1.1", optional = true } pin-project-lite = { version = "0.2.14", optional = true } http = "1.1.0" http-body = "1.0.1" -futures = "0.3.30" +futures = "0.3.31" tokio-util = "0.7.12" -anyhow = "1.0.86" +anyhow = "1.0.89" # Scheduler cron = { version = "0.12.1", optional = true } @@ -68,7 +68,7 @@ cron = { version = "0.12.1", optional = true } # Multipart multer = { version = "3.1.0", optional = true } multimap = { version = "0.10.0", features = ["serde"], optional = true } -tempfile = { version = "3.12.0", optional = true } +tempfile = { version = "3.13.0", optional = true } textnonce = { version = "1.0.0", optional = true } @@ -80,7 +80,7 @@ async-session = { version = "3.0.0", optional = true } cookie = { version = "0.18.1", features = ["secure", "percent-encode"], optional = true } # Grpc -tonic = { version = "0.12.2", optional = true } +tonic = { version = "0.12.3", optional = true } # Security argon2 = { version = "0.5.3", optional = true } From 73bd8373280a3b9aac109f4019f73bec4fe25bc0 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Sat, 12 Oct 2024 09:00:00 +0800 Subject: [PATCH 17/49] =?UTF-8?q?chore:=20session=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/session/src/main.rs | 18 ++++++-------- silent/src/prelude.rs | 2 ++ silent/src/session/mod.rs | 4 +++ silent/src/session/session_ext.rs | 41 +++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 silent/src/session/session_ext.rs diff --git a/examples/session/src/main.rs b/examples/session/src/main.rs index b248d99..48d5e65 100644 --- a/examples/session/src/main.rs +++ b/examples/session/src/main.rs @@ -3,17 +3,13 @@ use silent::prelude::*; fn main() { logger::fmt().with_max_level(Level::INFO).init(); let route = Route::new("").get(|mut req: Request| async move { - let session = req.extensions_mut().get_mut::(); - match session { - None => {} - Some(session) => { - if let Some(state) = session.clone().get::("state") { - session.insert("state", state + 1)?; - return Ok(state.to_string()); - } else { - session.insert("state", 1)?; - } - } + let state = req.session::<&str, i64>("state"); + let sessions_mut = req.sessions_mut(); + if let Some(state) = state { + sessions_mut.insert("state", state + 1)?; + return Ok(state.to_string()); + } else { + sessions_mut.insert("state", 1)?; } Ok("hello world".to_string()) }); diff --git a/silent/src/prelude.rs b/silent/src/prelude.rs index 0f0a1e9..d7cc104 100644 --- a/silent/src/prelude.rs +++ b/silent/src/prelude.rs @@ -24,6 +24,8 @@ pub use crate::scheduler::{SchedulerExt, Task}; pub use crate::security::{argon2, pbkdf2}; #[cfg(feature = "server")] pub use crate::service::Server; +#[cfg(feature = "session")] +pub use crate::session::session_ext::SessionExt; #[cfg(feature = "sse")] pub use crate::sse::{sse_reply, SSEEvent}; #[cfg(feature = "template")] diff --git a/silent/src/session/mod.rs b/silent/src/session/mod.rs index d5f498b..380722f 100644 --- a/silent/src/session/mod.rs +++ b/silent/src/session/mod.rs @@ -1,3 +1,5 @@ +pub mod session_ext; + use crate::{ CookieExt, Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError, StatusCode, }; @@ -41,6 +43,7 @@ where let cookie = cookies.get("silent-web-session"); let session_store = self.session_store.read().await; let session = if cookie.is_none() { + req.extensions_mut().insert(cookies.clone()); Session::new() } else { let cookie = cookie.unwrap(); @@ -58,6 +61,7 @@ where req.extensions_mut().insert(session.clone()); let mut res = next.call(req).await?; res.extensions_mut().insert(session.clone()); + res.extensions_mut().insert(cookies); let cookie_value = session_store.store_session(session).await.map_err(|e| { SilentError::business_error( StatusCode::INTERNAL_SERVER_ERROR, diff --git a/silent/src/session/session_ext.rs b/silent/src/session/session_ext.rs new file mode 100644 index 0000000..3785ef9 --- /dev/null +++ b/silent/src/session/session_ext.rs @@ -0,0 +1,41 @@ +use crate::{Request, Response}; +use async_session::Session; +use http_body::Body; +use serde::de::DeserializeOwned; + +pub trait SessionExt { + /// Get `Session` reference. + fn sessions(&self) -> Session; + /// Get `Session` mutable reference. + fn sessions_mut(&mut self) -> &mut Session; + /// Get `Session` from session. + fn session, V: DeserializeOwned>(&self, name: K) -> Option; +} + +impl SessionExt for Request { + fn sessions(&self) -> Session { + self.extensions().get().cloned().unwrap_or_default() + } + + fn sessions_mut(&mut self) -> &mut Session { + self.extensions_mut().get_mut().unwrap() + } + + fn session, V: DeserializeOwned>(&self, name: K) -> Option { + self.sessions().get(name.as_ref()) + } +} + +impl SessionExt for Response { + fn sessions(&self) -> Session { + self.extensions().get().cloned().unwrap_or_default() + } + + fn sessions_mut(&mut self) -> &mut Session { + self.extensions_mut().get_mut().unwrap() + } + + fn session, V: DeserializeOwned>(&self, name: K) -> Option { + self.sessions().get(name.as_ref()) + } +} From ddb6672cd4161a985b636c8d0e94b2608a65d95b Mon Sep 17 00:00:00 2001 From: Hubert Shelley <46239302+hubertshelley@users.noreply.github.com> Date: Mon, 14 Oct 2024 16:25:01 +0800 Subject: [PATCH 18/49] =?UTF-8?q?=E5=90=88=E5=B9=B6=E6=8B=89=E5=8F=96?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=20#67?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: cookie优化调整 * fix: session异常修复 --- examples/session/src/main.rs | 6 +- .../{core/cookie.rs => cookie/cookie_ext.rs} | 0 silent/src/cookie/middleware.rs | 48 +++++++++++ silent/src/cookie/mod.rs | 2 + silent/src/core/mod.rs | 2 - silent/src/lib.rs | 4 +- silent/src/prelude.rs | 4 +- silent/src/route/mod.rs | 14 --- silent/src/route/root.rs | 8 +- silent/src/service/mod.rs | 2 + silent/src/session/middleware.rs | 86 +++++++++++++++++++ silent/src/session/mod.rs | 79 +---------------- silent/src/session/session_ext.rs | 6 +- 13 files changed, 158 insertions(+), 103 deletions(-) rename silent/src/{core/cookie.rs => cookie/cookie_ext.rs} (100%) create mode 100644 silent/src/cookie/middleware.rs create mode 100644 silent/src/cookie/mod.rs create mode 100644 silent/src/session/middleware.rs diff --git a/examples/session/src/main.rs b/examples/session/src/main.rs index 48d5e65..4ee6f37 100644 --- a/examples/session/src/main.rs +++ b/examples/session/src/main.rs @@ -3,7 +3,7 @@ use silent::prelude::*; fn main() { logger::fmt().with_max_level(Level::INFO).init(); let route = Route::new("").get(|mut req: Request| async move { - let state = req.session::<&str, i64>("state"); + let state = req.session::("state"); let sessions_mut = req.sessions_mut(); if let Some(state) = state { sessions_mut.insert("state", state + 1)?; @@ -13,5 +13,7 @@ fn main() { } Ok("hello world".to_string()) }); - Server::new().run(route); + Server::new() + .bind("127.0.0.1:8000".parse().unwrap()) + .run(route); } diff --git a/silent/src/core/cookie.rs b/silent/src/cookie/cookie_ext.rs similarity index 100% rename from silent/src/core/cookie.rs rename to silent/src/cookie/cookie_ext.rs diff --git a/silent/src/cookie/middleware.rs b/silent/src/cookie/middleware.rs new file mode 100644 index 0000000..b0bbcc1 --- /dev/null +++ b/silent/src/cookie/middleware.rs @@ -0,0 +1,48 @@ +use crate::{Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError}; +use async_trait::async_trait; +use cookie::{Cookie, CookieJar}; +use http::{header, StatusCode}; + +#[derive(Debug, Default)] +pub struct CookieMiddleware {} + +impl CookieMiddleware { + pub(crate) fn new() -> Self { + Self::default() + } +} + +#[async_trait] +impl MiddleWareHandler for CookieMiddleware { + async fn handle(&self, mut req: Request, next: &Next) -> Result { + let mut jar = CookieJar::new(); + if let Some(cookies) = req.headers().get(header::COOKIE) { + for cookie_str in cookies + .to_str() + .map_err(|e| { + SilentError::business_error( + StatusCode::BAD_REQUEST, + format!("Failed to parse cookie: {}", e), + ) + })? + .split(';') + .map(|s| s.trim()) + { + if let Ok(cookie) = Cookie::parse_encoded(cookie_str).map(|c| c.into_owned()) { + jar.add_original(cookie); + } + } + } + req.extensions_mut().insert(jar.clone()); + let mut res = next.call(req).await?; + if let Some(cookie_jar) = res.extensions().get::() { + for cookie in cookie_jar.delta().cloned() { + jar.add(cookie) + } + res.extensions_mut().insert(jar); + } else { + res.extensions_mut().insert(jar); + }; + Ok(res) + } +} diff --git a/silent/src/cookie/mod.rs b/silent/src/cookie/mod.rs new file mode 100644 index 0000000..e5a089c --- /dev/null +++ b/silent/src/cookie/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod cookie_ext; +pub(crate) mod middleware; diff --git a/silent/src/core/mod.rs b/silent/src/core/mod.rs index 56a8e38..447e661 100644 --- a/silent/src/core/mod.rs +++ b/silent/src/core/mod.rs @@ -1,7 +1,5 @@ pub mod adapt; -#[cfg(feature = "cookie")] -pub(crate) mod cookie; #[cfg(feature = "multipart")] pub(crate) mod form; pub(crate) mod next; diff --git a/silent/src/lib.rs b/silent/src/lib.rs index e6a452e..ee65dfe 100644 --- a/silent/src/lib.rs +++ b/silent/src/lib.rs @@ -1,4 +1,6 @@ mod configs; +#[cfg(feature = "cookie")] +mod cookie; /// The `silent` library. mod core; mod error; @@ -32,7 +34,7 @@ use multer; pub use crate::configs::Configs; #[cfg(feature = "cookie")] -pub use crate::core::cookie::CookieExt; +pub use crate::cookie::cookie_ext::CookieExt; pub use crate::core::{next::Next, request::Request, response::Response}; #[cfg(feature = "grpc")] pub use crate::grpc::{GrpcHandler, GrpcRegister}; diff --git a/silent/src/prelude.rs b/silent/src/prelude.rs index d7cc104..e6b53ca 100644 --- a/silent/src/prelude.rs +++ b/silent/src/prelude.rs @@ -1,6 +1,6 @@ pub use crate::configs::Configs; #[cfg(feature = "cookie")] -pub use crate::core::cookie::CookieExt; +pub use crate::cookie::cookie_ext::CookieExt; #[cfg(feature = "multipart")] pub use crate::core::form::{FilePart, FormData}; pub use crate::core::{ @@ -39,6 +39,6 @@ pub use crate::ws::{Message, WebSocket, WebSocketHandler, WebSocketParts}; #[cfg(feature = "session")] pub use async_session::{Session, SessionStore}; #[cfg(feature = "cookie")] -pub use cookie::{time as CookieTime, Cookie}; +pub use cookie::{time as CookieTime, Cookie, CookieJar, Key}; pub use headers; pub use hyper::{header, upgrade, Method, StatusCode}; diff --git a/silent/src/route/mod.rs b/silent/src/route/mod.rs index dc40ccf..2460fc3 100644 --- a/silent/src/route/mod.rs +++ b/silent/src/route/mod.rs @@ -1,5 +1,3 @@ -#[cfg(feature = "session")] -use async_session::Session; use async_trait::async_trait; use http::StatusCode; pub use root::RootRoute; @@ -14,8 +12,6 @@ use crate::handler::Handler; use crate::middleware::MiddleWareHandler; #[cfg(feature = "static")] use crate::prelude::HandlerGetter; -#[cfg(feature = "cookie")] -use crate::CookieExt; use crate::{Method, Next, Request, Response, SilentError}; pub(crate) mod handler_append; @@ -191,16 +187,6 @@ impl Handler for Route { Some(handler) => { let mut pre_res = Response::empty(); pre_res.configs = configs; - #[cfg(feature = "cookie")] - { - pre_res.extensions_mut().insert(req.cookies().clone()); - } - #[cfg(feature = "session")] - let session = req.extensions().get::().cloned(); - #[cfg(feature = "session")] - if let Some(session) = session { - pre_res.extensions.insert(session); - } let mut active_middlewares = vec![]; for middleware in self.middlewares.iter().cloned() { if middleware.match_req(&req).await { diff --git a/silent/src/route/root.rs b/silent/src/route/root.rs index 9b0981e..993ac82 100644 --- a/silent/src/route/root.rs +++ b/silent/src/route/root.rs @@ -1,8 +1,10 @@ +#[cfg(feature = "cookie")] +use crate::cookie::middleware::CookieMiddleware; use crate::middlewares::RequestTimeLogger; use crate::route::handler_match::{Match, RouteMatched}; use crate::route::Route; #[cfg(feature = "session")] -use crate::session::SessionMiddleware; +use crate::session::middleware::SessionMiddleware; #[cfg(feature = "template")] use crate::templates::TemplateMiddleware; #[cfg(feature = "scheduler")] @@ -133,6 +135,10 @@ impl RootRoute { self.hook_first(SessionMiddleware::default()) } } + #[cfg(feature = "cookie")] + pub fn check_cookie(&mut self) { + self.hook_first(CookieMiddleware::new()) + } #[cfg(feature = "template")] pub fn set_template_dir(&mut self, dir: impl Into) -> &mut Self { diff --git a/silent/src/service/mod.rs b/silent/src/service/mod.rs index ff9452e..835ecf3 100644 --- a/silent/src/service/mod.rs +++ b/silent/src/service/mod.rs @@ -96,6 +96,8 @@ impl Server { root_route.set_configs(configs.clone()); #[cfg(feature = "session")] root_route.check_session(); + #[cfg(feature = "cookie")] + root_route.check_cookie(); #[cfg(feature = "scheduler")] let scheduler = root_route.scheduler.clone(); #[cfg(feature = "scheduler")] diff --git a/silent/src/session/middleware.rs b/silent/src/session/middleware.rs new file mode 100644 index 0000000..59c9402 --- /dev/null +++ b/silent/src/session/middleware.rs @@ -0,0 +1,86 @@ +use crate::{ + CookieExt, Handler, MiddleWareHandler, Next, Request, Response, SilentError, StatusCode, +}; +use async_session::{MemoryStore, Session, SessionStore}; +use async_trait::async_trait; +use cookie::{Cookie, CookieJar}; +use std::sync::Arc; +use tokio::sync::RwLock; + +pub struct SessionMiddleware +where + T: SessionStore, +{ + pub session_store: Arc>, +} + +impl Default for SessionMiddleware { + fn default() -> SessionMiddleware { + let session = MemoryStore::new(); + Self::new(session) + } +} + +impl SessionMiddleware +where + T: SessionStore, +{ + pub fn new(session: T) -> Self { + let session_store = Arc::new(RwLock::new(session)); + SessionMiddleware { session_store } + } +} + +#[async_trait] +impl MiddleWareHandler for SessionMiddleware +where + T: SessionStore, +{ + async fn handle(&self, mut req: Request, next: &Next) -> crate::Result { + let mut cookies = req.cookies().clone(); + let cookie = cookies.get("silent-web-session"); + let session_store = self.session_store.read().await; + let mut session_key_exists = false; + let cookie_value = if cookie.is_some() { + session_key_exists = true; + cookie.unwrap().value().to_string() + } else { + session_store.store_session(Session::new()).await?.unwrap() + }; + let session = session_store + .load_session(cookie_value.clone()) + .await + .map_err(|e| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Failed to load session: {}", e), + ) + })? + .ok_or(SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + "Failed to load session", + ))?; + req.extensions_mut().insert(session.clone()); + let session_copied = session.clone(); + if !session_key_exists { + cookies.add( + Cookie::build(("silent-web-session", cookie_value)) + .max_age(cookie::time::Duration::hours(2)), + ); + } + let mut res = next.call(req).await?; + if res.extensions().get::().is_none() { + res.extensions_mut().insert(session_copied); + } + if res.extensions().get::().is_none() { + res.extensions_mut().insert(cookies); + } else { + let cookie_jar = res.extensions_mut().get_mut::().unwrap(); + for cookie in cookie_jar.iter() { + cookies.add(cookie.clone()); + } + res.extensions_mut().insert(cookies.clone()); + } + Ok(res) + } +} diff --git a/silent/src/session/mod.rs b/silent/src/session/mod.rs index 380722f..c100e27 100644 --- a/silent/src/session/mod.rs +++ b/silent/src/session/mod.rs @@ -1,79 +1,2 @@ +pub(crate) mod middleware; pub mod session_ext; - -use crate::{ - CookieExt, Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError, StatusCode, -}; -use async_session::{MemoryStore, Session, SessionStore}; -use async_trait::async_trait; -use cookie::Cookie; -use std::sync::Arc; -use tokio::sync::RwLock; - -pub struct SessionMiddleware -where - T: SessionStore, -{ - pub session_store: Arc>, -} - -impl Default for SessionMiddleware { - fn default() -> SessionMiddleware { - let session = MemoryStore::new(); - Self::new(session) - } -} - -impl SessionMiddleware -where - T: SessionStore, -{ - pub fn new(session: T) -> Self { - let session_store = Arc::new(RwLock::new(session)); - SessionMiddleware { session_store } - } -} - -#[async_trait] -impl MiddleWareHandler for SessionMiddleware -where - T: SessionStore, -{ - async fn handle(&self, mut req: Request, next: &Next) -> Result { - let cookies = req.cookies().clone(); - let cookie = cookies.get("silent-web-session"); - let session_store = self.session_store.read().await; - let session = if cookie.is_none() { - req.extensions_mut().insert(cookies.clone()); - Session::new() - } else { - let cookie = cookie.unwrap(); - session_store - .load_session(cookie.value().to_string()) - .await - .map_err(|e| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Failed to load session: {}", e), - ) - })? - .unwrap_or_default() - }; - req.extensions_mut().insert(session.clone()); - let mut res = next.call(req).await?; - res.extensions_mut().insert(session.clone()); - res.extensions_mut().insert(cookies); - let cookie_value = session_store.store_session(session).await.map_err(|e| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Failed to store session: {}", e), - ) - })?; - if let Some(cookie_value) = cookie_value { - res.cookies_mut().add( - Cookie::build(("silent-web-session", cookie_value)) - .max_age(cookie::time::Duration::hours(2)), - ); - } - Ok(res) - } -} diff --git a/silent/src/session/session_ext.rs b/silent/src/session/session_ext.rs index 3785ef9..03a7376 100644 --- a/silent/src/session/session_ext.rs +++ b/silent/src/session/session_ext.rs @@ -9,7 +9,7 @@ pub trait SessionExt { /// Get `Session` mutable reference. fn sessions_mut(&mut self) -> &mut Session; /// Get `Session` from session. - fn session, V: DeserializeOwned>(&self, name: K) -> Option; + fn session(&self, name: &str) -> Option; } impl SessionExt for Request { @@ -21,7 +21,7 @@ impl SessionExt for Request { self.extensions_mut().get_mut().unwrap() } - fn session, V: DeserializeOwned>(&self, name: K) -> Option { + fn session(&self, name: &str) -> Option { self.sessions().get(name.as_ref()) } } @@ -35,7 +35,7 @@ impl SessionExt for Response { self.extensions_mut().get_mut().unwrap() } - fn session, V: DeserializeOwned>(&self, name: K) -> Option { + fn session(&self, name: &str) -> Option { self.sessions().get(name.as_ref()) } } From 2df1cb5b53256cd5389bfdf4f1404384bc77632c Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Mon, 14 Oct 2024 17:05:59 +0800 Subject: [PATCH 19/49] =?UTF-8?q?fix:=20cors=20=E5=BC=82=E5=B8=B8=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- silent/src/middleware/middlewares/cors.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/silent/src/middleware/middlewares/cors.rs b/silent/src/middleware/middlewares/cors.rs index bb62b1f..ffd8d99 100644 --- a/silent/src/middleware/middlewares/cors.rs +++ b/silent/src/middleware/middlewares/cors.rs @@ -234,7 +234,15 @@ impl MiddleWareHandler for Cors { if req.method() == Method::OPTIONS { return Ok(res); } - res.copy_from_response(next.call(req).await?); - Ok(res) + match next.call(req).await { + Ok(result) => { + res.copy_from_response(result); + Ok(res) + } + Err(e) => { + res.copy_from_response(e.into()); + Ok(res) + } + } } } From ec779422ed83b16ff130bcef27d4360c7348cd34 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Fri, 8 Nov 2024 15:50:47 +0800 Subject: [PATCH 20/49] feat: support vec in query by serde_html_form --- silent/Cargo.toml | 2 +- silent/src/core/request.rs | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/silent/Cargo.toml b/silent/Cargo.toml index cadd4d9..3fd8ddb 100644 --- a/silent/Cargo.toml +++ b/silent/Cargo.toml @@ -47,7 +47,7 @@ serde = { version = "1.0.210", features = ["derive"] } serde_json = "1.0.128" uuid = "1.10.0" url = "2.5.2" -serde_urlencoded = "0.7.1" +serde_html_form = "0.2.6" mime = "0.3.17" futures-util = "0.3.31" chrono = { version = "0.4.38", default-features = false, features = ["clock"] } diff --git a/silent/src/core/request.rs b/silent/src/core/request.rs index b8a674c..1ca2ac7 100644 --- a/silent/src/core/request.rs +++ b/silent/src/core/request.rs @@ -264,7 +264,7 @@ impl Request { for<'de> T: Deserialize<'de>, { let query = self.uri().query().unwrap_or(""); - let params = serde_urlencoded::from_str(query)?; + let params = serde_html_form::from_str(query)?; Ok(params) } @@ -357,7 +357,7 @@ impl Request { match content_type.subtype() { mime::WWW_FORM_URLENCODED => { let bytes = body.collect().await.unwrap().to_bytes(); - serde_urlencoded::from_bytes(&bytes).map_err(SilentError::from) + serde_html_form::from_bytes(&bytes).map_err(SilentError::from) } mime::JSON => { let bytes = body.collect().await.unwrap().to_bytes(); @@ -371,7 +371,7 @@ impl Request { } ReqBody::Once(bytes) => match content_type.subtype() { mime::WWW_FORM_URLENCODED => { - serde_urlencoded::from_bytes(&bytes).map_err(SilentError::from) + serde_html_form::from_bytes(&bytes).map_err(SilentError::from) } mime::JSON => serde_json::from_slice(&bytes).map_err(|e| e.into()), _ => Err(SilentError::JsonEmpty), @@ -413,3 +413,30 @@ impl Request { (self, url) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Deserialize, Debug, PartialEq)] + struct TestStruct { + a: i32, + b: String, + #[serde(default, alias = "c[]")] + c: Vec, + } + + #[test] + fn test_query_parse_alias() { + let mut req = Request::empty(); + *req.uri_mut() = Uri::from_static("http://localhost:8080/test?a=1&b=2&c[]=3&c[]=4"); + let _ = req.params_parse::().unwrap(); + } + + #[test] + fn test_query_parse() { + let mut req = Request::empty(); + *req.uri_mut() = Uri::from_static("http://localhost:8080/test?a=1&b=2&c=3&c=4"); + let _ = req.params_parse::().unwrap(); + } +} From c52e0b0e12c6e2ae9947ee92df4b1288fdbf9a54 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Fri, 8 Nov 2024 17:23:25 +0800 Subject: [PATCH 21/49] =?UTF-8?q?feat:=20sse=E5=BE=AE=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/sse-chat/src/main.rs | 3 +-- silent/src/prelude.rs | 2 +- silent/src/sse/keep_alive.rs | 24 ++++++++++++++---------- silent/src/sse/mod.rs | 3 +-- silent/src/sse/reply.rs | 9 ++++----- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/examples/sse-chat/src/main.rs b/examples/sse-chat/src/main.rs index 80a958e..f50ada1 100644 --- a/examples/sse-chat/src/main.rs +++ b/examples/sse-chat/src/main.rs @@ -82,8 +82,7 @@ async fn user_connected(_req: Request) -> Result { Ok(SSEEvent::default().data(reply)) } }); - let res = sse_reply(stream); - Ok(res) + sse_reply(stream) } fn user_message(my_id: usize, msg: &str) { diff --git a/silent/src/prelude.rs b/silent/src/prelude.rs index e6b53ca..dbaf0bd 100644 --- a/silent/src/prelude.rs +++ b/silent/src/prelude.rs @@ -27,7 +27,7 @@ pub use crate::service::Server; #[cfg(feature = "session")] pub use crate::session::session_ext::SessionExt; #[cfg(feature = "sse")] -pub use crate::sse::{sse_reply, SSEEvent}; +pub use crate::sse::{sse_reply, KeepAlive, SSEEvent}; #[cfg(feature = "template")] pub use crate::templates::*; #[cfg(feature = "upgrade")] diff --git a/silent/src/sse/keep_alive.rs b/silent/src/sse/keep_alive.rs index 0d4dc7c..b5db1b1 100644 --- a/silent/src/sse/keep_alive.rs +++ b/silent/src/sse/keep_alive.rs @@ -20,8 +20,20 @@ pub struct KeepAlive { max_interval: Duration, } -#[allow(dead_code)] +impl Default for KeepAlive { + fn default() -> Self { + Self { + comment_text: Cow::Borrowed(""), + max_interval: Duration::from_secs(15), + } + } +} + impl KeepAlive { + pub fn new() -> Self { + Self::default() + } + /// Customize the interval between keep-alive messages. /// /// Default is 15 seconds. @@ -33,7 +45,7 @@ impl KeepAlive { /// Customize the text of the keep-alive message. /// /// Default is an empty comment. - pub fn text(mut self, text: impl Into>) -> Self { + pub fn comment_text(mut self, text: impl Into>) -> Self { self.comment_text = text.into(); self } @@ -59,7 +71,6 @@ impl KeepAlive { } } -#[allow(missing_debug_implementations)] #[pin_project] struct SseKeepAlive { #[pin] @@ -70,13 +81,6 @@ struct SseKeepAlive { alive_timer: Sleep, } -pub fn keep_alive() -> KeepAlive { - KeepAlive { - comment_text: Cow::Borrowed(""), - max_interval: Duration::from_secs(15), - } -} - impl Stream for SseKeepAlive where S: TryStream + Send + 'static, diff --git a/silent/src/sse/mod.rs b/silent/src/sse/mod.rs index 35ce515..42d74da 100644 --- a/silent/src/sse/mod.rs +++ b/silent/src/sse/mod.rs @@ -45,6 +45,5 @@ mod keep_alive; mod reply; pub use event::SSEEvent; -#[allow(unused_imports)] -pub use keep_alive::{keep_alive, KeepAlive}; +pub use keep_alive::KeepAlive; pub use reply::sse_reply; diff --git a/silent/src/sse/reply.rs b/silent/src/sse/reply.rs index 6d71d71..c75197e 100644 --- a/silent/src/sse/reply.rs +++ b/silent/src/sse/reply.rs @@ -1,17 +1,16 @@ use crate::header::{CACHE_CONTROL, CONTENT_TYPE}; use crate::prelude::stream_body; -use crate::sse::{keep_alive, SSEEvent}; +use crate::sse::{KeepAlive, SSEEvent}; use crate::{headers::HeaderValue, log, Response, Result, SilentError, StatusCode}; use futures_util::{future, Stream, TryStreamExt}; -pub fn sse_reply(stream: S) -> Response +pub fn sse_reply(stream: S) -> Result where S: Stream> + Send + 'static, { - let event_stream = keep_alive().stream(stream); + let event_stream = KeepAlive::default().stream(stream); let body_stream = event_stream .map_err(|error| { - // FIXME: error logging log::error!("sse stream error: {}", error.to_string()); SilentError::BusinessError { code: StatusCode::INTERNAL_SERVER_ERROR, @@ -29,5 +28,5 @@ where // Disable response body caching res.headers_mut() .insert(CACHE_CONTROL, HeaderValue::from_static("no-cache")); - res + Ok(res) } From fdd4d2cb8fcd9d17b2a7db2ed7e4685a61144976 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Thu, 21 Nov 2024 13:20:55 +0800 Subject: [PATCH 22/49] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 95bfde1..715c237 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,4 @@ homepage = "https://github.com/hubertshelley/silent" license = "Apache-2.0" readme = "./readme.md" repository = "https://github.com/hubertshelley/silent" -version = "1.4.0" +version = "2.0.0" From dac382fd598678bb74d98dbc041aad023b575802 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Tue, 26 Nov 2024 16:04:23 +0800 Subject: [PATCH 23/49] feat: moute match edit --- silent/src/route/root.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/silent/src/route/root.rs b/silent/src/route/root.rs index 993ac82..7404778 100644 --- a/silent/src/route/root.rs +++ b/silent/src/route/root.rs @@ -9,7 +9,9 @@ use crate::session::middleware::SessionMiddleware; use crate::templates::TemplateMiddleware; #[cfg(feature = "scheduler")] use crate::Scheduler; -use crate::{Configs, Handler, MiddleWareHandler, Next, Request, Response, SilentError}; +use crate::{ + Configs, Handler, HandlerWrapper, MiddleWareHandler, Next, Request, Response, SilentError, +}; #[cfg(feature = "session")] use async_session::SessionStore; use async_trait::async_trait; @@ -92,7 +94,14 @@ impl Handler for RootHandler { let next = Next::build(Arc::new(route), self.middlewares.clone()); next.call(req).await } - RouteMatched::Unmatched => Err(SilentError::NotFound), + RouteMatched::Unmatched => { + let handler = |_req| async move { Err::<(), SilentError>(SilentError::NotFound) }; + let next = Next::build( + Arc::new(HandlerWrapper::new(handler)), + self.middlewares.clone(), + ); + next.call(req).await + } } } } From b334782ea6b332ecbe338bf9c7a68cfd0b94b7c6 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Fri, 29 Nov 2024 08:47:02 +0800 Subject: [PATCH 24/49] chore: grpc file name edit --- examples/grpc/build.rs | 2 +- .../proto/hello_world.proto} | 4 ++-- examples/grpc/src/client.rs | 2 +- examples/grpc/src/main.rs | 2 +- examples/grpc_h2c/build.rs | 4 ++-- .../proto/hello_world.proto} | 6 +++--- examples/grpc_h2c/src/client.rs | 2 +- examples/grpc_h2c/src/main.rs | 20 +++++++++---------- examples/grpc_streaming/build.rs | 2 +- 9 files changed, 22 insertions(+), 22 deletions(-) rename examples/{grpc_h2c/proto/helloworld.proto => grpc/proto/hello_world.proto} (93%) rename examples/{grpc/proto/helloworld.proto => grpc_h2c/proto/hello_world.proto} (92%) diff --git a/examples/grpc/build.rs b/examples/grpc/build.rs index 78e5e44..cf810e4 100644 --- a/examples/grpc/build.rs +++ b/examples/grpc/build.rs @@ -1,5 +1,5 @@ fn main() { tonic_build::configure() - .compile(&["proto/helloworld.proto"], &["/proto"]) + .compile_protos(&["proto/hello_world.proto"], &["/proto"]) .unwrap(); } diff --git a/examples/grpc_h2c/proto/helloworld.proto b/examples/grpc/proto/hello_world.proto similarity index 93% rename from examples/grpc_h2c/proto/helloworld.proto rename to examples/grpc/proto/hello_world.proto index d79a6a0..f2abed2 100644 --- a/examples/grpc_h2c/proto/helloworld.proto +++ b/examples/grpc/proto/hello_world.proto @@ -15,10 +15,10 @@ syntax = "proto3"; option java_multiple_files = true; -option java_package = "io.grpc.examples.helloworld"; +option java_package = "io.grpc.examples.hello_world"; option java_outer_classname = "HelloWorldProto"; -package helloworld; +package hello_world; // The greeting service definition. service Greeter { diff --git a/examples/grpc/src/client.rs b/examples/grpc/src/client.rs index d451465..769e636 100644 --- a/examples/grpc/src/client.rs +++ b/examples/grpc/src/client.rs @@ -3,7 +3,7 @@ use hello_world::HelloRequest; use silent::prelude::info; pub mod hello_world { - tonic::include_proto!("helloworld"); + tonic::include_proto!("hello_world"); } #[allow(dead_code)] #[tokio::main] diff --git a/examples/grpc/src/main.rs b/examples/grpc/src/main.rs index 9db735e..5a0b5e0 100644 --- a/examples/grpc/src/main.rs +++ b/examples/grpc/src/main.rs @@ -9,7 +9,7 @@ use silent::GrpcRegister; mod client; pub mod hello_world { - tonic::include_proto!("helloworld"); // The string specified here must match the proto package name + tonic::include_proto!("hello_world"); // The string specified here must match the proto package name } #[derive(Debug, Default)] diff --git a/examples/grpc_h2c/build.rs b/examples/grpc_h2c/build.rs index 3b3886e..ad2901a 100644 --- a/examples/grpc_h2c/build.rs +++ b/examples/grpc_h2c/build.rs @@ -4,7 +4,7 @@ fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); tonic_build::configure() - .file_descriptor_set_path(out_dir.join("helloworld_descriptor.bin")) - .compile(&["proto/helloworld.proto"], &["/proto"]) + .file_descriptor_set_path(out_dir.join("hello_world_descriptor.bin")) + .compile_protos(&["proto/hello_world.proto"], &["/proto"]) .unwrap(); } diff --git a/examples/grpc/proto/helloworld.proto b/examples/grpc_h2c/proto/hello_world.proto similarity index 92% rename from examples/grpc/proto/helloworld.proto rename to examples/grpc_h2c/proto/hello_world.proto index 8de5d08..f2abed2 100644 --- a/examples/grpc/proto/helloworld.proto +++ b/examples/grpc_h2c/proto/hello_world.proto @@ -15,10 +15,10 @@ syntax = "proto3"; option java_multiple_files = true; -option java_package = "io.grpc.examples.helloworld"; +option java_package = "io.grpc.examples.hello_world"; option java_outer_classname = "HelloWorldProto"; -package helloworld; +package hello_world; // The greeting service definition. service Greeter { @@ -34,4 +34,4 @@ message HelloRequest { // The response message containing the greetings message HelloReply { string message = 1; -} \ No newline at end of file +} diff --git a/examples/grpc_h2c/src/client.rs b/examples/grpc_h2c/src/client.rs index ae29ae9..45c8825 100644 --- a/examples/grpc_h2c/src/client.rs +++ b/examples/grpc_h2c/src/client.rs @@ -6,7 +6,7 @@ use hyper_util::rt::TokioExecutor; use silent::prelude::info; pub mod hello_world { - tonic::include_proto!("helloworld"); + tonic::include_proto!("hello_world"); } #[tokio::main] diff --git a/examples/grpc_h2c/src/main.rs b/examples/grpc_h2c/src/main.rs index b115f12..db94c70 100644 --- a/examples/grpc_h2c/src/main.rs +++ b/examples/grpc_h2c/src/main.rs @@ -7,7 +7,7 @@ use silent::prelude::{info, logger, HandlerAppend, Level, Route}; use silent::GrpcRegister; pub mod hello_world { - tonic::include_proto!("helloworld"); + tonic::include_proto!("hello_world"); } #[derive(Default)] @@ -30,14 +30,14 @@ impl Greeter for MyGreeter { #[tokio::main] async fn main() -> Result<(), Box> { - let _greeter = MyGreeter::default(); - // logger::fmt().with_max_level(Level::INFO).init(); - // - // let mut route = Route::new("").get(|_req| async { Ok("hello world") }); - // GreeterServer::new(greeter).register(&mut route); - // silent::prelude::Server::new() - // .bind("0.0.0.0:50051".parse().unwrap()) - // .serve(route) - // .await; + let greeter = MyGreeter::default(); + logger::fmt().with_max_level(Level::INFO).init(); + + let mut route = Route::new("").get(|_req| async { Ok("hello world") }); + GreeterServer::new(greeter).register(&mut route); + silent::prelude::Server::new() + .bind("0.0.0.0:50051".parse().unwrap()) + .serve(route) + .await; Ok(()) } diff --git a/examples/grpc_streaming/build.rs b/examples/grpc_streaming/build.rs index 4657bea..2ec2f69 100644 --- a/examples/grpc_streaming/build.rs +++ b/examples/grpc_streaming/build.rs @@ -1,5 +1,5 @@ fn main() { tonic_build::configure() - .compile(&["proto/echo.proto"], &["/proto"]) + .compile_protos(&["proto/echo.proto"], &["/proto"]) .unwrap(); } From f727664699434be844d70e4bdbc961767701b78e Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Wed, 7 Aug 2024 09:28:34 +0800 Subject: [PATCH 25/49] =?UTF-8?q?refactor:=20next=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- silent/src/core/mod.rs | 1 + silent/src/core/next.rs | 57 ++++++++ silent/src/grpc/handler.rs | 34 +++-- silent/src/lib.rs | 7 +- silent/src/middleware/middleware_trait.rs | 60 +++++---- silent/src/middleware/middlewares/cors.rs | 12 +- silent/src/middleware/middlewares/mod.rs | 2 + .../middlewares/request_time_logger.rs | 74 +++++++++++ silent/src/middleware/mod.rs | 1 - silent/src/route/mod.rs | 79 ++++++++++-- silent/src/route/root.rs | 122 ++++-------------- silent/src/session/mod.rs | 90 +++++-------- silent/src/templates/middleware.rs | 15 ++- 13 files changed, 328 insertions(+), 226 deletions(-) create mode 100644 silent/src/core/next.rs create mode 100644 silent/src/middleware/middlewares/request_time_logger.rs diff --git a/silent/src/core/mod.rs b/silent/src/core/mod.rs index e2ca601..447e661 100644 --- a/silent/src/core/mod.rs +++ b/silent/src/core/mod.rs @@ -2,6 +2,7 @@ pub mod adapt; #[cfg(feature = "multipart")] pub(crate) mod form; +pub(crate) mod next; pub(crate) mod path_param; pub(crate) mod req_body; pub(crate) mod request; diff --git a/silent/src/core/next.rs b/silent/src/core/next.rs new file mode 100644 index 0000000..e594902 --- /dev/null +++ b/silent/src/core/next.rs @@ -0,0 +1,57 @@ +use crate::{Handler, MiddleWareHandler, Request, Response}; +use async_trait::async_trait; +use std::sync::Arc; + +/// The `Next` struct is used to chain multiple middlewares and endpoints together. +pub struct Next { + inner: NextInstance, + next: Option>, +} + +pub(crate) enum NextInstance { + Middleware(Arc), + EndPoint(Arc), +} + +impl Next { + pub(crate) fn build( + endpoint: Arc, + mut middlewares: Vec>, + ) -> Self { + let end_point = Next { + inner: NextInstance::EndPoint(endpoint), + next: None, + }; + if middlewares.is_empty() { + end_point + } else { + let next = Next { + inner: NextInstance::Middleware(middlewares.pop().unwrap()), + next: Some(Arc::new(end_point)), + }; + middlewares.into_iter().fold(next, |next, mw| Next { + inner: NextInstance::Middleware(mw), + next: Some(Arc::new(next)), + }) + } + } +} + +#[async_trait] +impl Handler for Next { + async fn call(&self, req: Request) -> crate::Result { + match &self.inner { + NextInstance::Middleware(mw) => { + mw.handle(req, self.next.clone().unwrap().as_ref()).await + } + NextInstance::EndPoint(ep) => ep.call(req).await, + } + } +} + +#[async_trait] +impl MiddleWareHandler for Next { + async fn handle(&self, req: Request, next: &Next) -> crate::Result { + next.call(req).await + } +} diff --git a/silent/src/grpc/handler.rs b/silent/src/grpc/handler.rs index 5ed727d..ad6f145 100644 --- a/silent/src/grpc/handler.rs +++ b/silent/src/grpc/handler.rs @@ -1,7 +1,12 @@ use std::sync::Arc; +use super::utils::merge_grpc_response; +use crate::grpc::service::GrpcService; +use crate::prelude::HandlerGetter; +use crate::route::Route; +use crate::{Handler, Response, SilentError}; use async_trait::async_trait; -use http::{header, HeaderValue, StatusCode}; +use http::{header, HeaderValue, Method, StatusCode}; use http_body_util::BodyExt; use hyper::upgrade::OnUpgrade; use hyper_util::rt::TokioExecutor; @@ -10,11 +15,7 @@ use tonic::body::BoxBody; use tonic::codegen::Service; use tonic::server::NamedService; use tonic::Status; - -use crate::grpc::service::GrpcService; -use crate::{Handler, Response, SilentError}; - -use super::utils::merge_grpc_response; +use tracing::{error, info}; trait GrpcRequestAdapter { fn into_grpc_request(self) -> http::Request; @@ -54,6 +55,20 @@ where pub fn path(&self) -> &str { S::NAME } + + pub fn service(self) -> Route { + let path = self.path().to_string(); + let handler = Arc::new(self); + Route::new(path.as_str()).append( + Route::new("") + .insert_handler(Method::POST, handler.clone()) + .insert_handler(Method::GET, handler), + ) + } + + pub fn register(self, route: &mut Route) { + route.extend(self.service()); + } } impl From for GrpcHandler @@ -86,7 +101,7 @@ where tokio::spawn(async move { let conn = on_upgrade.await; if conn.is_err() { - eprintln!("upgrade error: {:?}", conn.err()); + error!("upgrade error: {:?}", conn.err()); return; } let upgraded_io = conn.unwrap(); @@ -96,8 +111,8 @@ where .serve_connection(upgraded_io, GrpcService::new(handler)) .await { - Ok(_) => eprintln!("finished gracefully"), - Err(err) => println!("ERROR: {err}"), + Ok(_) => info!("finished gracefully"), + Err(err) => error!("ERROR: {err}"), } }); let mut res = Response::empty(); @@ -116,7 +131,6 @@ where format!("grpc call failed: {}", e.into()), ) })?; - println!("{:?}", grpc_res); let mut res = Response::empty(); merge_grpc_response(&mut res, grpc_res).await; diff --git a/silent/src/lib.rs b/silent/src/lib.rs index f36727b..4f999a6 100644 --- a/silent/src/lib.rs +++ b/silent/src/lib.rs @@ -31,8 +31,8 @@ mod ws; use multer; pub use crate::configs::Configs; -pub use crate::core::{request::Request, response::Response}; -pub use crate::middleware::{middlewares, MiddleWareHandler, MiddlewareResult}; +pub use crate::core::{next::Next, request::Request, response::Response}; +pub use crate::middleware::{middlewares, MiddleWareHandler}; pub use error::SilentError; pub use error::SilentResult as Result; pub use handler::Handler; @@ -47,7 +47,7 @@ pub mod prelude { #[cfg(feature = "multipart")] pub use crate::core::form::{FilePart, FormData}; pub use crate::core::{ - path_param::PathParam, req_body::ReqBody, request::Request, res_body::full, + next::Next, path_param::PathParam, req_body::ReqBody, request::Request, res_body::full, res_body::stream_body, res_body::ResBody, response::Response, }; pub use crate::error::{SilentError, SilentResult as Result}; @@ -57,7 +57,6 @@ pub mod prelude { pub use crate::handler::HandlerWrapper; pub use crate::log::*; pub use crate::middleware::MiddleWareHandler; - pub use crate::middleware::MiddlewareResult; #[cfg(feature = "upgrade")] pub use crate::route::handler_append::WSHandlerAppend; pub use crate::route::handler_append::{HandlerAppend, HandlerGetter}; diff --git a/silent/src/middleware/middleware_trait.rs b/silent/src/middleware/middleware_trait.rs index 7b29d8f..403083f 100644 --- a/silent/src/middleware/middleware_trait.rs +++ b/silent/src/middleware/middleware_trait.rs @@ -1,54 +1,52 @@ -use crate::{Request, Response, Result, SilentError}; +use crate::core::next::Next; +use crate::{Request, Response, Result}; use async_trait::async_trait; -pub enum MiddlewareResult { - Continue, - Break(Response), - Error(SilentError), -} - #[async_trait] pub trait MiddleWareHandler: Send + Sync + 'static { async fn match_req(&self, _req: &Request) -> bool { true } - async fn pre_request( - &self, - _req: &mut Request, - _res: &mut Response, - ) -> Result { - Ok(MiddlewareResult::Continue) - } - async fn after_response(&self, _res: &mut Response) -> Result { - Ok(MiddlewareResult::Continue) - } + async fn handle(&self, _req: Request, _next: &Next) -> Result; } #[cfg(test)] mod tests { + use crate::{Handler, HandlerWrapper}; + use std::sync::Arc; + use tracing::info; + use super::*; - use hyper::Uri; - struct MiddleWare {} + struct TestMiddleWare { + count: u32, + } #[async_trait] - impl MiddleWareHandler for MiddleWare { - async fn match_req(&self, req: &Request) -> bool { - req.uri().path().ends_with("hello") + impl MiddleWareHandler for TestMiddleWare { + async fn handle(&self, req: Request, next: &Next) -> Result { + info!("{}", self.count); + next.call(req).await } } + async fn hello_world(_req: Request) -> Result { + Ok("Hello World".into()) + } + #[tokio::test] async fn test_middleware() -> Result<()> { - let middleware = MiddleWare {}; - let mut req = Request::empty(); - let mut res = Response::empty(); - *req.uri_mut() = "/hello".parse::().unwrap(); - assert!(middleware.match_req(&req).await); - *req.uri_mut() = "/hell".parse::().unwrap(); - assert!(!middleware.match_req(&req).await); - assert!(middleware.pre_request(&mut req, &mut res).await.is_ok()); - assert!(middleware.after_response(&mut res).await.is_ok()); + let handler_wrapper = HandlerWrapper::new(hello_world).arc(); + let middleware1 = TestMiddleWare { count: 1 }; + let middleware2 = TestMiddleWare { count: 2 }; + let req = Request::empty(); + let middlewares = Next::build( + handler_wrapper, + vec![Arc::new(middleware1), Arc::new(middleware2)], + ); + let res = middlewares.call(req).await; + assert!(res.is_ok()); + info!("{:?}", res.unwrap()); Ok(()) } } diff --git a/silent/src/middleware/middlewares/cors.rs b/silent/src/middleware/middlewares/cors.rs index a3c81fe..bb62b1f 100644 --- a/silent/src/middleware/middlewares/cors.rs +++ b/silent/src/middleware/middlewares/cors.rs @@ -1,4 +1,4 @@ -use crate::{MiddleWareHandler, MiddlewareResult, Request, Response, Result, SilentError}; +use crate::{Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError}; use async_trait::async_trait; use http::{header, Method}; @@ -151,12 +151,13 @@ impl Cors { #[async_trait] impl MiddleWareHandler for Cors { - async fn pre_request(&self, req: &mut Request, res: &mut Response) -> Result { + async fn handle(&self, req: Request, next: &Next) -> Result { let req_origin = req .headers() .get("origin") .map_or("", |v| v.to_str().unwrap_or("")) .to_string(); + let mut res = Response::empty(); if let Some(ref origin) = self.origin { let origin = origin.get_value(&req_origin); if origin.is_empty() { @@ -231,10 +232,9 @@ impl MiddleWareHandler for Cors { ); } if req.method() == Method::OPTIONS { - let mut option_res = Response::empty(); - option_res.headers = res.headers().clone(); - return Ok(MiddlewareResult::Break(option_res)); + return Ok(res); } - Ok(MiddlewareResult::Continue) + res.copy_from_response(next.call(req).await?); + Ok(res) } } diff --git a/silent/src/middleware/middlewares/mod.rs b/silent/src/middleware/middlewares/mod.rs index 9d1ac49..ba2ab3d 100644 --- a/silent/src/middleware/middlewares/mod.rs +++ b/silent/src/middleware/middlewares/mod.rs @@ -1,3 +1,5 @@ mod cors; +mod request_time_logger; pub use cors::{Cors, CorsType}; +pub use request_time_logger::RequestTimeLogger; diff --git a/silent/src/middleware/middlewares/request_time_logger.rs b/silent/src/middleware/middlewares/request_time_logger.rs new file mode 100644 index 0000000..b179057 --- /dev/null +++ b/silent/src/middleware/middlewares/request_time_logger.rs @@ -0,0 +1,74 @@ +use crate::{Handler, MiddleWareHandler, Next, Request, Response, Result}; +use async_trait::async_trait; +use chrono::Utc; + +/// ExceptionHandler 中间件 +/// ```rust +/// use silent::prelude::*; +/// use silent::middlewares::{RequestTimeLogger}; +/// // Define a request time logger middleware +/// let _ = RequestTimeLogger::new(); +#[derive(Default, Clone)] +pub struct RequestTimeLogger; + +impl RequestTimeLogger { + pub fn new() -> Self { + Self {} + } +} + +#[async_trait] +impl MiddleWareHandler for RequestTimeLogger { + async fn handle(&self, req: Request, next: &Next) -> Result { + let method = req.method().clone(); + let url = req.uri().to_string().clone(); + let http_version = req.version(); + let peer_addr = req.remote(); + let start_time = Utc::now().time(); + let res = next.call(req).await; + let end_time = Utc::now().time(); + let req_time = end_time - start_time; + Ok(match res { + Ok(res) => { + if res.status.as_u16() >= 400 { + tracing::info!( + "{} {} {} {:?} {} {:?} {}", + peer_addr, + method, + url, + http_version, + res.status.as_u16(), + res.content_length().lower(), + req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0 + ); + } else { + tracing::warn!( + "{} {} {} {:?} {} {:?} {}", + peer_addr, + method, + url, + http_version, + res.status.as_u16(), + res.content_length().lower(), + req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0 + ); + } + res + } + Err(e) => { + tracing::error!( + "{} {} {} {:?} {} {:?} {} {}", + peer_addr, + method, + url, + http_version, + e.status().as_u16(), + 0, + req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0, + e.to_string() + ); + e.into() + } + }) + } +} diff --git a/silent/src/middleware/mod.rs b/silent/src/middleware/mod.rs index 8f0f33b..7999e11 100644 --- a/silent/src/middleware/mod.rs +++ b/silent/src/middleware/mod.rs @@ -2,4 +2,3 @@ pub mod middleware_trait; pub mod middlewares; pub use middleware_trait::MiddleWareHandler; -pub use middleware_trait::MiddlewareResult; diff --git a/silent/src/route/mod.rs b/silent/src/route/mod.rs index a282220..1c024b5 100644 --- a/silent/src/route/mod.rs +++ b/silent/src/route/mod.rs @@ -1,23 +1,26 @@ +#[cfg(feature = "session")] +use async_session::Session; +use async_trait::async_trait; +use http::StatusCode; +pub use root::RootRoute; +pub use route_service::RouteService; +use std::collections::HashMap; +use std::fmt; +use std::sync::Arc; + #[cfg(feature = "static")] use crate::handler::static_handler; use crate::handler::Handler; use crate::middleware::MiddleWareHandler; -use crate::Method; -use std::collections::HashMap; -use std::fmt; -use std::sync::Arc; +#[cfg(feature = "static")] +use crate::prelude::HandlerGetter; +use crate::{Method, Next, Request, Response, SilentError}; pub(crate) mod handler_append; mod handler_match; mod root; mod route_service; -pub use root::RootRoute; - -#[cfg(feature = "static")] -use crate::prelude::HandlerGetter; -pub use route_service::RouteService; - pub trait RouterAdapt { fn into_router(self) -> Route; } @@ -122,6 +125,16 @@ impl Route { real_route.children.push(route); self } + pub fn extend(&mut self, route: R) { + let mut route = route.into_router(); + self.root_middlewares.extend(route.root_middlewares.clone()); + self.middlewares + .iter() + .cloned() + .for_each(|m| route.middleware_hook(m.clone())); + let real_route = self.get_append_real_route(&self.create_path.clone()); + real_route.children.push(route); + } pub fn root_hook(mut self, handler: impl MiddleWareHandler + 'static) -> Self { self.root_middlewares.push(Arc::new(handler)); self @@ -164,14 +177,56 @@ impl Route { } } +#[async_trait] +impl Handler for Route { + async fn call(&self, req: Request) -> crate::error::SilentResult { + let configs = req.configs(); + match self.handler.get(req.method()) { + None => Err(SilentError::business_error( + StatusCode::METHOD_NOT_ALLOWED, + "method not allowed".to_string(), + )), + Some(handler) => { + let mut pre_res = Response::empty(); + pre_res.configs = configs; + #[cfg(feature = "cookie")] + { + *pre_res.cookies_mut() = req.cookies().clone(); + } + #[cfg(feature = "session")] + let session = req.extensions().get::().cloned(); + #[cfg(feature = "session")] + if let Some(session) = session { + pre_res.extensions.insert(session); + } + let mut active_middlewares = vec![]; + for middleware in self.middlewares.iter().cloned() { + if middleware.match_req(&req).await { + active_middlewares.push(middleware); + } + } + let next = Next::build(handler.clone(), active_middlewares); + pre_res.copy_from_response(next.call(req).await?); + Ok(pre_res) + } + } + } +} + #[cfg(test)] mod tests { + use crate::{Next, Request, Response}; + use super::*; #[derive(Clone, Eq, PartialEq)] struct MiddlewareTest; - - impl MiddleWareHandler for MiddlewareTest {} + #[async_trait::async_trait] + impl MiddleWareHandler for MiddlewareTest { + async fn handle(&self, req: Request, next: &Next) -> crate::error::SilentResult { + next.call(req).await + } + } #[test] fn middleware_tree_test() { diff --git a/silent/src/route/root.rs b/silent/src/route/root.rs index 1eb6271..46fc58a 100644 --- a/silent/src/route/root.rs +++ b/silent/src/route/root.rs @@ -11,17 +11,15 @@ use crate::session::SessionMiddleware; use crate::templates::TemplateMiddleware; #[cfg(feature = "scheduler")] use crate::Scheduler; -use crate::{ - Configs, MiddleWareHandler, MiddlewareResult, Request, Response, SilentError, StatusCode, -}; +use crate::{Configs, Handler, MiddleWareHandler, Next, Request, Response, SilentError}; #[cfg(feature = "session")] -use async_session::{Session, SessionStore}; +use async_session::SessionStore; +use async_trait::async_trait; use chrono::Utc; #[cfg(feature = "grpc")] use http::Method; use std::fmt; use std::future::Future; -use std::net::SocketAddr; use std::sync::Arc; #[cfg(feature = "scheduler")] use tokio::sync::Mutex; @@ -103,7 +101,6 @@ impl RootRoute { } pub fn push(&mut self, route: Route) { - self.middlewares.clone_from(&route.middlewares); self.middlewares.extend(route.root_middlewares.clone()); self.children.push(route); } @@ -138,109 +135,38 @@ impl RootRoute { } } -impl RootRoute { - async fn handle_request( - &self, - mut req: Request, - path: String, - ) -> Result { - let configs = self.configs.clone().unwrap_or_default(); - let mut pre_res = Response::empty(); - let mut root_middlewares = vec![]; - for middleware in self.middlewares.iter() { - if middleware.match_req(&req).await { - root_middlewares.push(middleware); - } - } - for middleware in root_middlewares.clone() { - match middleware.pre_request(&mut req, &mut pre_res).await? { - MiddlewareResult::Continue => {} - MiddlewareResult::Break(res) => return Ok(res), - MiddlewareResult::Error(err) => return Err(err), - } - } - println!("path: {}", path); - match self.handler_match(&mut req, path.as_str()) { - RouteMatched::Matched(route) => match route.handler.get(req.method()) { - None => { - return Err(SilentError::business_error( - StatusCode::METHOD_NOT_ALLOWED, - "method not allowed".to_string(), - )) - } - Some(handler) => { - pre_res.configs = configs.clone(); - let mut active_middlewares = vec![]; - for middleware in route.middlewares.iter() { - if middleware.match_req(&req).await { - active_middlewares.push(middleware); - } - } - for middleware in active_middlewares.clone() { - match middleware.pre_request(&mut req, &mut pre_res).await? { - MiddlewareResult::Continue => {} - MiddlewareResult::Break(res) => return Ok(res), - MiddlewareResult::Error(err) => return Err(err), - } - } - #[cfg(feature = "cookie")] - { - *pre_res.cookies_mut() = req.cookies().clone(); - } - #[cfg(feature = "session")] - let session = req.extensions().get::().cloned(); - #[cfg(feature = "session")] - if let Some(session) = session { - pre_res.extensions.insert(session); - } - pre_res.copy_from_response(handler.call(req).await?); - active_middlewares.reverse(); - for middleware in active_middlewares { - match middleware.after_response(&mut pre_res).await? { - MiddlewareResult::Continue => {} - MiddlewareResult::Break(res) => return Ok(res), - MiddlewareResult::Error(err) => return Err(err), - } - } - } - }, - RouteMatched::Unmatched => { - return Err(SilentError::business_error( - StatusCode::NOT_FOUND, - "Server not found".to_string(), - )) - } - }; - root_middlewares.reverse(); - for middleware in root_middlewares { - match middleware.after_response(&mut pre_res).await? { - MiddlewareResult::Continue => {} - MiddlewareResult::Break(res) => return Ok(res), - MiddlewareResult::Error(err) => return Err(err), - } - } - Ok(pre_res) - } - pub async fn handle(&self, req: Request, peer_addr: SocketAddr) -> Response { +#[async_trait] +impl Handler for RootRoute { + async fn call(&self, mut req: Request) -> Result { tracing::debug!("{:?}", req); let exception_handler = self.exception_handler.clone(); - let (mut req, path) = req.split_url(); let configs = self.configs.clone().unwrap_or_default(); req.configs = configs.clone(); - if req.headers().get("x-real-ip").is_none() { - req.headers_mut() - .insert("x-real-ip", peer_addr.ip().to_string().parse().unwrap()); - } let method = req.method().clone(); let url = req.uri().to_string().clone(); let http_version = req.version(); + let peer_addr = req.remote(); let start_time = Utc::now().time(); #[cfg(feature = "scheduler")] req.extensions_mut().insert(self.scheduler.clone()); - let res = self.handle_request(req, path).await; + + let mut root_middlewares = vec![]; + for middleware in self.middlewares.iter().cloned() { + if middleware.match_req(&req).await { + root_middlewares.push(middleware); + } + } + let (mut req, path) = req.split_url(); + let res = match self.handler_match(&mut req, path.as_str()) { + RouteMatched::Matched(route) => { + let next = Next::build(Arc::new(route), root_middlewares); + next.call(req).await + } + RouteMatched::Unmatched => Err(SilentError::NotFound), + }; let end_time = Utc::now().time(); let req_time = end_time - start_time; - match res { + Ok(match res { Ok(res) => { tracing::info!( "{} {} {} {:?} {} {:?} {}", @@ -271,7 +197,7 @@ impl RootRoute { None => e.into(), } } - } + }) } } diff --git a/silent/src/session/mod.rs b/silent/src/session/mod.rs index 3500aca..e6ca309 100644 --- a/silent/src/session/mod.rs +++ b/silent/src/session/mod.rs @@ -1,6 +1,4 @@ -use crate::{ - MiddleWareHandler, MiddlewareResult, Request, Response, Result, SilentError, StatusCode, -}; +use crate::{Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError, StatusCode}; use async_session::{MemoryStore, Session, SessionStore}; use async_trait::async_trait; use cookie::Cookie; @@ -36,64 +34,40 @@ impl MiddleWareHandler for SessionMiddleware where T: SessionStore, { - async fn pre_request( - &self, - req: &mut Request, - _res: &mut Response, - ) -> Result { + async fn handle(&self, mut req: Request, next: &Next) -> Result { let cookies = req.cookies().clone(); let cookie = cookies.get("silent-web-session"); - if cookie.is_none() { - req.extensions_mut().insert(Session::new()); - return Ok(MiddlewareResult::Continue); - } - let cookie = cookie.unwrap(); - let session = self - .session_store - .read() - .await - .load_session(cookie.value().to_string()) - .await - .map_err(|e| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Failed to load session: {}", e), - ) - })?; - if let Some(session) = session { - req.extensions_mut().insert(session); - } else { - req.extensions_mut().insert(Session::new()); - } - Ok(MiddlewareResult::Continue) - } - async fn after_response(&self, res: &mut Response) -> Result { let session_store = self.session_store.read().await; - let session = res.extensions.remove::(); - let cookie = res.cookies().get("silent-web-session"); - if let Some(mut session) = session { - if let Some(cookie) = cookie { - if let Ok(session_id) = Session::id_from_cookie_value(cookie.value()) { - if session.id() != session_id { - session.regenerate() - } - } - } else { - session.regenerate() - } - let cookie_value = session_store.store_session(session).await.map_err(|e| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Failed to store session: {}", e), - ) - })?; - if let Some(cookie_value) = cookie_value { - res.cookies_mut().add( - Cookie::build(("silent-web-session", cookie_value)) - .max_age(cookie::time::Duration::hours(2)), - ); - } + let session = if cookie.is_none() { + Session::new() + } else { + let cookie = cookie.unwrap(); + session_store + .load_session(cookie.value().to_string()) + .await + .map_err(|e| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Failed to load session: {}", e), + ) + })? + .unwrap_or_default() + }; + req.extensions_mut().insert(session.clone()); + let mut res = next.call(req).await?; + res.extensions_mut().insert(session.clone()); + let cookie_value = session_store.store_session(session).await.map_err(|e| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Failed to store session: {}", e), + ) + })?; + if let Some(cookie_value) = cookie_value { + res.cookies_mut().add( + Cookie::build(("silent-web-session", cookie_value)) + .max_age(cookie::time::Duration::hours(2)), + ); } - Ok(MiddlewareResult::Continue) + Ok(res) } } diff --git a/silent/src/templates/middleware.rs b/silent/src/templates/middleware.rs index 40daff2..fea7804 100644 --- a/silent/src/templates/middleware.rs +++ b/silent/src/templates/middleware.rs @@ -1,4 +1,4 @@ -use crate::{MiddleWareHandler, MiddlewareResult, Response, Result, SilentError, StatusCode}; +use crate::{Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError, StatusCode}; use async_trait::async_trait; use serde::Serialize; use serde_json::Value; @@ -41,7 +41,8 @@ impl TemplateMiddleware { #[async_trait] impl MiddleWareHandler for TemplateMiddleware { - async fn after_response(&self, res: &mut Response) -> Result { + async fn handle(&self, req: Request, next: &Next) -> Result { + let mut res = next.call(req).await?; let template = res.extensions.get::().unwrap(); res.set_body( self.template @@ -63,7 +64,7 @@ impl MiddleWareHandler for TemplateMiddleware { .into(), ); res.set_typed_header(headers::ContentType::html()); - Ok(MiddlewareResult::Continue) + Ok(res) } } @@ -72,7 +73,7 @@ mod tests { use super::*; use crate::prelude::{HandlerAppend, Route}; use crate::route::RootRoute; - use crate::Request; + use crate::{Handler, Request}; use bytes::Bytes; use http_body_util::BodyExt; @@ -99,11 +100,13 @@ mod tests { .hook(temp_middleware); let mut routes = RootRoute::new(); routes.push(route); - let req = Request::empty(); + let mut req = Request::empty(); + req.set_remote("127.0.0.1:8080".parse().unwrap()); assert_eq!( routes - .handle(req, "127.0.0.1:8000".parse().unwrap()) + .call(req) .await + .unwrap() .body .frame() .await From fd991e001f420b71dbfff55cf8d93afc0ca5d1ec Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Wed, 7 Aug 2024 09:56:27 +0800 Subject: [PATCH 26/49] =?UTF-8?q?refactor:=20next=E9=87=8D=E6=9E=84,=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9Erequest=5Ftime=5Flogger=E4=B8=AD=E9=97=B4?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/exception_handler/src/main.rs | 8 +- silent/src/error/exception_handler_trait.rs | 7 -- silent/src/error/exception_handler_wrapper.rs | 75 ----------------- silent/src/error/mod.rs | 5 -- .../middlewares/request_time_logger.rs | 4 +- silent/src/route/handler_match.rs | 1 + silent/src/route/root.rs | 83 +++++-------------- 7 files changed, 27 insertions(+), 156 deletions(-) delete mode 100644 silent/src/error/exception_handler_trait.rs delete mode 100644 silent/src/error/exception_handler_wrapper.rs diff --git a/examples/exception_handler/src/main.rs b/examples/exception_handler/src/main.rs index 181ef18..98e73b1 100644 --- a/examples/exception_handler/src/main.rs +++ b/examples/exception_handler/src/main.rs @@ -11,12 +11,6 @@ fn main() { logger::fmt().with_max_level(Level::INFO).init(); let route = Route::new("") .get(|mut req| async move { req.params_parse::() }) - .route() - .set_exception_handler(|e, _| async move { - Exception { - code: e.status().as_u16(), - msg: e.to_string(), - } - }); + .route(); Server::new().run(route); } diff --git a/silent/src/error/exception_handler_trait.rs b/silent/src/error/exception_handler_trait.rs deleted file mode 100644 index a1ebde3..0000000 --- a/silent/src/error/exception_handler_trait.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::{Configs, Response, SilentError}; -use async_trait::async_trait; - -#[async_trait] -pub trait ExceptionHandler: Send + Sync + 'static { - async fn call(&self, err: SilentError, configs: Configs) -> Response; -} diff --git a/silent/src/error/exception_handler_wrapper.rs b/silent/src/error/exception_handler_wrapper.rs deleted file mode 100644 index 8d4236d..0000000 --- a/silent/src/error/exception_handler_wrapper.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::error::exception_handler_trait::ExceptionHandler; -use crate::{Configs, Response, SilentError}; -use async_trait::async_trait; -use std::future::Future; -use std::sync::Arc; - -/// 处理器包装结构体 -/// 包含 -/// 请求类型: Option -/// 请求方法: Handler -/// 其中请求类型可为空,用来定义中间件 -/// 请求方法不可为空,用来定义处理器 -/// 处理器为返回值为 Into + From的异步函数或者闭包函数 -pub struct ExceptionHandlerWrapper { - handler: F, -} - -#[allow(dead_code)] -impl ExceptionHandlerWrapper -where - Fut: Future + Send + 'static, - F: Fn(SilentError, Configs) -> Fut, - T: Into, -{ - pub fn new(handler: F) -> Self { - Self { handler } - } - - pub async fn handle(&self, err: SilentError, configs: Configs) -> T { - (self.handler)(err, configs).await - } - - pub fn arc(self) -> Arc { - Arc::new(self) - } -} - -/// 为HandlerWrapper实现Handler -#[async_trait] -impl ExceptionHandler for ExceptionHandlerWrapper -where - Fut: Future + Send + 'static, - F: Fn(SilentError, Configs) -> Fut + Send + Sync + 'static, - T: Into, -{ - async fn call(&self, err: SilentError, configs: Configs) -> Response { - self.handle(err, configs).await.into() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::StatusCode; - - async fn exception_handler(err: SilentError, _configs: Configs) -> SilentError { - err - } - - #[tokio::test] - async fn handler_wrapper_match_req_works() { - let handler_wrapper = ExceptionHandlerWrapper::new(exception_handler); - let configs = Configs::default(); - assert_eq!( - handler_wrapper - .call( - SilentError::business_error(StatusCode::OK, "".to_string()), - configs - ) - .await - .status, - StatusCode::OK - ); - } -} diff --git a/silent/src/error/mod.rs b/silent/src/error/mod.rs index ca5fc60..23aabb0 100644 --- a/silent/src/error/mod.rs +++ b/silent/src/error/mod.rs @@ -1,10 +1,5 @@ -mod exception_handler_trait; -mod exception_handler_wrapper; - use crate::headers::ContentType; use crate::{Response, StatusCode}; -pub(crate) use exception_handler_trait::ExceptionHandler; -pub(crate) use exception_handler_wrapper::ExceptionHandlerWrapper; use serde::Serialize; use serde_json::Value; use std::backtrace::Backtrace; diff --git a/silent/src/middleware/middlewares/request_time_logger.rs b/silent/src/middleware/middlewares/request_time_logger.rs index b179057..d158ece 100644 --- a/silent/src/middleware/middlewares/request_time_logger.rs +++ b/silent/src/middleware/middlewares/request_time_logger.rs @@ -31,7 +31,7 @@ impl MiddleWareHandler for RequestTimeLogger { Ok(match res { Ok(res) => { if res.status.as_u16() >= 400 { - tracing::info!( + tracing::warn!( "{} {} {} {:?} {} {:?} {}", peer_addr, method, @@ -42,7 +42,7 @@ impl MiddleWareHandler for RequestTimeLogger { req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0 ); } else { - tracing::warn!( + tracing::info!( "{} {} {} {:?} {} {:?} {}", peer_addr, method, diff --git a/silent/src/route/handler_match.rs b/silent/src/route/handler_match.rs index cb3a452..be25a06 100644 --- a/silent/src/route/handler_match.rs +++ b/silent/src/route/handler_match.rs @@ -2,6 +2,7 @@ use super::{RootRoute, Route}; use crate::core::path_param::PathParam; use crate::Request; +#[derive(Debug, Clone)] pub(crate) enum RouteMatched { Matched(Route), Unmatched, diff --git a/silent/src/route/root.rs b/silent/src/route/root.rs index 46fc58a..69b23d4 100644 --- a/silent/src/route/root.rs +++ b/silent/src/route/root.rs @@ -1,6 +1,6 @@ -use crate::error::{ExceptionHandler, ExceptionHandlerWrapper}; #[cfg(feature = "grpc")] use crate::grpc::GrpcHandler; +use crate::middlewares::RequestTimeLogger; #[cfg(feature = "grpc")] use crate::prelude::HandlerGetter; use crate::route::handler_match::{Match, RouteMatched}; @@ -15,11 +15,9 @@ use crate::{Configs, Handler, MiddleWareHandler, Next, Request, Response, Silent #[cfg(feature = "session")] use async_session::SessionStore; use async_trait::async_trait; -use chrono::Utc; #[cfg(feature = "grpc")] use http::Method; use std::fmt; -use std::future::Future; use std::sync::Arc; #[cfg(feature = "scheduler")] use tokio::sync::Mutex; @@ -34,7 +32,6 @@ use tonic::server::NamedService; pub struct RootRoute { pub(crate) children: Vec, pub(crate) middlewares: Vec>, - pub(crate) exception_handler: Option>, #[cfg(feature = "session")] pub(crate) session_set: bool, pub(crate) configs: Option, @@ -59,7 +56,6 @@ impl RootRoute { Self { children: vec![], middlewares: vec![], - exception_handler: None, #[cfg(feature = "session")] session_set: false, configs: None, @@ -121,32 +117,34 @@ impl RootRoute { .for_each(|r| r.middleware_hook_first(handler.clone())); } - pub fn set_exception_handler(mut self, handler: F) -> Self - where - Fut: Future + Send + 'static, - F: Fn(SilentError, Configs) -> Fut + Send + Sync + 'static, - T: Into, - { - self.exception_handler = Some(ExceptionHandlerWrapper::new(handler).arc()); - self - } pub(crate) fn set_configs(&mut self, configs: Option) { self.configs = configs; } } +struct RootHandler { + inner: RouteMatched, + middlewares: Vec>, +} +#[async_trait] +impl Handler for RootHandler { + async fn call(&self, req: Request) -> Result { + match self.inner.clone() { + RouteMatched::Matched(route) => { + let next = Next::build(Arc::new(route), self.middlewares.clone()); + next.call(req).await + } + RouteMatched::Unmatched => Err(SilentError::NotFound), + } + } +} + #[async_trait] impl Handler for RootRoute { async fn call(&self, mut req: Request) -> Result { tracing::debug!("{:?}", req); - let exception_handler = self.exception_handler.clone(); let configs = self.configs.clone().unwrap_or_default(); req.configs = configs.clone(); - let method = req.method().clone(); - let url = req.uri().to_string().clone(); - let http_version = req.version(); - let peer_addr = req.remote(); - let start_time = Utc::now().time(); #[cfg(feature = "scheduler")] req.extensions_mut().insert(self.scheduler.clone()); @@ -157,47 +155,12 @@ impl Handler for RootRoute { } } let (mut req, path) = req.split_url(); - let res = match self.handler_match(&mut req, path.as_str()) { - RouteMatched::Matched(route) => { - let next = Next::build(Arc::new(route), root_middlewares); - next.call(req).await - } - RouteMatched::Unmatched => Err(SilentError::NotFound), + let handler = RootHandler { + inner: self.handler_match(&mut req, &path), + middlewares: root_middlewares, }; - let end_time = Utc::now().time(); - let req_time = end_time - start_time; - Ok(match res { - Ok(res) => { - tracing::info!( - "{} {} {} {:?} {} {:?} {}", - peer_addr, - method, - url, - http_version, - res.status.as_u16(), - res.content_length().lower(), - req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0 - ); - res - } - Err(e) => { - tracing::error!( - "{} {} {} {:?} {} {:?} {} {}", - peer_addr, - method, - url, - http_version, - e.status().as_u16(), - 0, - req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0, - e.to_string() - ); - match exception_handler { - Some(handler) => handler.call(e, configs.clone()).await, - None => e.into(), - } - } - }) + let next = Next::build(Arc::new(handler), vec![Arc::new(RequestTimeLogger::new())]); + next.call(req).await } } From 25a7097bc4abc745980b1c14bfe67732914f1139 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Wed, 7 Aug 2024 08:32:36 +0800 Subject: [PATCH 27/49] =?UTF-8?q?refactor:=20exception=5Fhandler=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/exception_handler/src/main.rs | 3 ++ .../middlewares/exception_handler.rs | 46 +++++++++++++++++++ silent/src/middleware/middlewares/mod.rs | 2 + 3 files changed, 51 insertions(+) create mode 100644 silent/src/middleware/middlewares/exception_handler.rs diff --git a/examples/exception_handler/src/main.rs b/examples/exception_handler/src/main.rs index 98e73b1..4eb9698 100644 --- a/examples/exception_handler/src/main.rs +++ b/examples/exception_handler/src/main.rs @@ -1,4 +1,6 @@ use serde::{Deserialize, Serialize}; + +use silent::middlewares::ExceptionHandler; use silent::prelude::*; #[derive(Deserialize, Serialize, Debug)] @@ -10,6 +12,7 @@ struct Exception { fn main() { logger::fmt().with_max_level(Level::INFO).init(); let route = Route::new("") + .root_hook(ExceptionHandler::new(|res, _| async move { res })) .get(|mut req| async move { req.params_parse::() }) .route(); Server::new().run(route); diff --git a/silent/src/middleware/middlewares/exception_handler.rs b/silent/src/middleware/middlewares/exception_handler.rs new file mode 100644 index 0000000..bbf6e6f --- /dev/null +++ b/silent/src/middleware/middlewares/exception_handler.rs @@ -0,0 +1,46 @@ +use std::future::Future; +use std::sync::Arc; + +use async_trait::async_trait; + +use crate::middleware::middleware_trait::Next; +use crate::{Configs, MiddleWareHandler, Request, Response, Result}; + +/// ExceptionHandler 中间件 +/// ```rust +/// use silent::prelude::*; +/// use silent::middlewares::{ExceptionHandler}; +/// // Define a custom error handler function +/// let _ = ExceptionHandler::new(|res, _configs| async {res}); +#[derive(Default, Clone)] +pub struct ExceptionHandler { + handler: Arc, +} + +impl ExceptionHandler +where + Fut: Future> + Send + 'static, + F: Fn(Result, Configs) -> Fut + Send + Sync + 'static, + T: Into, +{ + pub fn new(handler: F) -> Self { + Self { + handler: Arc::new(handler), + } + } +} + +#[async_trait] +impl MiddleWareHandler for ExceptionHandler +where + Fut: Future> + Send + 'static, + F: Fn(Result, Configs) -> Fut + Send + Sync + 'static, + T: Into, +{ + async fn handle(&self, req: Request, next: &Next) -> Result { + let configs = req.configs(); + self.handler.clone()(next.call(req).await, configs) + .await + .map(|r| r.into()) + } +} diff --git a/silent/src/middleware/middlewares/mod.rs b/silent/src/middleware/middlewares/mod.rs index ba2ab3d..3a9c052 100644 --- a/silent/src/middleware/middlewares/mod.rs +++ b/silent/src/middleware/middlewares/mod.rs @@ -1,5 +1,7 @@ mod cors; +mod exception_handler; mod request_time_logger; pub use cors::{Cors, CorsType}; +pub use exception_handler::ExceptionHandler; pub use request_time_logger::RequestTimeLogger; From 4bcbc02a33a2ce284c5e98930497e8a35fe189b9 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Wed, 7 Aug 2024 16:58:14 +0800 Subject: [PATCH 28/49] =?UTF-8?q?exception=5Fhandler=20=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- silent/src/middleware/middlewares/exception_handler.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/silent/src/middleware/middlewares/exception_handler.rs b/silent/src/middleware/middlewares/exception_handler.rs index bbf6e6f..94c5440 100644 --- a/silent/src/middleware/middlewares/exception_handler.rs +++ b/silent/src/middleware/middlewares/exception_handler.rs @@ -3,8 +3,7 @@ use std::sync::Arc; use async_trait::async_trait; -use crate::middleware::middleware_trait::Next; -use crate::{Configs, MiddleWareHandler, Request, Response, Result}; +use crate::{Configs, Handler, MiddleWareHandler, Next, Request, Response, Result}; /// ExceptionHandler 中间件 /// ```rust From 4c077c000a39b335f626bdaeffaa81f421558720 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Wed, 7 Aug 2024 08:32:36 +0800 Subject: [PATCH 29/49] =?UTF-8?q?refactor:=20exception=5Fhandler=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../middlewares/exception_handler.rs | 3 +- silent/src/middleware/middlewares/mod.rs | 2 - silent/src/route/root.rs | 70 ++++++++++++------- 3 files changed, 47 insertions(+), 28 deletions(-) diff --git a/silent/src/middleware/middlewares/exception_handler.rs b/silent/src/middleware/middlewares/exception_handler.rs index 94c5440..bbf6e6f 100644 --- a/silent/src/middleware/middlewares/exception_handler.rs +++ b/silent/src/middleware/middlewares/exception_handler.rs @@ -3,7 +3,8 @@ use std::sync::Arc; use async_trait::async_trait; -use crate::{Configs, Handler, MiddleWareHandler, Next, Request, Response, Result}; +use crate::middleware::middleware_trait::Next; +use crate::{Configs, MiddleWareHandler, Request, Response, Result}; /// ExceptionHandler 中间件 /// ```rust diff --git a/silent/src/middleware/middlewares/mod.rs b/silent/src/middleware/middlewares/mod.rs index 3a9c052..ec7060a 100644 --- a/silent/src/middleware/middlewares/mod.rs +++ b/silent/src/middleware/middlewares/mod.rs @@ -1,7 +1,5 @@ mod cors; mod exception_handler; -mod request_time_logger; pub use cors::{Cors, CorsType}; pub use exception_handler::ExceptionHandler; -pub use request_time_logger::RequestTimeLogger; diff --git a/silent/src/route/root.rs b/silent/src/route/root.rs index 69b23d4..fe72c20 100644 --- a/silent/src/route/root.rs +++ b/silent/src/route/root.rs @@ -1,6 +1,6 @@ #[cfg(feature = "grpc")] use crate::grpc::GrpcHandler; -use crate::middlewares::RequestTimeLogger; +use crate::middleware::middleware_trait::Next; #[cfg(feature = "grpc")] use crate::prelude::HandlerGetter; use crate::route::handler_match::{Match, RouteMatched}; @@ -11,10 +11,11 @@ use crate::session::SessionMiddleware; use crate::templates::TemplateMiddleware; #[cfg(feature = "scheduler")] use crate::Scheduler; -use crate::{Configs, Handler, MiddleWareHandler, Next, Request, Response, SilentError}; +use crate::{Configs, Handler, MiddleWareHandler, Request, Response, SilentError}; #[cfg(feature = "session")] use async_session::SessionStore; use async_trait::async_trait; +use chrono::Utc; #[cfg(feature = "grpc")] use http::Method; use std::fmt; @@ -116,35 +117,22 @@ impl RootRoute { .iter_mut() .for_each(|r| r.middleware_hook_first(handler.clone())); } - pub(crate) fn set_configs(&mut self, configs: Option) { self.configs = configs; } } -struct RootHandler { - inner: RouteMatched, - middlewares: Vec>, -} -#[async_trait] -impl Handler for RootHandler { - async fn call(&self, req: Request) -> Result { - match self.inner.clone() { - RouteMatched::Matched(route) => { - let next = Next::build(Arc::new(route), self.middlewares.clone()); - next.call(req).await - } - RouteMatched::Unmatched => Err(SilentError::NotFound), - } - } -} - #[async_trait] impl Handler for RootRoute { async fn call(&self, mut req: Request) -> Result { tracing::debug!("{:?}", req); let configs = self.configs.clone().unwrap_or_default(); req.configs = configs.clone(); + let method = req.method().clone(); + let url = req.uri().to_string().clone(); + let http_version = req.version(); + let peer_addr = req.remote(); + let start_time = Utc::now().time(); #[cfg(feature = "scheduler")] req.extensions_mut().insert(self.scheduler.clone()); @@ -155,12 +143,44 @@ impl Handler for RootRoute { } } let (mut req, path) = req.split_url(); - let handler = RootHandler { - inner: self.handler_match(&mut req, &path), - middlewares: root_middlewares, + let res = match self.handler_match(&mut req, path.as_str()) { + RouteMatched::Matched(route) => { + let next = Next::build(Arc::new(route), root_middlewares); + next.call(req).await + } + RouteMatched::Unmatched => Err(SilentError::NotFound), }; - let next = Next::build(Arc::new(handler), vec![Arc::new(RequestTimeLogger::new())]); - next.call(req).await + let end_time = Utc::now().time(); + let req_time = end_time - start_time; + Ok(match res { + Ok(res) => { + tracing::info!( + "{} {} {} {:?} {} {:?} {}", + peer_addr, + method, + url, + http_version, + res.status.as_u16(), + res.content_length().lower(), + req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0 + ); + res + } + Err(e) => { + tracing::error!( + "{} {} {} {:?} {} {:?} {} {}", + peer_addr, + method, + url, + http_version, + e.status().as_u16(), + 0, + req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0, + e.to_string() + ); + e.into() + } + }) } } From e79d90b7e7e219f5085863af09bed01ce4afb4aa Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Wed, 7 Aug 2024 17:35:44 +0800 Subject: [PATCH 30/49] =?UTF-8?q?refactor:=20grpc=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/grpc/src/main.rs | 14 ++-- examples/grpc_h2c/src/main.rs | 11 +-- examples/grpc_streaming/src/main.rs | 20 ++--- silent/src/grpc/handler.rs | 18 +---- silent/src/grpc/mod.rs | 2 + silent/src/grpc/register.rs | 41 ++++++++++ silent/src/lib.rs | 4 + silent/src/route/root.rs | 114 ++++++---------------------- 8 files changed, 97 insertions(+), 127 deletions(-) create mode 100644 silent/src/grpc/register.rs diff --git a/examples/grpc/src/main.rs b/examples/grpc/src/main.rs index d896607..9db735e 100644 --- a/examples/grpc/src/main.rs +++ b/examples/grpc/src/main.rs @@ -3,7 +3,8 @@ use tonic::{Request, Response, Status}; use hello_world::greeter_server::{Greeter, GreeterServer}; use hello_world::{HelloReply, HelloRequest}; -use silent::prelude::{logger, HandlerAppend, Level, Route, RouteService, Server}; +use silent::prelude::{info, logger, HandlerAppend, Level, Route, Server}; +use silent::GrpcRegister; mod client; @@ -21,7 +22,7 @@ impl Greeter for MyGreeter { request: Request, // Accept request of type HelloRequest ) -> Result, Status> { // Return an instance of type HelloReply - println!("Got a request: {:?}", request); + info!("Got a request: {:?}", request); let reply = HelloReply { message: format!("Hello {}!", request.into_inner().name), // We must use .into_inner() as the fields of gRPC requests and responses are private @@ -40,12 +41,13 @@ async fn main() -> Result<(), Box> { // // Wrap all services in the middleware stack // .add_service(greeter_server) // .into_router(); - let route = Route::new("").get(|_req| async { Ok("hello world") }); - let root = route.route().with_grpc(greeter_server.into()); - println!("route: \n{:?}", root); + let route = Route::new("") + .get(|_req| async { Ok("hello world") }) + .append(greeter_server.service()); + info!("route: \n{:?}", route); Server::new() .bind("0.0.0.0:50051".parse().unwrap()) - .serve(root) + .serve(route) .await; Ok(()) } diff --git a/examples/grpc_h2c/src/main.rs b/examples/grpc_h2c/src/main.rs index f6d1e21..8aecac6 100644 --- a/examples/grpc_h2c/src/main.rs +++ b/examples/grpc_h2c/src/main.rs @@ -3,7 +3,8 @@ use tonic::{Request, Response, Status}; use hello_world::greeter_server::{Greeter, GreeterServer}; use hello_world::{HelloReply, HelloRequest}; -use silent::prelude::{logger, HandlerAppend, Level, Route, RouteService}; +use silent::prelude::{info, logger, HandlerAppend, Level, Route}; +use silent::GrpcRegister; pub mod hello_world { tonic::include_proto!("helloworld"); @@ -18,7 +19,7 @@ impl Greeter for MyGreeter { &self, request: Request, ) -> Result, Status> { - println!("Got a request from {:?}", request.remote_addr()); + info!("Got a request from {:?}", request.remote_addr()); let reply = hello_world::HelloReply { message: format!("Hello {}!", request.into_inner().name), @@ -32,11 +33,11 @@ async fn main() -> Result<(), Box> { let greeter = MyGreeter::default(); logger::fmt().with_max_level(Level::INFO).init(); - let route = Route::new("").get(|_req| async { Ok("hello world") }); - let root = route.route().with_grpc(GreeterServer::new(greeter).into()); + let mut route = Route::new("").get(|_req| async { Ok("hello world") }); + GreeterServer::new(greeter).register(&mut route); silent::prelude::Server::new() .bind("0.0.0.0:50051".parse().unwrap()) - .serve(root) + .serve(route) .await; Ok(()) } diff --git a/examples/grpc_streaming/src/main.rs b/examples/grpc_streaming/src/main.rs index b442260..e533559 100644 --- a/examples/grpc_streaming/src/main.rs +++ b/examples/grpc_streaming/src/main.rs @@ -5,7 +5,8 @@ use tokio_stream::{wrappers::ReceiverStream, Stream, StreamExt}; use tonic::{Request, Response, Status, Streaming}; use pb::{EchoRequest, EchoResponse}; -use silent::prelude::{logger, HandlerAppend, Level, Route, RouteService, Server}; +use silent::prelude::{info, logger, HandlerAppend, Level, Route, RouteService, Server}; +use silent::GrpcRegister; mod client; @@ -54,8 +55,8 @@ impl pb::echo_server::Echo for EchoServer { &self, req: Request, ) -> EchoResult { - println!("EchoServer::server_streaming_echo"); - println!("\tclient connected from: {:?}", req.remote_addr()); + info!("EchoServer::server_streaming_echo"); + info!("\tclient connected from: {:?}", req.remote_addr()); // creating infinite stream with requested message let repeat = std::iter::repeat(EchoResponse { @@ -78,7 +79,7 @@ impl pb::echo_server::Echo for EchoServer { } } } - println!("\tclient disconnected"); + info!("\tclient disconnected"); }); let output_stream = ReceiverStream::new(rx); @@ -100,7 +101,7 @@ impl pb::echo_server::Echo for EchoServer { &self, req: Request>, ) -> EchoResult { - println!("EchoServer::bidirectional_streaming_echo"); + info!("EchoServer::bidirectional_streaming_echo"); let mut in_stream = req.into_inner(); let (tx, rx) = mpsc::channel(128); @@ -121,7 +122,7 @@ impl pb::echo_server::Echo for EchoServer { if io_err.kind() == ErrorKind::BrokenPipe { // here you can handle special case when client // disconnected in unexpected way - eprintln!("\tclient disconnected: broken pipe"); + info!("\tclient disconnected: broken pipe"); break; } } @@ -133,7 +134,7 @@ impl pb::echo_server::Echo for EchoServer { } } } - println!("\tstream ended"); + info!("\tstream ended"); }); // echo just write the same data that was received @@ -150,9 +151,8 @@ async fn main() -> Result<(), Box> { logger::fmt().with_max_level(Level::INFO).init(); let server = EchoServer {}; let route = Route::new("").get(|_req| async { Ok("hello world") }); - let root = route - .route() - .with_grpc(pb::echo_server::EchoServer::new(server).into()); + let mut root = route.route(); + root.push(pb::echo_server::EchoServer::new(server).service()); Server::new() .bind("0.0.0.0:50051".parse().unwrap()) .serve(root) diff --git a/silent/src/grpc/handler.rs b/silent/src/grpc/handler.rs index ad6f145..d0c962b 100644 --- a/silent/src/grpc/handler.rs +++ b/silent/src/grpc/handler.rs @@ -2,11 +2,9 @@ use std::sync::Arc; use super::utils::merge_grpc_response; use crate::grpc::service::GrpcService; -use crate::prelude::HandlerGetter; -use crate::route::Route; use crate::{Handler, Response, SilentError}; use async_trait::async_trait; -use http::{header, HeaderValue, Method, StatusCode}; +use http::{header, HeaderValue, StatusCode}; use http_body_util::BodyExt; use hyper::upgrade::OnUpgrade; use hyper_util::rt::TokioExecutor; @@ -55,20 +53,6 @@ where pub fn path(&self) -> &str { S::NAME } - - pub fn service(self) -> Route { - let path = self.path().to_string(); - let handler = Arc::new(self); - Route::new(path.as_str()).append( - Route::new("") - .insert_handler(Method::POST, handler.clone()) - .insert_handler(Method::GET, handler), - ) - } - - pub fn register(self, route: &mut Route) { - route.extend(self.service()); - } } impl From for GrpcHandler diff --git a/silent/src/grpc/mod.rs b/silent/src/grpc/mod.rs index 91c6c1a..cfbd1f9 100644 --- a/silent/src/grpc/mod.rs +++ b/silent/src/grpc/mod.rs @@ -1,6 +1,8 @@ mod handler; +mod register; mod service; mod utils; // mod stream; pub use handler::GrpcHandler; +pub use register::GrpcRegister; diff --git a/silent/src/grpc/register.rs b/silent/src/grpc/register.rs new file mode 100644 index 0000000..eb7b367 --- /dev/null +++ b/silent/src/grpc/register.rs @@ -0,0 +1,41 @@ +use crate::grpc::GrpcHandler; +use crate::prelude::HandlerGetter; +use crate::prelude::Route; +use http::Method; +use std::sync::Arc; +use tonic::body::BoxBody; +use tonic::codegen::Service; +use tonic::server::NamedService; + +pub trait GrpcRegister { + fn get_handler(self) -> GrpcHandler; + fn service(self) -> Route; + fn register(self, route: &mut Route); +} + +impl GrpcRegister for S +where + S: Service, Response = http::Response> + NamedService, + S: Clone + Send + 'static, + S: Sync + Send + 'static, + S::Future: Send + 'static, + S::Error: Into> + Send, +{ + fn get_handler(self) -> GrpcHandler { + GrpcHandler::new(self) + } + fn service(self) -> Route { + let handler = self.get_handler(); + let path = handler.path().to_string(); + let handler = Arc::new(handler); + Route::new(path.as_str()).append( + Route::new("") + .insert_handler(Method::POST, handler.clone()) + .insert_handler(Method::GET, handler), + ) + } + + fn register(self, route: &mut Route) { + route.extend(self.service()); + } +} diff --git a/silent/src/lib.rs b/silent/src/lib.rs index 4f999a6..59debe3 100644 --- a/silent/src/lib.rs +++ b/silent/src/lib.rs @@ -32,6 +32,8 @@ use multer; pub use crate::configs::Configs; pub use crate::core::{next::Next, request::Request, response::Response}; +#[cfg(feature = "grpc")] +pub use crate::grpc::{GrpcHandler, GrpcRegister}; pub use crate::middleware::{middlewares, MiddleWareHandler}; pub use error::SilentError; pub use error::SilentResult as Result; @@ -51,6 +53,8 @@ pub mod prelude { res_body::stream_body, res_body::ResBody, response::Response, }; pub use crate::error::{SilentError, SilentResult as Result}; + #[cfg(feature = "grpc")] + pub use crate::grpc::{GrpcHandler, GrpcRegister}; #[cfg(feature = "static")] pub use crate::handler::static_handler; pub use crate::handler::Handler; diff --git a/silent/src/route/root.rs b/silent/src/route/root.rs index fe72c20..9b0981e 100644 --- a/silent/src/route/root.rs +++ b/silent/src/route/root.rs @@ -1,8 +1,4 @@ -#[cfg(feature = "grpc")] -use crate::grpc::GrpcHandler; -use crate::middleware::middleware_trait::Next; -#[cfg(feature = "grpc")] -use crate::prelude::HandlerGetter; +use crate::middlewares::RequestTimeLogger; use crate::route::handler_match::{Match, RouteMatched}; use crate::route::Route; #[cfg(feature = "session")] @@ -11,23 +7,14 @@ use crate::session::SessionMiddleware; use crate::templates::TemplateMiddleware; #[cfg(feature = "scheduler")] use crate::Scheduler; -use crate::{Configs, Handler, MiddleWareHandler, Request, Response, SilentError}; +use crate::{Configs, Handler, MiddleWareHandler, Next, Request, Response, SilentError}; #[cfg(feature = "session")] use async_session::SessionStore; use async_trait::async_trait; -use chrono::Utc; -#[cfg(feature = "grpc")] -use http::Method; use std::fmt; use std::sync::Arc; #[cfg(feature = "scheduler")] use tokio::sync::Mutex; -#[cfg(feature = "grpc")] -use tonic::body::BoxBody; -#[cfg(feature = "grpc")] -use tonic::codegen::Service; -#[cfg(feature = "grpc")] -use tonic::server::NamedService; #[derive(Clone, Default)] pub struct RootRoute { @@ -65,38 +52,6 @@ impl RootRoute { } } - #[cfg(feature = "grpc")] - pub fn grpc(&mut self, grpc: GrpcHandler) - where - S: Service, Response = http::Response> + NamedService, - S: Clone + Send + 'static, - S: Sync + Send + 'static, - S::Future: Send + 'static, - S::Error: Into> + Send, - { - let path = grpc.path().to_string(); - let handler = Arc::new(grpc); - let route = Route::new(path.as_str()).append( - Route::new("") - .insert_handler(Method::POST, handler.clone()) - .insert_handler(Method::GET, handler), - ); - self.push(route) - } - - #[cfg(feature = "grpc")] - pub fn with_grpc(mut self, grpc: GrpcHandler) -> Self - where - S: Service, Response = http::Response> + NamedService, - S: Clone + Send + 'static, - S: Sync + Send + 'static, - S::Future: Send + 'static, - S::Error: Into> + Send, - { - self.grpc(grpc); - self - } - pub fn push(&mut self, route: Route) { self.middlewares.extend(route.root_middlewares.clone()); self.children.push(route); @@ -117,22 +72,35 @@ impl RootRoute { .iter_mut() .for_each(|r| r.middleware_hook_first(handler.clone())); } + pub(crate) fn set_configs(&mut self, configs: Option) { self.configs = configs; } } +struct RootHandler { + inner: RouteMatched, + middlewares: Vec>, +} +#[async_trait] +impl Handler for RootHandler { + async fn call(&self, req: Request) -> Result { + match self.inner.clone() { + RouteMatched::Matched(route) => { + let next = Next::build(Arc::new(route), self.middlewares.clone()); + next.call(req).await + } + RouteMatched::Unmatched => Err(SilentError::NotFound), + } + } +} + #[async_trait] impl Handler for RootRoute { async fn call(&self, mut req: Request) -> Result { tracing::debug!("{:?}", req); let configs = self.configs.clone().unwrap_or_default(); req.configs = configs.clone(); - let method = req.method().clone(); - let url = req.uri().to_string().clone(); - let http_version = req.version(); - let peer_addr = req.remote(); - let start_time = Utc::now().time(); #[cfg(feature = "scheduler")] req.extensions_mut().insert(self.scheduler.clone()); @@ -143,44 +111,12 @@ impl Handler for RootRoute { } } let (mut req, path) = req.split_url(); - let res = match self.handler_match(&mut req, path.as_str()) { - RouteMatched::Matched(route) => { - let next = Next::build(Arc::new(route), root_middlewares); - next.call(req).await - } - RouteMatched::Unmatched => Err(SilentError::NotFound), + let handler = RootHandler { + inner: self.handler_match(&mut req, &path), + middlewares: root_middlewares, }; - let end_time = Utc::now().time(); - let req_time = end_time - start_time; - Ok(match res { - Ok(res) => { - tracing::info!( - "{} {} {} {:?} {} {:?} {}", - peer_addr, - method, - url, - http_version, - res.status.as_u16(), - res.content_length().lower(), - req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0 - ); - res - } - Err(e) => { - tracing::error!( - "{} {} {} {:?} {} {:?} {} {}", - peer_addr, - method, - url, - http_version, - e.status().as_u16(), - 0, - req_time.num_nanoseconds().unwrap_or(0) as f64 / 1000000.0, - e.to_string() - ); - e.into() - } - }) + let next = Next::build(Arc::new(handler), vec![Arc::new(RequestTimeLogger::new())]); + next.call(req).await } } From 85b3d12d07c9cbb880f567b0cecc636e303d2acc Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Mon, 12 Aug 2024 16:29:15 +0800 Subject: [PATCH 31/49] =?UTF-8?q?request=E6=B7=BB=E5=8A=A0form=5Fparse?= =?UTF-8?q?=E3=80=81json=5Ffield=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- silent/src/core/request.rs | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/silent/src/core/request.rs b/silent/src/core/request.rs index 2310c9c..231b7c6 100644 --- a/silent/src/core/request.rs +++ b/silent/src/core/request.rs @@ -234,11 +234,6 @@ impl Request { &mut self.configs } - /// 获取可变原请求体 - // pub fn req_mut(&mut self) -> &mut BaseRequest { - // &mut self.req - // } - /// 获取路径参数集合 pub fn path_params(&self) -> &HashMap { &self.path_params @@ -312,6 +307,18 @@ impl Request { .await } + /// 获取请求form_data + #[cfg(feature = "multipart")] + #[inline] + pub async fn form_parse(&mut self) -> Result + where + for<'de> T: Deserialize<'de>, + { + let form_data = self.form_data().await?; + let value = serde_json::to_value(form_data.fields.clone())?; + serde_json::from_value(value).map_err(Into::into) + } + /// 转换body参数 #[cfg(feature = "multipart")] pub async fn form_field(&mut self, key: &str) -> Option @@ -376,6 +383,21 @@ impl Request { } } + /// 转换body参数按Json匹配 + pub async fn json_field(&mut self, key: &str) -> Result + where + for<'de> T: Deserialize<'de>, + { + let value: Value = self.json_parse().await?; + serde_json::from_value( + value + .get(key) + .ok_or(SilentError::ParamsNotFound)? + .to_owned(), + ) + .map_err(Into::into) + } + /// 获取请求body #[inline] pub fn replace_extensions(&mut self, extensions: Extensions) -> Extensions { From cb161f1ae5cc5e4a921586dc8e50dfbb44fbaa86 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Fri, 6 Sep 2024 13:46:54 +0800 Subject: [PATCH 32/49] =?UTF-8?q?feat:=20cookie=20scheduler=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/form/Cargo.toml | 2 +- examples/grpc/Cargo.toml | 8 +-- examples/grpc_streaming/Cargo.toml | 8 +-- examples/sse-chat/Cargo.toml | 2 +- silent/src/core/adapt.rs | 28 +++-------- silent/src/core/cookie.rs | 78 ++++++++++++++++++++++++++++++ silent/src/core/mod.rs | 2 + silent/src/core/request.rs | 67 +++++++------------------ silent/src/core/response.rs | 41 ---------------- silent/src/lib.rs | 4 +- silent/src/scheduler/mod.rs | 2 + silent/src/scheduler/traits.rs | 16 ++++++ 12 files changed, 134 insertions(+), 124 deletions(-) create mode 100644 silent/src/core/cookie.rs create mode 100644 silent/src/scheduler/traits.rs diff --git a/examples/form/Cargo.toml b/examples/form/Cargo.toml index 5877aa3..e450f80 100644 --- a/examples/form/Cargo.toml +++ b/examples/form/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -serde = { version = "1.0.213", features = ["derive"] } +serde = { version = "1.0.209", features = ["derive"] } diff --git a/examples/grpc/Cargo.toml b/examples/grpc/Cargo.toml index 87eb8a5..b3775f5 100644 --- a/examples/grpc/Cargo.toml +++ b/examples/grpc/Cargo.toml @@ -13,11 +13,11 @@ name = "example-grpc-client" path = "src/client.rs" [dependencies] -tonic = { version = "0.12.3" } +tonic = { version = "0.12.2" } prost = "0.13" -tokio = { version = "1.41", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] } silent = { path = "../../silent", features = ["grpc"] } -async-trait = "0.1.83" +async-trait = "0.1.82" [build-dependencies] -tonic-build = { version = "0.12.3" } +tonic-build = { version = "0.12.2" } diff --git a/examples/grpc_streaming/Cargo.toml b/examples/grpc_streaming/Cargo.toml index f9662f1..b5a838f 100644 --- a/examples/grpc_streaming/Cargo.toml +++ b/examples/grpc_streaming/Cargo.toml @@ -13,13 +13,13 @@ name = "example-grpc-client" path = "src/client.rs" [dependencies] -tonic = { version = "0.12.3" } +tonic = { version = "0.12.2" } prost = "0.13" -tokio = { version = "1.41", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] } silent = { path = "../../silent", features = ["grpc"] } -async-trait = "0.1.83" +async-trait = "0.1.82" tokio-stream = "0.1.16" h2 = "0.4.6" [build-dependencies] -tonic-build = { version = "0.12.3" } +tonic-build = { version = "0.12.2" } diff --git a/examples/sse-chat/Cargo.toml b/examples/sse-chat/Cargo.toml index 533d6b7..39d053d 100644 --- a/examples/sse-chat/Cargo.toml +++ b/examples/sse-chat/Cargo.toml @@ -11,4 +11,4 @@ once_cell = "1" parking_lot = "0.12" tokio = { version = "1", features = ["macros"] } tokio-stream = { version = "0.1", features = ["net"] } -serde = { version = "1.0.213", features = ["derive"] } +serde = { version = "1.0.209", features = ["derive"] } diff --git a/silent/src/core/adapt.rs b/silent/src/core/adapt.rs index de35b07..b9303e5 100644 --- a/silent/src/core/adapt.rs +++ b/silent/src/core/adapt.rs @@ -44,35 +44,20 @@ fn get_cookie(req: &HyperRequest) -> Result { } impl RequestAdapt for HyperRequest { - #[cfg(feature = "cookie")] fn tran_to_request(self) -> Request { + #[cfg(feature = "cookie")] let cookies = get_cookie(&self).unwrap_or_default(); let (parts, body) = self.into_parts(); let mut req = Request::from_parts(parts, body); - *req.cookies_mut() = cookies; + #[cfg(feature = "cookie")] + req.extensions_mut().insert(cookies); req } - #[cfg(not(feature = "cookie"))] - fn tran_to_request(self) -> Request { - let (parts, body) = self.into_parts(); - Request::from_parts(parts, body) - } } impl ResponseAdapt for HyperResponse { type Body = B; fn tran_from_response(res: Response) -> Self { - #[cfg(feature = "cookie")] - let Response { - status, - headers, - body, - cookies, - version, - extensions, - .. - } = res; - #[cfg(not(feature = "cookie"))] let Response { status, headers, @@ -84,15 +69,14 @@ impl ResponseAdapt for HyperResponse { let mut res = hyper::Response::new(body); res.headers_mut().extend(headers); - res.extensions_mut().extend(extensions); - *res.version_mut() = version; #[cfg(feature = "cookie")] - for cookie in cookies.delta() { + for cookie in extensions.get::().unwrap().delta() { if let Ok(hv) = cookie.encoded().to_string().parse() { res.headers_mut().append(header::SET_COOKIE, hv); } } - // Default to a 404 if no response code was set + res.extensions_mut().extend(extensions); + *res.version_mut() = version; *res.status_mut() = status; res diff --git a/silent/src/core/cookie.rs b/silent/src/core/cookie.rs new file mode 100644 index 0000000..c659d03 --- /dev/null +++ b/silent/src/core/cookie.rs @@ -0,0 +1,78 @@ +use crate::{Request, Response, Result, SilentError}; +use cookie::{Cookie, CookieJar}; +use http::StatusCode; + +pub trait CookieExt { + /// Get `CookieJar` reference. + fn cookies(&self) -> Result<&CookieJar>; + /// Get `CookieJar` mutable reference. + fn cookies_mut(&mut self) -> Result<&mut CookieJar>; + /// Get `Cookie` from cookies. + fn cookie>(&self, name: T) -> Result>>; +} + +impl CookieExt for Request { + fn cookies(&self) -> Result<&CookieJar> { + self.extensions().get().ok_or_else(|| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + "Request does not have cookie jar", + ) + }) + } + + fn cookies_mut(&mut self) -> Result<&mut CookieJar> { + self.extensions_mut().get_mut().ok_or_else(|| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + "Request does not have cookie jar", + ) + }) + } + + fn cookie>(&self, name: T) -> Result>> { + Ok(self + .extensions() + .get::() + .ok_or_else(|| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + "Request does not have cookie jar", + ) + })? + .get(name.as_ref())) + } +} + +impl CookieExt for Response { + fn cookies(&self) -> Result<&CookieJar> { + self.extensions().get().ok_or_else(|| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + "Request does not have cookie jar", + ) + }) + } + + fn cookies_mut(&mut self) -> Result<&mut CookieJar> { + self.extensions_mut().get_mut().ok_or_else(|| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + "Request does not have cookie jar", + ) + }) + } + + fn cookie>(&self, name: T) -> Result>> { + Ok(self + .extensions() + .get::() + .ok_or_else(|| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + "Request does not have cookie jar", + ) + })? + .get(name.as_ref())) + } +} diff --git a/silent/src/core/mod.rs b/silent/src/core/mod.rs index 447e661..56a8e38 100644 --- a/silent/src/core/mod.rs +++ b/silent/src/core/mod.rs @@ -1,5 +1,7 @@ pub mod adapt; +#[cfg(feature = "cookie")] +pub(crate) mod cookie; #[cfg(feature = "multipart")] pub(crate) mod form; pub(crate) mod next; diff --git a/silent/src/core/request.rs b/silent/src/core/request.rs index 231b7c6..1ca2ac7 100644 --- a/silent/src/core/request.rs +++ b/silent/src/core/request.rs @@ -5,12 +5,8 @@ use crate::core::req_body::ReqBody; #[cfg(feature = "multipart")] use crate::core::serde::from_str_multi_val; use crate::header::CONTENT_TYPE; -#[cfg(feature = "scheduler")] -use crate::Scheduler; -use crate::{Configs, SilentError}; +use crate::{Configs, Result, SilentError}; use bytes::Bytes; -#[cfg(feature = "cookie")] -use cookie::{Cookie, CookieJar}; use http::request::Parts; use http::{Extensions, HeaderMap, HeaderValue, Method, Uri, Version}; use http::{Request as BaseRequest, StatusCode}; @@ -20,11 +16,7 @@ use serde::de::StdError; use serde::Deserialize; use serde_json::Value; use std::collections::HashMap; -use std::net::IpAddr; -#[cfg(feature = "scheduler")] -use std::sync::Arc; -#[cfg(feature = "scheduler")] -use tokio::sync::Mutex; +use std::net::{IpAddr, SocketAddr}; use tokio::sync::OnceCell; use url::form_urlencoded; @@ -43,8 +35,6 @@ pub struct Request { #[cfg(feature = "multipart")] form_data: OnceCell, json_data: OnceCell, - #[cfg(feature = "cookie")] - pub(crate) cookies: CookieJar, pub(crate) configs: Configs, } @@ -55,7 +45,7 @@ impl Request { } /// Strip the request to [`hyper::Request`]. #[doc(hidden)] - pub fn strip_to_hyper(&mut self) -> Result, SilentError> + pub fn strip_to_hyper(&mut self) -> Result> where QB: TryFrom, >::Error: StdError + Send + Sync + 'static, @@ -83,7 +73,7 @@ impl Request { } /// Strip the request to [`hyper::Request`]. #[doc(hidden)] - pub async fn strip_to_bytes_hyper(&mut self) -> Result, SilentError> { + pub async fn strip_to_bytes_hyper(&mut self) -> Result> { let mut builder = http::request::Builder::new() .method(self.method().clone()) .uri(self.uri().clone()) @@ -128,8 +118,6 @@ impl Request { #[cfg(feature = "multipart")] form_data: OnceCell::new(), json_data: OnceCell::new(), - #[cfg(feature = "cookie")] - cookies: CookieJar::default(), configs: Configs::default(), } } @@ -155,6 +143,15 @@ impl Request { .unwrap() } + /// 设置访问真实地址 + #[inline] + pub fn set_remote(&mut self, remote_addr: SocketAddr) { + if self.headers().get("x-real-ip").is_none() { + self.headers_mut() + .insert("x-real-ip", remote_addr.ip().to_string().parse().unwrap()); + } + } + /// 获取请求方法 #[inline] pub fn method(&self) -> &Method { @@ -212,7 +209,7 @@ impl Request { /// 获取配置 #[inline] - pub fn get_config(&self) -> Result<&T, SilentError> { + pub fn get_config(&self) -> Result<&T> { self.configs.get::().ok_or(SilentError::ConfigNotFound) } @@ -240,7 +237,7 @@ impl Request { } /// 获取路径参数 - pub fn get_path_params<'a, T>(&'a self, key: &'a str) -> Result + pub fn get_path_params<'a, T>(&'a self, key: &'a str) -> Result where T: TryFrom<&'a PathParam, Error = SilentError>, { @@ -262,7 +259,7 @@ impl Request { } /// 转换query参数 - pub fn params_parse(&mut self) -> Result + pub fn params_parse(&mut self) -> Result where for<'de> T: Deserialize<'de>, { @@ -295,7 +292,7 @@ impl Request { /// 获取请求form_data #[cfg(feature = "multipart")] #[inline] - pub async fn form_data(&mut self) -> Result<&FormData, SilentError> { + pub async fn form_data(&mut self) -> Result<&FormData> { let content_type = self.content_type().unwrap(); if content_type.subtype() != mime::FORM_DATA { return Err(SilentError::ContentTypeError); @@ -343,7 +340,7 @@ impl Request { } /// 转换body参数按Json匹配 - pub async fn json_parse(&mut self) -> Result + pub async fn json_parse(&mut self) -> Result where for<'de> T: Deserialize<'de>, { @@ -415,34 +412,6 @@ impl Request { let url = self.uri().path().to_string(); (self, url) } - - #[cfg(feature = "cookie")] - /// Get `CookieJar` reference. - #[inline] - pub fn cookies(&self) -> &CookieJar { - &self.cookies - } - #[cfg(feature = "cookie")] - /// Get `CookieJar` mutable reference. - #[inline] - pub fn cookies_mut(&mut self) -> &mut CookieJar { - &mut self.cookies - } - #[cfg(feature = "cookie")] - /// Get `Cookie` from cookies. - #[inline] - pub fn cookie(&self, name: T) -> Option<&Cookie<'static>> - where - T: AsRef, - { - self.cookies.get(name.as_ref()) - } - #[cfg(feature = "scheduler")] - #[inline] - /// Get `Scheduler` from extensions. - pub fn scheduler(&self) -> &Arc> { - self.extensions().get().unwrap() - } } #[cfg(test)] diff --git a/silent/src/core/response.rs b/silent/src/core/response.rs index 44b0fea..cefc0a3 100644 --- a/silent/src/core/response.rs +++ b/silent/src/core/response.rs @@ -4,8 +4,6 @@ use std::fmt::{Display, Formatter}; use crate::core::res_body::{full, ResBody}; use crate::headers::{ContentType, Header, HeaderMap, HeaderMapExt}; use crate::{header, Configs, Result, SilentError, StatusCode}; -#[cfg(feature = "cookie")] -use cookie::{Cookie, CookieJar}; use http::{Extensions, Version}; use http_body::{Body, SizeHint}; use serde::Serialize; @@ -24,8 +22,6 @@ pub struct Response { /// The HTTP headers. pub(crate) headers: HeaderMap, pub(crate) body: B, - #[cfg(feature = "cookie")] - pub(crate) cookies: CookieJar, pub(crate) extensions: Extensions, pub(crate) configs: Configs, } @@ -52,8 +48,6 @@ impl Response { headers: HeaderMap::new(), version: Version::default(), body: ResBody::None, - #[cfg(feature = "cookie")] - cookies: CookieJar::default(), extensions: Extensions::default(), configs: Configs::default(), } @@ -206,41 +200,6 @@ impl Response { self } - #[cfg(feature = "cookie")] - /// Get `CookieJar` reference. - #[inline] - pub fn cookies(&self) -> &CookieJar { - &self.cookies - } - #[cfg(feature = "cookie")] - /// Get `CookieJar` mutable reference. - #[inline] - pub fn cookies_mut(&mut self) -> &mut CookieJar { - &mut self.cookies - } - #[cfg(feature = "cookie")] - /// Get `Cookie` from cookies. - #[inline] - pub fn cookie(&self, name: T) -> Option<&Cookie<'static>> - where - T: AsRef, - { - self.cookies.get(name.as_ref()) - } - - #[cfg(feature = "cookie")] - /// move response to from another response - pub fn copy_from_response(&mut self, res: Response) { - self.headers.extend(res.headers); - res.cookies.delta().for_each(|cookie| { - self.cookies.add(cookie.clone()); - }); - self.status = res.status; - self.extensions.extend(res.extensions); - self.set_body(res.body); - } - - #[cfg(not(feature = "cookie"))] /// move response to from another response pub fn copy_from_response(&mut self, res: Response) { self.headers.extend(res.headers); diff --git a/silent/src/lib.rs b/silent/src/lib.rs index 59debe3..278b884 100644 --- a/silent/src/lib.rs +++ b/silent/src/lib.rs @@ -42,7 +42,7 @@ pub use handler::HandlerWrapper; pub use headers; pub use hyper::{header, Method, StatusCode}; #[cfg(feature = "scheduler")] -pub use scheduler::{ProcessTime, Scheduler, Task}; +pub use scheduler::{ProcessTime, Scheduler, SchedulerExt, Task}; pub mod prelude { pub use crate::configs::Configs; @@ -66,7 +66,7 @@ pub mod prelude { pub use crate::route::handler_append::{HandlerAppend, HandlerGetter}; pub use crate::route::{RootRoute, Route, RouteService, RouterAdapt}; #[cfg(feature = "scheduler")] - pub use crate::scheduler::Task; + pub use crate::scheduler::{SchedulerExt, Task}; #[cfg(feature = "security")] pub use crate::security::{argon2, pbkdf2}; #[cfg(feature = "server")] diff --git a/silent/src/scheduler/mod.rs b/silent/src/scheduler/mod.rs index e79dc19..c3d0c30 100644 --- a/silent/src/scheduler/mod.rs +++ b/silent/src/scheduler/mod.rs @@ -1,6 +1,7 @@ mod process_time; mod storage; mod task; +pub mod traits; use anyhow::{anyhow, Result}; use std::sync::Arc; @@ -10,6 +11,7 @@ use tracing::{debug, error, info}; pub use process_time::ProcessTime; pub use task::Task; +pub use traits::SchedulerExt; #[derive(Debug, Clone)] pub struct Scheduler { diff --git a/silent/src/scheduler/traits.rs b/silent/src/scheduler/traits.rs new file mode 100644 index 0000000..cf06e6f --- /dev/null +++ b/silent/src/scheduler/traits.rs @@ -0,0 +1,16 @@ +use crate::{Request, Result, Scheduler, SilentError}; +use http::StatusCode; +use std::sync::Arc; +use tokio::sync::Mutex; + +pub trait SchedulerExt { + fn scheduler(&self) -> Result<&Arc>>; +} + +impl SchedulerExt for Request { + fn scheduler(&self) -> Result<&Arc>> { + self.extensions().get().ok_or_else(|| { + SilentError::business_error(StatusCode::INTERNAL_SERVER_ERROR, "No scheduler found") + }) + } +} From 8c41d1fbb7c1a518bc1b426649f5e8277da6613e Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Fri, 6 Sep 2024 15:15:54 +0800 Subject: [PATCH 33/49] =?UTF-8?q?=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/grpc_h2c/src/main.rs | 18 ++++----- examples/scheduler/src/main.rs | 2 +- silent/src/core/adapt.rs | 6 ++- silent/src/core/cookie.rs | 74 +++++++++------------------------- silent/src/lib.rs | 5 ++- silent/src/route/mod.rs | 4 +- silent/src/session/mod.rs | 4 +- 7 files changed, 43 insertions(+), 70 deletions(-) diff --git a/examples/grpc_h2c/src/main.rs b/examples/grpc_h2c/src/main.rs index 8aecac6..b115f12 100644 --- a/examples/grpc_h2c/src/main.rs +++ b/examples/grpc_h2c/src/main.rs @@ -30,14 +30,14 @@ impl Greeter for MyGreeter { #[tokio::main] async fn main() -> Result<(), Box> { - let greeter = MyGreeter::default(); - logger::fmt().with_max_level(Level::INFO).init(); - - let mut route = Route::new("").get(|_req| async { Ok("hello world") }); - GreeterServer::new(greeter).register(&mut route); - silent::prelude::Server::new() - .bind("0.0.0.0:50051".parse().unwrap()) - .serve(route) - .await; + let _greeter = MyGreeter::default(); + // logger::fmt().with_max_level(Level::INFO).init(); + // + // let mut route = Route::new("").get(|_req| async { Ok("hello world") }); + // GreeterServer::new(greeter).register(&mut route); + // silent::prelude::Server::new() + // .bind("0.0.0.0:50051".parse().unwrap()) + // .serve(route) + // .await; Ok(()) } diff --git a/examples/scheduler/src/main.rs b/examples/scheduler/src/main.rs index 25dc5c3..9bcd149 100644 --- a/examples/scheduler/src/main.rs +++ b/examples/scheduler/src/main.rs @@ -17,7 +17,7 @@ fn main() { }) }), ); - req.scheduler().lock().await.add_task(task)?; + req.scheduler()?.lock().await.add_task(task)?; Ok("hello world") }); Server::new().run(route); diff --git a/silent/src/core/adapt.rs b/silent/src/core/adapt.rs index b9303e5..d392405 100644 --- a/silent/src/core/adapt.rs +++ b/silent/src/core/adapt.rs @@ -8,6 +8,8 @@ use hyper::Response as HyperResponse; use crate::core::req_body::ReqBody; #[cfg(feature = "cookie")] +use crate::CookieExt; +#[cfg(feature = "cookie")] use crate::SilentError; use crate::{Request, Response}; @@ -58,6 +60,8 @@ impl RequestAdapt for HyperRequest { impl ResponseAdapt for HyperResponse { type Body = B; fn tran_from_response(res: Response) -> Self { + #[cfg(feature = "cookie")] + let cookies = res.cookies(); let Response { status, headers, @@ -70,7 +74,7 @@ impl ResponseAdapt for HyperResponse { let mut res = hyper::Response::new(body); res.headers_mut().extend(headers); #[cfg(feature = "cookie")] - for cookie in extensions.get::().unwrap().delta() { + for cookie in cookies.delta() { if let Ok(hv) = cookie.encoded().to_string().parse() { res.headers_mut().append(header::SET_COOKIE, hv); } diff --git a/silent/src/core/cookie.rs b/silent/src/core/cookie.rs index c659d03..6fd387c 100644 --- a/silent/src/core/cookie.rs +++ b/silent/src/core/cookie.rs @@ -1,78 +1,40 @@ -use crate::{Request, Response, Result, SilentError}; +use crate::{Request, Response}; use cookie::{Cookie, CookieJar}; -use http::StatusCode; +use http_body::Body; pub trait CookieExt { /// Get `CookieJar` reference. - fn cookies(&self) -> Result<&CookieJar>; + fn cookies(&self) -> CookieJar; /// Get `CookieJar` mutable reference. - fn cookies_mut(&mut self) -> Result<&mut CookieJar>; + fn cookies_mut(&mut self) -> &mut CookieJar; /// Get `Cookie` from cookies. - fn cookie>(&self, name: T) -> Result>>; + fn cookie>(&self, name: T) -> Option<&Cookie<'static>>; } impl CookieExt for Request { - fn cookies(&self) -> Result<&CookieJar> { - self.extensions().get().ok_or_else(|| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - "Request does not have cookie jar", - ) - }) + fn cookies(&self) -> CookieJar { + self.extensions().get().cloned().unwrap_or_default() } - fn cookies_mut(&mut self) -> Result<&mut CookieJar> { - self.extensions_mut().get_mut().ok_or_else(|| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - "Request does not have cookie jar", - ) - }) + fn cookies_mut(&mut self) -> &mut CookieJar { + self.extensions_mut().get_mut().unwrap() } - fn cookie>(&self, name: T) -> Result>> { - Ok(self - .extensions() - .get::() - .ok_or_else(|| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - "Request does not have cookie jar", - ) - })? - .get(name.as_ref())) + fn cookie>(&self, name: T) -> Option<&Cookie<'static>> { + self.extensions().get::()?.get(name.as_ref()) } } -impl CookieExt for Response { - fn cookies(&self) -> Result<&CookieJar> { - self.extensions().get().ok_or_else(|| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - "Request does not have cookie jar", - ) - }) +impl CookieExt for Response { + fn cookies(&self) -> CookieJar { + self.extensions().get().cloned().unwrap_or_default() } - fn cookies_mut(&mut self) -> Result<&mut CookieJar> { - self.extensions_mut().get_mut().ok_or_else(|| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - "Request does not have cookie jar", - ) - }) + fn cookies_mut(&mut self) -> &mut CookieJar { + self.extensions_mut().get_mut().unwrap() } - fn cookie>(&self, name: T) -> Result>> { - Ok(self - .extensions() - .get::() - .ok_or_else(|| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - "Request does not have cookie jar", - ) - })? - .get(name.as_ref())) + fn cookie>(&self, name: T) -> Option<&Cookie<'static>> { + self.extensions().get::()?.get(name.as_ref()) } } diff --git a/silent/src/lib.rs b/silent/src/lib.rs index 278b884..a6a1b90 100644 --- a/silent/src/lib.rs +++ b/silent/src/lib.rs @@ -1,6 +1,5 @@ mod configs; /// The `silent` library. -#[warn(missing_docs)] mod core; mod error; #[cfg(feature = "grpc")] @@ -31,6 +30,8 @@ mod ws; use multer; pub use crate::configs::Configs; +#[cfg(feature = "cookie")] +pub use crate::core::cookie::CookieExt; pub use crate::core::{next::Next, request::Request, response::Response}; #[cfg(feature = "grpc")] pub use crate::grpc::{GrpcHandler, GrpcRegister}; @@ -46,6 +47,8 @@ pub use scheduler::{ProcessTime, Scheduler, SchedulerExt, Task}; pub mod prelude { pub use crate::configs::Configs; + #[cfg(feature = "cookie")] + pub use crate::core::cookie::CookieExt; #[cfg(feature = "multipart")] pub use crate::core::form::{FilePart, FormData}; pub use crate::core::{ diff --git a/silent/src/route/mod.rs b/silent/src/route/mod.rs index 1c024b5..dc40ccf 100644 --- a/silent/src/route/mod.rs +++ b/silent/src/route/mod.rs @@ -14,6 +14,8 @@ use crate::handler::Handler; use crate::middleware::MiddleWareHandler; #[cfg(feature = "static")] use crate::prelude::HandlerGetter; +#[cfg(feature = "cookie")] +use crate::CookieExt; use crate::{Method, Next, Request, Response, SilentError}; pub(crate) mod handler_append; @@ -191,7 +193,7 @@ impl Handler for Route { pre_res.configs = configs; #[cfg(feature = "cookie")] { - *pre_res.cookies_mut() = req.cookies().clone(); + pre_res.extensions_mut().insert(req.cookies().clone()); } #[cfg(feature = "session")] let session = req.extensions().get::().cloned(); diff --git a/silent/src/session/mod.rs b/silent/src/session/mod.rs index e6ca309..d5f498b 100644 --- a/silent/src/session/mod.rs +++ b/silent/src/session/mod.rs @@ -1,4 +1,6 @@ -use crate::{Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError, StatusCode}; +use crate::{ + CookieExt, Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError, StatusCode, +}; use async_session::{MemoryStore, Session, SessionStore}; use async_trait::async_trait; use cookie::Cookie; From f56716920e21c07e5ab09d3613cb8ac3ca7c8b56 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Thu, 5 Sep 2024 17:15:19 +0800 Subject: [PATCH 34/49] =?UTF-8?q?feat[plugin]:=20=E6=B7=BB=E5=8A=A0plugin?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/plugin_test/Cargo.toml | 14 +++ examples/plugin_test/README.MD | 6 + .../plugin_test/libexamples_plugins.dylib | Bin 0 -> 541512 bytes examples/plugin_test/src/main.rs | 21 ++++ examples/plugins/.cargo/config.toml | 2 + examples/plugins/Cargo.toml | 15 +++ examples/plugins/src/lib.rs | 6 + silent/src/ws/route.rs | 115 ++++++++++++++++++ 8 files changed, 179 insertions(+) create mode 100644 examples/plugin_test/Cargo.toml create mode 100644 examples/plugin_test/README.MD create mode 100755 examples/plugin_test/libexamples_plugins.dylib create mode 100644 examples/plugin_test/src/main.rs create mode 100644 examples/plugins/.cargo/config.toml create mode 100644 examples/plugins/Cargo.toml create mode 100644 examples/plugins/src/lib.rs create mode 100644 silent/src/ws/route.rs diff --git a/examples/plugin_test/Cargo.toml b/examples/plugin_test/Cargo.toml new file mode 100644 index 0000000..0eef40c --- /dev/null +++ b/examples/plugin_test/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "examples_plugin_test" +edition.workspace = true +authors.workspace = true +homepage.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +silent = { path = "../../silent" } +crossbeam-epoch = "0.9.18" +dlopen2 = { version = "0.7.0", features = ["derive"] } diff --git a/examples/plugin_test/README.MD b/examples/plugin_test/README.MD new file mode 100644 index 0000000..bf295dc --- /dev/null +++ b/examples/plugin_test/README.MD @@ -0,0 +1,6 @@ +## where is lib from? + +Compiled from example project plugins + +this example lib is compiled by MacOS, if your system is different, you need to compile it by yourself and edit +lib_path. diff --git a/examples/plugin_test/libexamples_plugins.dylib b/examples/plugin_test/libexamples_plugins.dylib new file mode 100755 index 0000000000000000000000000000000000000000..c165da4efe4df9aa195b14a290c8b9ed9e7acd6f GIT binary patch literal 541512 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%0A@fllUe*@1r?k8D!0PU~2j1?s9{{fxyduOF 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 &TX$~46#{1j}d>4w# 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 PluginMetadata, + #[allow(improper_ctypes_definitions)] + get_route: unsafe extern "C" fn() -> Route, +} + +fn main() -> Result<(), Box> { + logger::fmt().with_max_level(Level::INFO).init(); + let lib_path = "./libexamples_plugins.dylib"; + let container: Container = + unsafe { Container::load(lib_path) }.expect("无法加载插件"); + let route = unsafe { container.get_route() }; + Server::new().run(route); + Ok(()) +} diff --git a/examples/plugins/.cargo/config.toml b/examples/plugins/.cargo/config.toml new file mode 100644 index 0000000..a6b014e --- /dev/null +++ b/examples/plugins/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target-dir = "target" diff --git a/examples/plugins/Cargo.toml b/examples/plugins/Cargo.toml new file mode 100644 index 0000000..b88673c --- /dev/null +++ b/examples/plugins/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "examples-plugins" +edition.workspace = true +authors.workspace = true +homepage.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[lib] +crate-type = ['cdylib'] + +[dependencies] +silent = { path = "../../silent" } diff --git a/examples/plugins/src/lib.rs b/examples/plugins/src/lib.rs new file mode 100644 index 0000000..ecb2f36 --- /dev/null +++ b/examples/plugins/src/lib.rs @@ -0,0 +1,6 @@ +use silent::prelude::{HandlerAppend, Route}; +#[no_mangle] +#[allow(improper_ctypes_definitions)] +extern "C" fn get_route() -> Route { + Route::new("hello").get(|_req| async { Ok("hello world") }) +} diff --git a/silent/src/ws/route.rs b/silent/src/ws/route.rs new file mode 100644 index 0000000..25c823d --- /dev/null +++ b/silent/src/ws/route.rs @@ -0,0 +1,115 @@ +use crate::prelude::{HandlerGetter, Message, Result, WebSocketParts}; +use crate::route::Route; +use crate::ws::{HandlerWrapperWebSocket, WebSocketHandler}; +use http::Method; +use std::future::Future; +use std::sync::Arc; +use tokio::sync::mpsc::UnboundedSender; +use tokio::sync::RwLock; +use tokio_tungstenite::tungstenite::protocol::WebSocketConfig; + +pub trait WSHandlerAppend< + FnOnConnect, + FnOnConnectFut, + FnOnSend, + FnOnSendFut, + FnOnReceive, + FnOnReceiveFut, + FnOnClose, + FnOnCloseFut, +>: HandlerGetter where + FnOnConnect: Fn(Arc>, UnboundedSender) -> FnOnConnectFut + + Send + + Sync + + 'static, + FnOnConnectFut: Future> + Send + Sync + 'static, + FnOnSend: Fn(Message, Arc>) -> FnOnSendFut + Send + Sync + 'static, + FnOnSendFut: Future> + Send + Sync + 'static, + FnOnReceive: Fn(Message, Arc>) -> FnOnReceiveFut + Send + Sync + 'static, + FnOnReceiveFut: Future> + Send + Sync + 'static, + FnOnClose: Fn(Arc>) -> FnOnCloseFut + Send + Sync + 'static, + FnOnCloseFut: Future + Send + Sync + 'static, +{ + fn ws( + self, + config: Option, + handler: WebSocketHandler< + FnOnConnect, + FnOnConnectFut, + FnOnSend, + FnOnSendFut, + FnOnReceive, + FnOnReceiveFut, + FnOnClose, + FnOnCloseFut, + >, + ) -> Self; + fn ws_handler_append( + &mut self, + handler: HandlerWrapperWebSocket< + FnOnConnect, + FnOnConnectFut, + FnOnSend, + FnOnSendFut, + FnOnReceive, + FnOnReceiveFut, + FnOnClose, + FnOnCloseFut, + >, + ) { + let handler = Arc::new(handler); + self.get_handler_mut().insert(Method::GET, handler); + } +} +impl< + FnOnConnect, + FnOnConnectFut, + FnOnSend, + FnOnSendFut, + FnOnReceive, + FnOnReceiveFut, + FnOnClose, + FnOnCloseFut, + > + WSHandlerAppend< + FnOnConnect, + FnOnConnectFut, + FnOnSend, + FnOnSendFut, + FnOnReceive, + FnOnReceiveFut, + FnOnClose, + FnOnCloseFut, + > for Route +where + FnOnConnect: Fn(Arc>, UnboundedSender) -> FnOnConnectFut + + Send + + Sync + + 'static, + FnOnConnectFut: Future> + Send + Sync + 'static, + FnOnSend: Fn(Message, Arc>) -> FnOnSendFut + Send + Sync + 'static, + FnOnSendFut: Future> + Send + Sync + 'static, + FnOnReceive: Fn(Message, Arc>) -> FnOnReceiveFut + Send + Sync + 'static, + FnOnReceiveFut: Future> + Send + Sync + 'static, + FnOnClose: Fn(Arc>) -> FnOnCloseFut + Send + Sync + 'static, + FnOnCloseFut: Future + Send + Sync + 'static, +{ + fn ws( + mut self, + config: Option, + handler: WebSocketHandler< + FnOnConnect, + FnOnConnectFut, + FnOnSend, + FnOnSendFut, + FnOnReceive, + FnOnReceiveFut, + FnOnClose, + FnOnCloseFut, + >, + ) -> Self { + let handler = HandlerWrapperWebSocket::new(config).set_handler(handler); + self.ws_handler_append(handler); + self + } +} From eb428439ea21ea4bd668338492ca6d3bdd7eb486 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Sat, 14 Sep 2024 13:46:46 +0800 Subject: [PATCH 35/49] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4prelude?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- silent/src/lib.rs | 53 ++----------------------------------------- silent/src/prelude.rs | 44 +++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 51 deletions(-) create mode 100644 silent/src/prelude.rs diff --git a/silent/src/lib.rs b/silent/src/lib.rs index a6a1b90..6f40e86 100644 --- a/silent/src/lib.rs +++ b/silent/src/lib.rs @@ -7,6 +7,7 @@ mod grpc; mod handler; mod log; pub mod middleware; +pub mod prelude; mod route; #[cfg(feature = "scheduler")] mod scheduler; @@ -29,10 +30,7 @@ mod ws; #[allow(clippy::single_component_path_imports)] use multer; -pub use crate::configs::Configs; -#[cfg(feature = "cookie")] -pub use crate::core::cookie::CookieExt; -pub use crate::core::{next::Next, request::Request, response::Response}; +pub use crate::core::{request::Request, response::Response}; #[cfg(feature = "grpc")] pub use crate::grpc::{GrpcHandler, GrpcRegister}; pub use crate::middleware::{middlewares, MiddleWareHandler}; @@ -44,50 +42,3 @@ pub use headers; pub use hyper::{header, Method, StatusCode}; #[cfg(feature = "scheduler")] pub use scheduler::{ProcessTime, Scheduler, SchedulerExt, Task}; - -pub mod prelude { - pub use crate::configs::Configs; - #[cfg(feature = "cookie")] - pub use crate::core::cookie::CookieExt; - #[cfg(feature = "multipart")] - pub use crate::core::form::{FilePart, FormData}; - pub use crate::core::{ - next::Next, path_param::PathParam, req_body::ReqBody, request::Request, res_body::full, - res_body::stream_body, res_body::ResBody, response::Response, - }; - pub use crate::error::{SilentError, SilentResult as Result}; - #[cfg(feature = "grpc")] - pub use crate::grpc::{GrpcHandler, GrpcRegister}; - #[cfg(feature = "static")] - pub use crate::handler::static_handler; - pub use crate::handler::Handler; - pub use crate::handler::HandlerWrapper; - pub use crate::log::*; - pub use crate::middleware::MiddleWareHandler; - #[cfg(feature = "upgrade")] - pub use crate::route::handler_append::WSHandlerAppend; - pub use crate::route::handler_append::{HandlerAppend, HandlerGetter}; - pub use crate::route::{RootRoute, Route, RouteService, RouterAdapt}; - #[cfg(feature = "scheduler")] - pub use crate::scheduler::{SchedulerExt, Task}; - #[cfg(feature = "security")] - pub use crate::security::{argon2, pbkdf2}; - #[cfg(feature = "server")] - pub use crate::service::Server; - #[cfg(feature = "sse")] - pub use crate::sse::{sse_reply, SSEEvent}; - #[cfg(feature = "template")] - pub use crate::templates::*; - #[cfg(feature = "upgrade")] - pub use crate::ws::{ - FnOnClose, FnOnConnect, FnOnNoneResultFut, FnOnReceive, FnOnSend, FnOnSendFut, - }; - #[cfg(feature = "upgrade")] - pub use crate::ws::{Message, WebSocket, WebSocketHandler, WebSocketParts}; - #[cfg(feature = "session")] - pub use async_session::{Session, SessionStore}; - #[cfg(feature = "cookie")] - pub use cookie::{time as CookieTime, Cookie}; - pub use headers; - pub use hyper::{header, upgrade, Method, StatusCode}; -} diff --git a/silent/src/prelude.rs b/silent/src/prelude.rs new file mode 100644 index 0000000..c44f7ba --- /dev/null +++ b/silent/src/prelude.rs @@ -0,0 +1,44 @@ +pub use crate::configs::Configs; +#[cfg(feature = "cookie")] +pub use crate::core::cookie::CookieExt; +#[cfg(feature = "multipart")] +pub use crate::core::form::{FilePart, FormData}; +pub use crate::core::{ + next::Next, path_param::PathParam, req_body::ReqBody, request::Request, res_body::full, + res_body::stream_body, res_body::ResBody, response::Response, +}; +pub use crate::error::{SilentError, SilentResult as Result}; +#[cfg(feature = "grpc")] +pub use crate::grpc::{GrpcHandler, GrpcRegister}; +#[cfg(feature = "static")] +pub use crate::handler::static_handler; +pub use crate::handler::Handler; +pub use crate::handler::HandlerWrapper; +pub use crate::log::*; +pub use crate::middleware::MiddleWareHandler; +#[cfg(feature = "upgrade")] +pub use crate::route::handler_append::WSHandlerAppend; +pub use crate::route::handler_append::{HandlerAppend, HandlerGetter}; +pub use crate::route::{RootRoute, Route, RouteService, RouterAdapt}; +#[cfg(feature = "scheduler")] +pub use crate::scheduler::{SchedulerExt, Task}; +#[cfg(feature = "security")] +pub use crate::security::{argon2, pbkdf2}; +#[cfg(feature = "server")] +pub use crate::service::Server; +#[cfg(feature = "sse")] +pub use crate::sse::{sse_reply, SSEEvent}; +#[cfg(feature = "template")] +pub use crate::templates::*; +#[cfg(feature = "upgrade")] +pub use crate::ws::{ + FnOnClose, FnOnConnect, FnOnNoneResultFut, FnOnReceive, FnOnSend, FnOnSendFut, +}; +#[cfg(feature = "upgrade")] +pub use crate::ws::{Message, WebSocket, WebSocketHandler, WebSocketParts}; +#[cfg(feature = "session")] +pub use async_session::{Session, SessionStore}; +#[cfg(feature = "cookie")] +pub use cookie::{time as CookieTime, Cookie}; +pub use headers; +pub use hyper::{header, upgrade, Method, StatusCode}; From 66905273dbd035831c23f42846209591a8a77231 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Sat, 14 Sep 2024 13:51:30 +0800 Subject: [PATCH 36/49] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4prelude?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- silent/src/core/request.rs | 2 +- silent/src/lib.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/silent/src/core/request.rs b/silent/src/core/request.rs index 1ca2ac7..8585859 100644 --- a/silent/src/core/request.rs +++ b/silent/src/core/request.rs @@ -5,7 +5,7 @@ use crate::core::req_body::ReqBody; #[cfg(feature = "multipart")] use crate::core::serde::from_str_multi_val; use crate::header::CONTENT_TYPE; -use crate::{Configs, Result, SilentError}; +use crate::{Result, SilentError}; use bytes::Bytes; use http::request::Parts; use http::{Extensions, HeaderMap, HeaderValue, Method, Uri, Version}; diff --git a/silent/src/lib.rs b/silent/src/lib.rs index 6f40e86..e6a452e 100644 --- a/silent/src/lib.rs +++ b/silent/src/lib.rs @@ -30,7 +30,10 @@ mod ws; #[allow(clippy::single_component_path_imports)] use multer; -pub use crate::core::{request::Request, response::Response}; +pub use crate::configs::Configs; +#[cfg(feature = "cookie")] +pub use crate::core::cookie::CookieExt; +pub use crate::core::{next::Next, request::Request, response::Response}; #[cfg(feature = "grpc")] pub use crate::grpc::{GrpcHandler, GrpcRegister}; pub use crate::middleware::{middlewares, MiddleWareHandler}; From 577286d745721e4b4d0a0b7a94ef64c7760f52b9 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Sat, 14 Sep 2024 13:51:56 +0800 Subject: [PATCH 37/49] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4prelude?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- silent/src/core/request.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/silent/src/core/request.rs b/silent/src/core/request.rs index 8585859..1ca2ac7 100644 --- a/silent/src/core/request.rs +++ b/silent/src/core/request.rs @@ -5,7 +5,7 @@ use crate::core::req_body::ReqBody; #[cfg(feature = "multipart")] use crate::core::serde::from_str_multi_val; use crate::header::CONTENT_TYPE; -use crate::{Result, SilentError}; +use crate::{Configs, Result, SilentError}; use bytes::Bytes; use http::request::Parts; use http::{Extensions, HeaderMap, HeaderValue, Method, Uri, Version}; From fcdd78cc52c2f70cbe752a40f2f47c5eb8cd9297 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Sat, 14 Sep 2024 14:37:48 +0800 Subject: [PATCH 38/49] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E4=B8=AD=E9=97=B4=E4=BB=B6=E5=8F=8A=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/timeout/Cargo.lock | 7 ++++ examples/timeout/Cargo.toml | 10 ++++++ examples/timeout/src/main.rs | 16 +++++++++ silent/src/core/adapt.rs | 1 + silent/src/middleware/middlewares/mod.rs | 4 +++ silent/src/middleware/middlewares/timeout.rs | 38 ++++++++++++++++++++ 6 files changed, 76 insertions(+) create mode 100644 examples/timeout/Cargo.lock create mode 100644 examples/timeout/Cargo.toml create mode 100644 examples/timeout/src/main.rs create mode 100644 silent/src/middleware/middlewares/timeout.rs diff --git a/examples/timeout/Cargo.lock b/examples/timeout/Cargo.lock new file mode 100644 index 0000000..e7cecae --- /dev/null +++ b/examples/timeout/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "hello-world" +version = "0.1.0" diff --git a/examples/timeout/Cargo.toml b/examples/timeout/Cargo.toml new file mode 100644 index 0000000..cc8abff --- /dev/null +++ b/examples/timeout/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "example-timeout" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +silent = { path = "../../silent" } +tokio = { version = "1", features = ["time"] } diff --git a/examples/timeout/src/main.rs b/examples/timeout/src/main.rs new file mode 100644 index 0000000..259204d --- /dev/null +++ b/examples/timeout/src/main.rs @@ -0,0 +1,16 @@ +use silent::middlewares::Timeout; +use silent::prelude::*; +use std::time::Duration; + +fn main() { + logger::fmt().with_max_level(Level::INFO).init(); + let route = Route::new("") + .hook(Timeout::new(Duration::from_secs(1))) + .get(|_req| async { + println!("sleeping for 2 seconds"); + tokio::time::sleep(Duration::from_secs(2)).await; + println!("done sleeping"); + Ok("hello world") + }); + Server::new().run(route); +} diff --git a/silent/src/core/adapt.rs b/silent/src/core/adapt.rs index d392405..de805d3 100644 --- a/silent/src/core/adapt.rs +++ b/silent/src/core/adapt.rs @@ -50,6 +50,7 @@ impl RequestAdapt for HyperRequest { #[cfg(feature = "cookie")] let cookies = get_cookie(&self).unwrap_or_default(); let (parts, body) = self.into_parts(); + #[allow(unused_mut)] let mut req = Request::from_parts(parts, body); #[cfg(feature = "cookie")] req.extensions_mut().insert(cookies); diff --git a/silent/src/middleware/middlewares/mod.rs b/silent/src/middleware/middlewares/mod.rs index ec7060a..a667b83 100644 --- a/silent/src/middleware/middlewares/mod.rs +++ b/silent/src/middleware/middlewares/mod.rs @@ -1,5 +1,9 @@ mod cors; mod exception_handler; +mod request_time_logger; +mod timeout; pub use cors::{Cors, CorsType}; pub use exception_handler::ExceptionHandler; +pub use request_time_logger::RequestTimeLogger; +pub use timeout::Timeout; diff --git a/silent/src/middleware/middlewares/timeout.rs b/silent/src/middleware/middlewares/timeout.rs new file mode 100644 index 0000000..5de0ecc --- /dev/null +++ b/silent/src/middleware/middlewares/timeout.rs @@ -0,0 +1,38 @@ +use crate::{Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError}; +use async_trait::async_trait; +use http::StatusCode; +use std::time::Duration; + +/// ExceptionHandler 中间件 +/// ```rust +/// use silent::prelude::*; +/// use silent::middlewares::{RequestTimeLogger}; +/// // Define a request time logger middleware +/// let _ = RequestTimeLogger::new(); +#[derive(Default, Clone)] +pub struct Timeout { + timeout: Duration, +} + +impl Timeout { + pub fn new(timeout: Duration) -> Self { + Self { timeout } + } +} + +#[async_trait] +impl MiddleWareHandler for Timeout { + async fn handle(&self, req: Request, next: &Next) -> Result { + match tokio::time::timeout(self.timeout, next.call(req)) + .await + .map_err(|_| { + SilentError::business_error( + StatusCode::REQUEST_TIMEOUT, + "Request timed out".to_string(), + ) + }) { + Ok(res) => res, + Err(err) => Err(err), + } + } +} From 4f022a2979391845b466374d315b81333b93a9f8 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Sat, 14 Sep 2024 16:45:13 +0800 Subject: [PATCH 39/49] =?UTF-8?q?feat:=20websocket=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/websocket-chat/src/main.rs | 4 +- examples/websocket/src/main.rs | 8 +- silent/src/prelude.rs | 4 +- silent/src/route/handler_append.rs | 117 ---------------------------- silent/src/ws/mod.rs | 2 + 5 files changed, 10 insertions(+), 125 deletions(-) diff --git a/examples/websocket-chat/src/main.rs b/examples/websocket-chat/src/main.rs index 9a9f04e..e505fc0 100644 --- a/examples/websocket-chat/src/main.rs +++ b/examples/websocket-chat/src/main.rs @@ -82,8 +82,8 @@ async fn on_close(parts: Arc>) { ONLINE_USERS.write().await.remove(my_id); } -async fn index<'a>(_res: Request) -> Result<&'a str> { - Ok(INDEX_HTML) +async fn index(_res: Request) -> Result { + Ok(Response::html(INDEX_HTML)) } static INDEX_HTML: &str = r#" diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs index 4697748..3cf63ac 100644 --- a/examples/websocket/src/main.rs +++ b/examples/websocket/src/main.rs @@ -15,8 +15,9 @@ fn main() { Server::new().run(route); } -async fn show_form(_req: Request) -> Result<&'static str> { - Ok(r#" +async fn show_form(_req: Request) -> Result { + Ok(Response::html( + r#" WS @@ -38,5 +39,6 @@ async fn show_form(_req: Request) -> Result<&'static str> { -"#) +"#, + )) } diff --git a/silent/src/prelude.rs b/silent/src/prelude.rs index c44f7ba..0f0a1e9 100644 --- a/silent/src/prelude.rs +++ b/silent/src/prelude.rs @@ -16,8 +16,6 @@ pub use crate::handler::Handler; pub use crate::handler::HandlerWrapper; pub use crate::log::*; pub use crate::middleware::MiddleWareHandler; -#[cfg(feature = "upgrade")] -pub use crate::route::handler_append::WSHandlerAppend; pub use crate::route::handler_append::{HandlerAppend, HandlerGetter}; pub use crate::route::{RootRoute, Route, RouteService, RouterAdapt}; #[cfg(feature = "scheduler")] @@ -32,7 +30,7 @@ pub use crate::sse::{sse_reply, SSEEvent}; pub use crate::templates::*; #[cfg(feature = "upgrade")] pub use crate::ws::{ - FnOnClose, FnOnConnect, FnOnNoneResultFut, FnOnReceive, FnOnSend, FnOnSendFut, + FnOnClose, FnOnConnect, FnOnNoneResultFut, FnOnReceive, FnOnSend, FnOnSendFut, WSHandlerAppend, }; #[cfg(feature = "upgrade")] pub use crate::ws::{Message, WebSocket, WebSocketHandler, WebSocketParts}; diff --git a/silent/src/route/handler_append.rs b/silent/src/route/handler_append.rs index d73302e..bee78a3 100644 --- a/silent/src/route/handler_append.rs +++ b/silent/src/route/handler_append.rs @@ -1,16 +1,8 @@ use super::Route; -#[cfg(feature = "upgrade")] -use crate::ws::{HandlerWrapperWebSocket, Message, WebSocketHandler, WebSocketParts}; use crate::{Handler, HandlerWrapper, Method, Request, Response, Result}; use std::collections::HashMap; use std::future::Future; use std::sync::Arc; -#[cfg(feature = "upgrade")] -use tokio::sync::mpsc::UnboundedSender; -#[cfg(feature = "upgrade")] -use tokio::sync::RwLock; -#[cfg(feature = "upgrade")] -use tokio_tungstenite::tungstenite::protocol::WebSocketConfig; pub trait HandlerGetter { fn get_handler_mut(&mut self) -> &mut HashMap>; @@ -36,61 +28,6 @@ where } } -#[cfg(feature = "upgrade")] -pub trait WSHandlerAppend< - FnOnConnect, - FnOnConnectFut, - FnOnSend, - FnOnSendFut, - FnOnReceive, - FnOnReceiveFut, - FnOnClose, - FnOnCloseFut, ->: HandlerGetter where - FnOnConnect: Fn(Arc>, UnboundedSender) -> FnOnConnectFut - + Send - + Sync - + 'static, - FnOnConnectFut: Future> + Send + Sync + 'static, - FnOnSend: Fn(Message, Arc>) -> FnOnSendFut + Send + Sync + 'static, - FnOnSendFut: Future> + Send + Sync + 'static, - FnOnReceive: Fn(Message, Arc>) -> FnOnReceiveFut + Send + Sync + 'static, - FnOnReceiveFut: Future> + Send + Sync + 'static, - FnOnClose: Fn(Arc>) -> FnOnCloseFut + Send + Sync + 'static, - FnOnCloseFut: Future + Send + Sync + 'static, -{ - fn ws( - self, - config: Option, - handler: WebSocketHandler< - FnOnConnect, - FnOnConnectFut, - FnOnSend, - FnOnSendFut, - FnOnReceive, - FnOnReceiveFut, - FnOnClose, - FnOnCloseFut, - >, - ) -> Self; - fn ws_handler_append( - &mut self, - handler: HandlerWrapperWebSocket< - FnOnConnect, - FnOnConnectFut, - FnOnSend, - FnOnSendFut, - FnOnReceive, - FnOnReceiveFut, - FnOnClose, - FnOnCloseFut, - >, - ) { - let handler = Arc::new(handler); - self.get_handler_mut().insert(Method::GET, handler); - } -} - impl HandlerGetter for Route { fn get_handler_mut(&mut self) -> &mut HashMap> { if self.path == self.create_path { @@ -154,57 +91,3 @@ where self } } - -#[cfg(feature = "upgrade")] -impl< - FnOnConnect, - FnOnConnectFut, - FnOnSend, - FnOnSendFut, - FnOnReceive, - FnOnReceiveFut, - FnOnClose, - FnOnCloseFut, - > - WSHandlerAppend< - FnOnConnect, - FnOnConnectFut, - FnOnSend, - FnOnSendFut, - FnOnReceive, - FnOnReceiveFut, - FnOnClose, - FnOnCloseFut, - > for Route -where - FnOnConnect: Fn(Arc>, UnboundedSender) -> FnOnConnectFut - + Send - + Sync - + 'static, - FnOnConnectFut: Future> + Send + Sync + 'static, - FnOnSend: Fn(Message, Arc>) -> FnOnSendFut + Send + Sync + 'static, - FnOnSendFut: Future> + Send + Sync + 'static, - FnOnReceive: Fn(Message, Arc>) -> FnOnReceiveFut + Send + Sync + 'static, - FnOnReceiveFut: Future> + Send + Sync + 'static, - FnOnClose: Fn(Arc>) -> FnOnCloseFut + Send + Sync + 'static, - FnOnCloseFut: Future + Send + Sync + 'static, -{ - fn ws( - mut self, - config: Option, - handler: WebSocketHandler< - FnOnConnect, - FnOnConnectFut, - FnOnSend, - FnOnSendFut, - FnOnReceive, - FnOnReceiveFut, - FnOnClose, - FnOnCloseFut, - >, - ) -> Self { - let handler = HandlerWrapperWebSocket::new(config).set_handler(handler); - self.ws_handler_append(handler); - self - } -} diff --git a/silent/src/ws/mod.rs b/silent/src/ws/mod.rs index d99e922..ccd4ac4 100644 --- a/silent/src/ws/mod.rs +++ b/silent/src/ws/mod.rs @@ -1,6 +1,7 @@ mod handler; mod handler_wrapper_websocket; mod message; +mod route; mod types; mod upgrade; mod websocket; @@ -8,6 +9,7 @@ mod websocket_handler; pub use handler_wrapper_websocket::HandlerWrapperWebSocket; pub use message::Message; +pub use route::WSHandlerAppend; pub use types::{FnOnClose, FnOnConnect, FnOnNoneResultFut, FnOnReceive, FnOnSend, FnOnSendFut}; pub use upgrade::WebSocketParts; pub use websocket::WebSocket; From 3c67c3a3eed92afac322a94fe9d40a9321f7a924 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Fri, 11 Oct 2024 16:48:18 +0800 Subject: [PATCH 40/49] chore: deps update --- examples/form/Cargo.toml | 2 +- examples/grpc/Cargo.toml | 6 +++--- examples/grpc_streaming/Cargo.toml | 6 +++--- examples/sse-chat/Cargo.toml | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/form/Cargo.toml b/examples/form/Cargo.toml index e450f80..cf8e409 100644 --- a/examples/form/Cargo.toml +++ b/examples/form/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] silent = { path = "../../silent" } -serde = { version = "1.0.209", features = ["derive"] } +serde = { version = "1.0.210", features = ["derive"] } diff --git a/examples/grpc/Cargo.toml b/examples/grpc/Cargo.toml index b3775f5..bfd86fe 100644 --- a/examples/grpc/Cargo.toml +++ b/examples/grpc/Cargo.toml @@ -13,11 +13,11 @@ name = "example-grpc-client" path = "src/client.rs" [dependencies] -tonic = { version = "0.12.2" } +tonic = { version = "0.12.3" } prost = "0.13" tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] } silent = { path = "../../silent", features = ["grpc"] } -async-trait = "0.1.82" +async-trait = "0.1.83" [build-dependencies] -tonic-build = { version = "0.12.2" } +tonic-build = { version = "0.12.3" } diff --git a/examples/grpc_streaming/Cargo.toml b/examples/grpc_streaming/Cargo.toml index b5a838f..57e75b3 100644 --- a/examples/grpc_streaming/Cargo.toml +++ b/examples/grpc_streaming/Cargo.toml @@ -13,13 +13,13 @@ name = "example-grpc-client" path = "src/client.rs" [dependencies] -tonic = { version = "0.12.2" } +tonic = { version = "0.12.3" } prost = "0.13" tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] } silent = { path = "../../silent", features = ["grpc"] } -async-trait = "0.1.82" +async-trait = "0.1.83" tokio-stream = "0.1.16" h2 = "0.4.6" [build-dependencies] -tonic-build = { version = "0.12.2" } +tonic-build = { version = "0.12.3" } diff --git a/examples/sse-chat/Cargo.toml b/examples/sse-chat/Cargo.toml index 39d053d..ff8dfbc 100644 --- a/examples/sse-chat/Cargo.toml +++ b/examples/sse-chat/Cargo.toml @@ -11,4 +11,4 @@ once_cell = "1" parking_lot = "0.12" tokio = { version = "1", features = ["macros"] } tokio-stream = { version = "0.1", features = ["net"] } -serde = { version = "1.0.209", features = ["derive"] } +serde = { version = "1.0.210", features = ["derive"] } From c2ce99dc83cbbfaae6dc1c354cd537d8623728a6 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Sat, 12 Oct 2024 09:00:00 +0800 Subject: [PATCH 41/49] =?UTF-8?q?chore:=20session=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/session/src/main.rs | 18 ++++++-------- silent/src/prelude.rs | 2 ++ silent/src/session/mod.rs | 4 +++ silent/src/session/session_ext.rs | 41 +++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 silent/src/session/session_ext.rs diff --git a/examples/session/src/main.rs b/examples/session/src/main.rs index b248d99..48d5e65 100644 --- a/examples/session/src/main.rs +++ b/examples/session/src/main.rs @@ -3,17 +3,13 @@ use silent::prelude::*; fn main() { logger::fmt().with_max_level(Level::INFO).init(); let route = Route::new("").get(|mut req: Request| async move { - let session = req.extensions_mut().get_mut::(); - match session { - None => {} - Some(session) => { - if let Some(state) = session.clone().get::("state") { - session.insert("state", state + 1)?; - return Ok(state.to_string()); - } else { - session.insert("state", 1)?; - } - } + let state = req.session::<&str, i64>("state"); + let sessions_mut = req.sessions_mut(); + if let Some(state) = state { + sessions_mut.insert("state", state + 1)?; + return Ok(state.to_string()); + } else { + sessions_mut.insert("state", 1)?; } Ok("hello world".to_string()) }); diff --git a/silent/src/prelude.rs b/silent/src/prelude.rs index 0f0a1e9..d7cc104 100644 --- a/silent/src/prelude.rs +++ b/silent/src/prelude.rs @@ -24,6 +24,8 @@ pub use crate::scheduler::{SchedulerExt, Task}; pub use crate::security::{argon2, pbkdf2}; #[cfg(feature = "server")] pub use crate::service::Server; +#[cfg(feature = "session")] +pub use crate::session::session_ext::SessionExt; #[cfg(feature = "sse")] pub use crate::sse::{sse_reply, SSEEvent}; #[cfg(feature = "template")] diff --git a/silent/src/session/mod.rs b/silent/src/session/mod.rs index d5f498b..380722f 100644 --- a/silent/src/session/mod.rs +++ b/silent/src/session/mod.rs @@ -1,3 +1,5 @@ +pub mod session_ext; + use crate::{ CookieExt, Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError, StatusCode, }; @@ -41,6 +43,7 @@ where let cookie = cookies.get("silent-web-session"); let session_store = self.session_store.read().await; let session = if cookie.is_none() { + req.extensions_mut().insert(cookies.clone()); Session::new() } else { let cookie = cookie.unwrap(); @@ -58,6 +61,7 @@ where req.extensions_mut().insert(session.clone()); let mut res = next.call(req).await?; res.extensions_mut().insert(session.clone()); + res.extensions_mut().insert(cookies); let cookie_value = session_store.store_session(session).await.map_err(|e| { SilentError::business_error( StatusCode::INTERNAL_SERVER_ERROR, diff --git a/silent/src/session/session_ext.rs b/silent/src/session/session_ext.rs new file mode 100644 index 0000000..3785ef9 --- /dev/null +++ b/silent/src/session/session_ext.rs @@ -0,0 +1,41 @@ +use crate::{Request, Response}; +use async_session::Session; +use http_body::Body; +use serde::de::DeserializeOwned; + +pub trait SessionExt { + /// Get `Session` reference. + fn sessions(&self) -> Session; + /// Get `Session` mutable reference. + fn sessions_mut(&mut self) -> &mut Session; + /// Get `Session` from session. + fn session, V: DeserializeOwned>(&self, name: K) -> Option; +} + +impl SessionExt for Request { + fn sessions(&self) -> Session { + self.extensions().get().cloned().unwrap_or_default() + } + + fn sessions_mut(&mut self) -> &mut Session { + self.extensions_mut().get_mut().unwrap() + } + + fn session, V: DeserializeOwned>(&self, name: K) -> Option { + self.sessions().get(name.as_ref()) + } +} + +impl SessionExt for Response { + fn sessions(&self) -> Session { + self.extensions().get().cloned().unwrap_or_default() + } + + fn sessions_mut(&mut self) -> &mut Session { + self.extensions_mut().get_mut().unwrap() + } + + fn session, V: DeserializeOwned>(&self, name: K) -> Option { + self.sessions().get(name.as_ref()) + } +} From 1e050c383cb3d81ec737edf698cdc24781fc4524 Mon Sep 17 00:00:00 2001 From: Hubert Shelley <46239302+hubertshelley@users.noreply.github.com> Date: Mon, 14 Oct 2024 16:25:01 +0800 Subject: [PATCH 42/49] =?UTF-8?q?=E5=90=88=E5=B9=B6=E6=8B=89=E5=8F=96?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=20#67?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: cookie优化调整 * fix: session异常修复 --- examples/session/src/main.rs | 6 +- .../{core/cookie.rs => cookie/cookie_ext.rs} | 0 silent/src/cookie/middleware.rs | 48 +++++++++++ silent/src/cookie/mod.rs | 2 + silent/src/core/mod.rs | 2 - silent/src/lib.rs | 4 +- silent/src/prelude.rs | 4 +- silent/src/route/mod.rs | 14 --- silent/src/route/root.rs | 8 +- silent/src/service/mod.rs | 2 + silent/src/session/middleware.rs | 86 +++++++++++++++++++ silent/src/session/mod.rs | 79 +---------------- silent/src/session/session_ext.rs | 6 +- 13 files changed, 158 insertions(+), 103 deletions(-) rename silent/src/{core/cookie.rs => cookie/cookie_ext.rs} (100%) create mode 100644 silent/src/cookie/middleware.rs create mode 100644 silent/src/cookie/mod.rs create mode 100644 silent/src/session/middleware.rs diff --git a/examples/session/src/main.rs b/examples/session/src/main.rs index 48d5e65..4ee6f37 100644 --- a/examples/session/src/main.rs +++ b/examples/session/src/main.rs @@ -3,7 +3,7 @@ use silent::prelude::*; fn main() { logger::fmt().with_max_level(Level::INFO).init(); let route = Route::new("").get(|mut req: Request| async move { - let state = req.session::<&str, i64>("state"); + let state = req.session::("state"); let sessions_mut = req.sessions_mut(); if let Some(state) = state { sessions_mut.insert("state", state + 1)?; @@ -13,5 +13,7 @@ fn main() { } Ok("hello world".to_string()) }); - Server::new().run(route); + Server::new() + .bind("127.0.0.1:8000".parse().unwrap()) + .run(route); } diff --git a/silent/src/core/cookie.rs b/silent/src/cookie/cookie_ext.rs similarity index 100% rename from silent/src/core/cookie.rs rename to silent/src/cookie/cookie_ext.rs diff --git a/silent/src/cookie/middleware.rs b/silent/src/cookie/middleware.rs new file mode 100644 index 0000000..b0bbcc1 --- /dev/null +++ b/silent/src/cookie/middleware.rs @@ -0,0 +1,48 @@ +use crate::{Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError}; +use async_trait::async_trait; +use cookie::{Cookie, CookieJar}; +use http::{header, StatusCode}; + +#[derive(Debug, Default)] +pub struct CookieMiddleware {} + +impl CookieMiddleware { + pub(crate) fn new() -> Self { + Self::default() + } +} + +#[async_trait] +impl MiddleWareHandler for CookieMiddleware { + async fn handle(&self, mut req: Request, next: &Next) -> Result { + let mut jar = CookieJar::new(); + if let Some(cookies) = req.headers().get(header::COOKIE) { + for cookie_str in cookies + .to_str() + .map_err(|e| { + SilentError::business_error( + StatusCode::BAD_REQUEST, + format!("Failed to parse cookie: {}", e), + ) + })? + .split(';') + .map(|s| s.trim()) + { + if let Ok(cookie) = Cookie::parse_encoded(cookie_str).map(|c| c.into_owned()) { + jar.add_original(cookie); + } + } + } + req.extensions_mut().insert(jar.clone()); + let mut res = next.call(req).await?; + if let Some(cookie_jar) = res.extensions().get::() { + for cookie in cookie_jar.delta().cloned() { + jar.add(cookie) + } + res.extensions_mut().insert(jar); + } else { + res.extensions_mut().insert(jar); + }; + Ok(res) + } +} diff --git a/silent/src/cookie/mod.rs b/silent/src/cookie/mod.rs new file mode 100644 index 0000000..e5a089c --- /dev/null +++ b/silent/src/cookie/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod cookie_ext; +pub(crate) mod middleware; diff --git a/silent/src/core/mod.rs b/silent/src/core/mod.rs index 56a8e38..447e661 100644 --- a/silent/src/core/mod.rs +++ b/silent/src/core/mod.rs @@ -1,7 +1,5 @@ pub mod adapt; -#[cfg(feature = "cookie")] -pub(crate) mod cookie; #[cfg(feature = "multipart")] pub(crate) mod form; pub(crate) mod next; diff --git a/silent/src/lib.rs b/silent/src/lib.rs index e6a452e..ee65dfe 100644 --- a/silent/src/lib.rs +++ b/silent/src/lib.rs @@ -1,4 +1,6 @@ mod configs; +#[cfg(feature = "cookie")] +mod cookie; /// The `silent` library. mod core; mod error; @@ -32,7 +34,7 @@ use multer; pub use crate::configs::Configs; #[cfg(feature = "cookie")] -pub use crate::core::cookie::CookieExt; +pub use crate::cookie::cookie_ext::CookieExt; pub use crate::core::{next::Next, request::Request, response::Response}; #[cfg(feature = "grpc")] pub use crate::grpc::{GrpcHandler, GrpcRegister}; diff --git a/silent/src/prelude.rs b/silent/src/prelude.rs index d7cc104..e6b53ca 100644 --- a/silent/src/prelude.rs +++ b/silent/src/prelude.rs @@ -1,6 +1,6 @@ pub use crate::configs::Configs; #[cfg(feature = "cookie")] -pub use crate::core::cookie::CookieExt; +pub use crate::cookie::cookie_ext::CookieExt; #[cfg(feature = "multipart")] pub use crate::core::form::{FilePart, FormData}; pub use crate::core::{ @@ -39,6 +39,6 @@ pub use crate::ws::{Message, WebSocket, WebSocketHandler, WebSocketParts}; #[cfg(feature = "session")] pub use async_session::{Session, SessionStore}; #[cfg(feature = "cookie")] -pub use cookie::{time as CookieTime, Cookie}; +pub use cookie::{time as CookieTime, Cookie, CookieJar, Key}; pub use headers; pub use hyper::{header, upgrade, Method, StatusCode}; diff --git a/silent/src/route/mod.rs b/silent/src/route/mod.rs index dc40ccf..2460fc3 100644 --- a/silent/src/route/mod.rs +++ b/silent/src/route/mod.rs @@ -1,5 +1,3 @@ -#[cfg(feature = "session")] -use async_session::Session; use async_trait::async_trait; use http::StatusCode; pub use root::RootRoute; @@ -14,8 +12,6 @@ use crate::handler::Handler; use crate::middleware::MiddleWareHandler; #[cfg(feature = "static")] use crate::prelude::HandlerGetter; -#[cfg(feature = "cookie")] -use crate::CookieExt; use crate::{Method, Next, Request, Response, SilentError}; pub(crate) mod handler_append; @@ -191,16 +187,6 @@ impl Handler for Route { Some(handler) => { let mut pre_res = Response::empty(); pre_res.configs = configs; - #[cfg(feature = "cookie")] - { - pre_res.extensions_mut().insert(req.cookies().clone()); - } - #[cfg(feature = "session")] - let session = req.extensions().get::().cloned(); - #[cfg(feature = "session")] - if let Some(session) = session { - pre_res.extensions.insert(session); - } let mut active_middlewares = vec![]; for middleware in self.middlewares.iter().cloned() { if middleware.match_req(&req).await { diff --git a/silent/src/route/root.rs b/silent/src/route/root.rs index 9b0981e..993ac82 100644 --- a/silent/src/route/root.rs +++ b/silent/src/route/root.rs @@ -1,8 +1,10 @@ +#[cfg(feature = "cookie")] +use crate::cookie::middleware::CookieMiddleware; use crate::middlewares::RequestTimeLogger; use crate::route::handler_match::{Match, RouteMatched}; use crate::route::Route; #[cfg(feature = "session")] -use crate::session::SessionMiddleware; +use crate::session::middleware::SessionMiddleware; #[cfg(feature = "template")] use crate::templates::TemplateMiddleware; #[cfg(feature = "scheduler")] @@ -133,6 +135,10 @@ impl RootRoute { self.hook_first(SessionMiddleware::default()) } } + #[cfg(feature = "cookie")] + pub fn check_cookie(&mut self) { + self.hook_first(CookieMiddleware::new()) + } #[cfg(feature = "template")] pub fn set_template_dir(&mut self, dir: impl Into) -> &mut Self { diff --git a/silent/src/service/mod.rs b/silent/src/service/mod.rs index 885c3da..548d812 100644 --- a/silent/src/service/mod.rs +++ b/silent/src/service/mod.rs @@ -96,6 +96,8 @@ impl Server { root_route.set_configs(configs.clone()); #[cfg(feature = "session")] root_route.check_session(); + #[cfg(feature = "cookie")] + root_route.check_cookie(); #[cfg(feature = "scheduler")] let scheduler = root_route.scheduler.clone(); #[cfg(feature = "scheduler")] diff --git a/silent/src/session/middleware.rs b/silent/src/session/middleware.rs new file mode 100644 index 0000000..59c9402 --- /dev/null +++ b/silent/src/session/middleware.rs @@ -0,0 +1,86 @@ +use crate::{ + CookieExt, Handler, MiddleWareHandler, Next, Request, Response, SilentError, StatusCode, +}; +use async_session::{MemoryStore, Session, SessionStore}; +use async_trait::async_trait; +use cookie::{Cookie, CookieJar}; +use std::sync::Arc; +use tokio::sync::RwLock; + +pub struct SessionMiddleware +where + T: SessionStore, +{ + pub session_store: Arc>, +} + +impl Default for SessionMiddleware { + fn default() -> SessionMiddleware { + let session = MemoryStore::new(); + Self::new(session) + } +} + +impl SessionMiddleware +where + T: SessionStore, +{ + pub fn new(session: T) -> Self { + let session_store = Arc::new(RwLock::new(session)); + SessionMiddleware { session_store } + } +} + +#[async_trait] +impl MiddleWareHandler for SessionMiddleware +where + T: SessionStore, +{ + async fn handle(&self, mut req: Request, next: &Next) -> crate::Result { + let mut cookies = req.cookies().clone(); + let cookie = cookies.get("silent-web-session"); + let session_store = self.session_store.read().await; + let mut session_key_exists = false; + let cookie_value = if cookie.is_some() { + session_key_exists = true; + cookie.unwrap().value().to_string() + } else { + session_store.store_session(Session::new()).await?.unwrap() + }; + let session = session_store + .load_session(cookie_value.clone()) + .await + .map_err(|e| { + SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Failed to load session: {}", e), + ) + })? + .ok_or(SilentError::business_error( + StatusCode::INTERNAL_SERVER_ERROR, + "Failed to load session", + ))?; + req.extensions_mut().insert(session.clone()); + let session_copied = session.clone(); + if !session_key_exists { + cookies.add( + Cookie::build(("silent-web-session", cookie_value)) + .max_age(cookie::time::Duration::hours(2)), + ); + } + let mut res = next.call(req).await?; + if res.extensions().get::().is_none() { + res.extensions_mut().insert(session_copied); + } + if res.extensions().get::().is_none() { + res.extensions_mut().insert(cookies); + } else { + let cookie_jar = res.extensions_mut().get_mut::().unwrap(); + for cookie in cookie_jar.iter() { + cookies.add(cookie.clone()); + } + res.extensions_mut().insert(cookies.clone()); + } + Ok(res) + } +} diff --git a/silent/src/session/mod.rs b/silent/src/session/mod.rs index 380722f..c100e27 100644 --- a/silent/src/session/mod.rs +++ b/silent/src/session/mod.rs @@ -1,79 +1,2 @@ +pub(crate) mod middleware; pub mod session_ext; - -use crate::{ - CookieExt, Handler, MiddleWareHandler, Next, Request, Response, Result, SilentError, StatusCode, -}; -use async_session::{MemoryStore, Session, SessionStore}; -use async_trait::async_trait; -use cookie::Cookie; -use std::sync::Arc; -use tokio::sync::RwLock; - -pub struct SessionMiddleware -where - T: SessionStore, -{ - pub session_store: Arc>, -} - -impl Default for SessionMiddleware { - fn default() -> SessionMiddleware { - let session = MemoryStore::new(); - Self::new(session) - } -} - -impl SessionMiddleware -where - T: SessionStore, -{ - pub fn new(session: T) -> Self { - let session_store = Arc::new(RwLock::new(session)); - SessionMiddleware { session_store } - } -} - -#[async_trait] -impl MiddleWareHandler for SessionMiddleware -where - T: SessionStore, -{ - async fn handle(&self, mut req: Request, next: &Next) -> Result { - let cookies = req.cookies().clone(); - let cookie = cookies.get("silent-web-session"); - let session_store = self.session_store.read().await; - let session = if cookie.is_none() { - req.extensions_mut().insert(cookies.clone()); - Session::new() - } else { - let cookie = cookie.unwrap(); - session_store - .load_session(cookie.value().to_string()) - .await - .map_err(|e| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Failed to load session: {}", e), - ) - })? - .unwrap_or_default() - }; - req.extensions_mut().insert(session.clone()); - let mut res = next.call(req).await?; - res.extensions_mut().insert(session.clone()); - res.extensions_mut().insert(cookies); - let cookie_value = session_store.store_session(session).await.map_err(|e| { - SilentError::business_error( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Failed to store session: {}", e), - ) - })?; - if let Some(cookie_value) = cookie_value { - res.cookies_mut().add( - Cookie::build(("silent-web-session", cookie_value)) - .max_age(cookie::time::Duration::hours(2)), - ); - } - Ok(res) - } -} diff --git a/silent/src/session/session_ext.rs b/silent/src/session/session_ext.rs index 3785ef9..03a7376 100644 --- a/silent/src/session/session_ext.rs +++ b/silent/src/session/session_ext.rs @@ -9,7 +9,7 @@ pub trait SessionExt { /// Get `Session` mutable reference. fn sessions_mut(&mut self) -> &mut Session; /// Get `Session` from session. - fn session, V: DeserializeOwned>(&self, name: K) -> Option; + fn session(&self, name: &str) -> Option; } impl SessionExt for Request { @@ -21,7 +21,7 @@ impl SessionExt for Request { self.extensions_mut().get_mut().unwrap() } - fn session, V: DeserializeOwned>(&self, name: K) -> Option { + fn session(&self, name: &str) -> Option { self.sessions().get(name.as_ref()) } } @@ -35,7 +35,7 @@ impl SessionExt for Response { self.extensions_mut().get_mut().unwrap() } - fn session, V: DeserializeOwned>(&self, name: K) -> Option { + fn session(&self, name: &str) -> Option { self.sessions().get(name.as_ref()) } } From 05bedd3bb8e340f3406f84b3c64444e34deb3cf2 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Mon, 14 Oct 2024 17:05:59 +0800 Subject: [PATCH 43/49] =?UTF-8?q?fix:=20cors=20=E5=BC=82=E5=B8=B8=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- silent/src/middleware/middlewares/cors.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/silent/src/middleware/middlewares/cors.rs b/silent/src/middleware/middlewares/cors.rs index bb62b1f..ffd8d99 100644 --- a/silent/src/middleware/middlewares/cors.rs +++ b/silent/src/middleware/middlewares/cors.rs @@ -234,7 +234,15 @@ impl MiddleWareHandler for Cors { if req.method() == Method::OPTIONS { return Ok(res); } - res.copy_from_response(next.call(req).await?); - Ok(res) + match next.call(req).await { + Ok(result) => { + res.copy_from_response(result); + Ok(res) + } + Err(e) => { + res.copy_from_response(e.into()); + Ok(res) + } + } } } From f37163d3e222c1fbc0f6c2d9625fad0e784f16b9 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Fri, 29 Nov 2024 08:47:02 +0800 Subject: [PATCH 44/49] chore: grpc file name edit --- examples/grpc/build.rs | 2 +- .../proto/hello_world.proto} | 4 ++-- examples/grpc/src/client.rs | 2 +- examples/grpc/src/main.rs | 2 +- examples/grpc_h2c/build.rs | 4 ++-- .../proto/hello_world.proto} | 6 +++--- examples/grpc_h2c/src/client.rs | 2 +- examples/grpc_h2c/src/main.rs | 20 +++++++++---------- examples/grpc_streaming/build.rs | 2 +- 9 files changed, 22 insertions(+), 22 deletions(-) rename examples/{grpc_h2c/proto/helloworld.proto => grpc/proto/hello_world.proto} (93%) rename examples/{grpc/proto/helloworld.proto => grpc_h2c/proto/hello_world.proto} (92%) diff --git a/examples/grpc/build.rs b/examples/grpc/build.rs index 78e5e44..cf810e4 100644 --- a/examples/grpc/build.rs +++ b/examples/grpc/build.rs @@ -1,5 +1,5 @@ fn main() { tonic_build::configure() - .compile(&["proto/helloworld.proto"], &["/proto"]) + .compile_protos(&["proto/hello_world.proto"], &["/proto"]) .unwrap(); } diff --git a/examples/grpc_h2c/proto/helloworld.proto b/examples/grpc/proto/hello_world.proto similarity index 93% rename from examples/grpc_h2c/proto/helloworld.proto rename to examples/grpc/proto/hello_world.proto index d79a6a0..f2abed2 100644 --- a/examples/grpc_h2c/proto/helloworld.proto +++ b/examples/grpc/proto/hello_world.proto @@ -15,10 +15,10 @@ syntax = "proto3"; option java_multiple_files = true; -option java_package = "io.grpc.examples.helloworld"; +option java_package = "io.grpc.examples.hello_world"; option java_outer_classname = "HelloWorldProto"; -package helloworld; +package hello_world; // The greeting service definition. service Greeter { diff --git a/examples/grpc/src/client.rs b/examples/grpc/src/client.rs index 6159d00..f8966e4 100644 --- a/examples/grpc/src/client.rs +++ b/examples/grpc/src/client.rs @@ -2,7 +2,7 @@ use hello_world::greeter_client::GreeterClient; use hello_world::HelloRequest; pub mod hello_world { - tonic::include_proto!("helloworld"); + tonic::include_proto!("hello_world"); } #[allow(dead_code)] #[tokio::main] diff --git a/examples/grpc/src/main.rs b/examples/grpc/src/main.rs index 9db735e..5a0b5e0 100644 --- a/examples/grpc/src/main.rs +++ b/examples/grpc/src/main.rs @@ -9,7 +9,7 @@ use silent::GrpcRegister; mod client; pub mod hello_world { - tonic::include_proto!("helloworld"); // The string specified here must match the proto package name + tonic::include_proto!("hello_world"); // The string specified here must match the proto package name } #[derive(Debug, Default)] diff --git a/examples/grpc_h2c/build.rs b/examples/grpc_h2c/build.rs index 3b3886e..ad2901a 100644 --- a/examples/grpc_h2c/build.rs +++ b/examples/grpc_h2c/build.rs @@ -4,7 +4,7 @@ fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); tonic_build::configure() - .file_descriptor_set_path(out_dir.join("helloworld_descriptor.bin")) - .compile(&["proto/helloworld.proto"], &["/proto"]) + .file_descriptor_set_path(out_dir.join("hello_world_descriptor.bin")) + .compile_protos(&["proto/hello_world.proto"], &["/proto"]) .unwrap(); } diff --git a/examples/grpc/proto/helloworld.proto b/examples/grpc_h2c/proto/hello_world.proto similarity index 92% rename from examples/grpc/proto/helloworld.proto rename to examples/grpc_h2c/proto/hello_world.proto index 8de5d08..f2abed2 100644 --- a/examples/grpc/proto/helloworld.proto +++ b/examples/grpc_h2c/proto/hello_world.proto @@ -15,10 +15,10 @@ syntax = "proto3"; option java_multiple_files = true; -option java_package = "io.grpc.examples.helloworld"; +option java_package = "io.grpc.examples.hello_world"; option java_outer_classname = "HelloWorldProto"; -package helloworld; +package hello_world; // The greeting service definition. service Greeter { @@ -34,4 +34,4 @@ message HelloRequest { // The response message containing the greetings message HelloReply { string message = 1; -} \ No newline at end of file +} diff --git a/examples/grpc_h2c/src/client.rs b/examples/grpc_h2c/src/client.rs index 256fd63..6af0e40 100644 --- a/examples/grpc_h2c/src/client.rs +++ b/examples/grpc_h2c/src/client.rs @@ -5,7 +5,7 @@ use hyper_util::client::legacy::Client; use hyper_util::rt::TokioExecutor; pub mod hello_world { - tonic::include_proto!("helloworld"); + tonic::include_proto!("hello_world"); } #[tokio::main] diff --git a/examples/grpc_h2c/src/main.rs b/examples/grpc_h2c/src/main.rs index b115f12..db94c70 100644 --- a/examples/grpc_h2c/src/main.rs +++ b/examples/grpc_h2c/src/main.rs @@ -7,7 +7,7 @@ use silent::prelude::{info, logger, HandlerAppend, Level, Route}; use silent::GrpcRegister; pub mod hello_world { - tonic::include_proto!("helloworld"); + tonic::include_proto!("hello_world"); } #[derive(Default)] @@ -30,14 +30,14 @@ impl Greeter for MyGreeter { #[tokio::main] async fn main() -> Result<(), Box> { - let _greeter = MyGreeter::default(); - // logger::fmt().with_max_level(Level::INFO).init(); - // - // let mut route = Route::new("").get(|_req| async { Ok("hello world") }); - // GreeterServer::new(greeter).register(&mut route); - // silent::prelude::Server::new() - // .bind("0.0.0.0:50051".parse().unwrap()) - // .serve(route) - // .await; + let greeter = MyGreeter::default(); + logger::fmt().with_max_level(Level::INFO).init(); + + let mut route = Route::new("").get(|_req| async { Ok("hello world") }); + GreeterServer::new(greeter).register(&mut route); + silent::prelude::Server::new() + .bind("0.0.0.0:50051".parse().unwrap()) + .serve(route) + .await; Ok(()) } diff --git a/examples/grpc_streaming/build.rs b/examples/grpc_streaming/build.rs index 4657bea..2ec2f69 100644 --- a/examples/grpc_streaming/build.rs +++ b/examples/grpc_streaming/build.rs @@ -1,5 +1,5 @@ fn main() { tonic_build::configure() - .compile(&["proto/echo.proto"], &["/proto"]) + .compile_protos(&["proto/echo.proto"], &["/proto"]) .unwrap(); } From ed1d4f504370a0fae10fbd3ae389a5b63c4d1486 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Fri, 8 Nov 2024 17:23:25 +0800 Subject: [PATCH 45/49] =?UTF-8?q?feat:=20sse=E5=BE=AE=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/sse-chat/src/main.rs | 3 +-- silent/src/prelude.rs | 2 +- silent/src/sse/keep_alive.rs | 24 ++++++++++++++---------- silent/src/sse/mod.rs | 3 +-- silent/src/sse/reply.rs | 9 ++++----- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/examples/sse-chat/src/main.rs b/examples/sse-chat/src/main.rs index f31e53d..44a6372 100644 --- a/examples/sse-chat/src/main.rs +++ b/examples/sse-chat/src/main.rs @@ -82,8 +82,7 @@ async fn user_connected(_req: Request) -> Result { Ok(SSEEvent::default().data(reply)) } }); - let res = sse_reply(stream); - Ok(res) + sse_reply(stream) } fn user_message(my_id: usize, msg: &str) { diff --git a/silent/src/prelude.rs b/silent/src/prelude.rs index e6b53ca..dbaf0bd 100644 --- a/silent/src/prelude.rs +++ b/silent/src/prelude.rs @@ -27,7 +27,7 @@ pub use crate::service::Server; #[cfg(feature = "session")] pub use crate::session::session_ext::SessionExt; #[cfg(feature = "sse")] -pub use crate::sse::{sse_reply, SSEEvent}; +pub use crate::sse::{sse_reply, KeepAlive, SSEEvent}; #[cfg(feature = "template")] pub use crate::templates::*; #[cfg(feature = "upgrade")] diff --git a/silent/src/sse/keep_alive.rs b/silent/src/sse/keep_alive.rs index 0d4dc7c..b5db1b1 100644 --- a/silent/src/sse/keep_alive.rs +++ b/silent/src/sse/keep_alive.rs @@ -20,8 +20,20 @@ pub struct KeepAlive { max_interval: Duration, } -#[allow(dead_code)] +impl Default for KeepAlive { + fn default() -> Self { + Self { + comment_text: Cow::Borrowed(""), + max_interval: Duration::from_secs(15), + } + } +} + impl KeepAlive { + pub fn new() -> Self { + Self::default() + } + /// Customize the interval between keep-alive messages. /// /// Default is 15 seconds. @@ -33,7 +45,7 @@ impl KeepAlive { /// Customize the text of the keep-alive message. /// /// Default is an empty comment. - pub fn text(mut self, text: impl Into>) -> Self { + pub fn comment_text(mut self, text: impl Into>) -> Self { self.comment_text = text.into(); self } @@ -59,7 +71,6 @@ impl KeepAlive { } } -#[allow(missing_debug_implementations)] #[pin_project] struct SseKeepAlive { #[pin] @@ -70,13 +81,6 @@ struct SseKeepAlive { alive_timer: Sleep, } -pub fn keep_alive() -> KeepAlive { - KeepAlive { - comment_text: Cow::Borrowed(""), - max_interval: Duration::from_secs(15), - } -} - impl Stream for SseKeepAlive where S: TryStream + Send + 'static, diff --git a/silent/src/sse/mod.rs b/silent/src/sse/mod.rs index 35ce515..42d74da 100644 --- a/silent/src/sse/mod.rs +++ b/silent/src/sse/mod.rs @@ -45,6 +45,5 @@ mod keep_alive; mod reply; pub use event::SSEEvent; -#[allow(unused_imports)] -pub use keep_alive::{keep_alive, KeepAlive}; +pub use keep_alive::KeepAlive; pub use reply::sse_reply; diff --git a/silent/src/sse/reply.rs b/silent/src/sse/reply.rs index 6d71d71..c75197e 100644 --- a/silent/src/sse/reply.rs +++ b/silent/src/sse/reply.rs @@ -1,17 +1,16 @@ use crate::header::{CACHE_CONTROL, CONTENT_TYPE}; use crate::prelude::stream_body; -use crate::sse::{keep_alive, SSEEvent}; +use crate::sse::{KeepAlive, SSEEvent}; use crate::{headers::HeaderValue, log, Response, Result, SilentError, StatusCode}; use futures_util::{future, Stream, TryStreamExt}; -pub fn sse_reply(stream: S) -> Response +pub fn sse_reply(stream: S) -> Result where S: Stream> + Send + 'static, { - let event_stream = keep_alive().stream(stream); + let event_stream = KeepAlive::default().stream(stream); let body_stream = event_stream .map_err(|error| { - // FIXME: error logging log::error!("sse stream error: {}", error.to_string()); SilentError::BusinessError { code: StatusCode::INTERNAL_SERVER_ERROR, @@ -29,5 +28,5 @@ where // Disable response body caching res.headers_mut() .insert(CACHE_CONTROL, HeaderValue::from_static("no-cache")); - res + Ok(res) } From 4ed4938b7410bf775e1ffaa6a268b8ad2807c377 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Thu, 21 Nov 2024 13:20:55 +0800 Subject: [PATCH 46/49] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c514c44..715c237 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,4 @@ homepage = "https://github.com/hubertshelley/silent" license = "Apache-2.0" readme = "./readme.md" repository = "https://github.com/hubertshelley/silent" -version = "1.5.0" +version = "2.0.0" From c772fd6e8b7a07d7646bdd0104aaf80c935270a1 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Tue, 26 Nov 2024 16:04:23 +0800 Subject: [PATCH 47/49] feat: moute match edit --- silent/src/route/root.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/silent/src/route/root.rs b/silent/src/route/root.rs index 993ac82..7404778 100644 --- a/silent/src/route/root.rs +++ b/silent/src/route/root.rs @@ -9,7 +9,9 @@ use crate::session::middleware::SessionMiddleware; use crate::templates::TemplateMiddleware; #[cfg(feature = "scheduler")] use crate::Scheduler; -use crate::{Configs, Handler, MiddleWareHandler, Next, Request, Response, SilentError}; +use crate::{ + Configs, Handler, HandlerWrapper, MiddleWareHandler, Next, Request, Response, SilentError, +}; #[cfg(feature = "session")] use async_session::SessionStore; use async_trait::async_trait; @@ -92,7 +94,14 @@ impl Handler for RootHandler { let next = Next::build(Arc::new(route), self.middlewares.clone()); next.call(req).await } - RouteMatched::Unmatched => Err(SilentError::NotFound), + RouteMatched::Unmatched => { + let handler = |_req| async move { Err::<(), SilentError>(SilentError::NotFound) }; + let next = Next::build( + Arc::new(HandlerWrapper::new(handler)), + self.middlewares.clone(), + ); + next.call(req).await + } } } } From 7acee788637a0a43a2bf8fc89b29e9c13f2f9fd1 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Tue, 3 Dec 2024 15:03:06 +0800 Subject: [PATCH 48/49] =?UTF-8?q?=E5=90=88=E5=B9=B6=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/middleware/src/main.rs | 23 ++----------- examples/todo/src/main.rs | 8 ++--- silent/src/error/mod.rs | 15 ++++++--- .../middlewares/exception_handler.rs | 3 +- silent/src/route/handler_match.rs | 33 ++++++++----------- silent/src/service/hyper_service.rs | 21 +++++++----- silent/src/sse/mod.rs | 3 +- 7 files changed, 42 insertions(+), 64 deletions(-) diff --git a/examples/middleware/src/main.rs b/examples/middleware/src/main.rs index b5c93ca..ea83f01 100644 --- a/examples/middleware/src/main.rs +++ b/examples/middleware/src/main.rs @@ -19,11 +19,7 @@ struct MiddleWare { #[async_trait] impl MiddleWareHandler for MiddleWare { - async fn pre_request( - &self, - _req: &mut Request, - _res: &mut Response, - ) -> Result { + async fn handle(&self, req: Request, next: &Next) -> Result { self.count.fetch_add(1, std::sync::atomic::Ordering::SeqCst); let count = self.count.load(std::sync::atomic::Ordering::SeqCst); info!("pre_request count: {}", count); @@ -34,21 +30,6 @@ impl MiddleWareHandler for MiddleWare { msg: "bad request:pre_request".to_string(), }); } - Ok(MiddlewareResult::Continue) - } - async fn after_response(&self, res: &mut Response) -> Result { - let count = self.count.load(std::sync::atomic::Ordering::SeqCst); - info!("after_response count: {}", count); - if count % 3 == 0 { - error!("set after_response error"); - return Err(SilentError::BusinessError { - code: StatusCode::BAD_REQUEST, - msg: "bad request:after_response".to_string(), - }); - } - if let ResBody::Once(body) = res.body() { - println!("body: {:?}", body); - } - Ok(MiddlewareResult::Continue) + next.call(req).await } } diff --git a/examples/todo/src/main.rs b/examples/todo/src/main.rs index 2303c96..2cb987e 100644 --- a/examples/todo/src/main.rs +++ b/examples/todo/src/main.rs @@ -28,13 +28,9 @@ struct MiddleWare { #[async_trait] impl MiddleWareHandler for MiddleWare { - async fn pre_request( - &self, - req: &mut Request, - _res: &mut Response, - ) -> Result { + async fn handle(&self, mut req: Request, next: &Next) -> Result { req.extensions_mut().insert(self.db.clone()); - Ok(MiddlewareResult::Continue) + next.call(req).await } } diff --git a/silent/src/error/mod.rs b/silent/src/error/mod.rs index 23aabb0..16bceab 100644 --- a/silent/src/error/mod.rs +++ b/silent/src/error/mod.rs @@ -64,6 +64,8 @@ pub enum SilentError { /// 错误信息 msg: String, }, + #[error("not found")] + NotFound, } pub type SilentResult = Result; @@ -103,14 +105,18 @@ impl SilentError { let msg = serde_json::to_string(&msg).unwrap_or_default(); Self::BusinessError { code, msg } } - pub fn business_error(code: StatusCode, msg: String) -> Self { - Self::BusinessError { code, msg } + pub fn business_error>(code: StatusCode, msg: T) -> Self { + Self::BusinessError { + code, + msg: msg.into(), + } } pub fn status(&self) -> StatusCode { match self { Self::BusinessError { code, .. } => *code, Self::SerdeDeError(_) => StatusCode::UNPROCESSABLE_ENTITY, Self::SerdeJsonError(_) => StatusCode::UNPROCESSABLE_ENTITY, + Self::NotFound => StatusCode::NOT_FOUND, _ => StatusCode::INTERNAL_SERVER_ERROR, } } @@ -146,6 +152,7 @@ mod tests { use http_body_util::BodyExt; use hyper::StatusCode; use serde_json::Value; + use tracing::info; #[derive(Serialize)] struct ResBody { @@ -164,8 +171,8 @@ mod tests { let err = SilentError::business_error_obj(StatusCode::BAD_REQUEST, res_body); let mut res: Response = err.into(); assert_eq!(res.status, StatusCode::BAD_REQUEST); - println!("{:#?}", res.headers); - println!( + info!("{:#?}", res.headers); + info!( "{:#?}", res.body.frame().await.unwrap().unwrap().data_ref().unwrap() ); diff --git a/silent/src/middleware/middlewares/exception_handler.rs b/silent/src/middleware/middlewares/exception_handler.rs index bbf6e6f..94c5440 100644 --- a/silent/src/middleware/middlewares/exception_handler.rs +++ b/silent/src/middleware/middlewares/exception_handler.rs @@ -3,8 +3,7 @@ use std::sync::Arc; use async_trait::async_trait; -use crate::middleware::middleware_trait::Next; -use crate::{Configs, MiddleWareHandler, Request, Response, Result}; +use crate::{Configs, Handler, MiddleWareHandler, Next, Request, Response, Result}; /// ExceptionHandler 中间件 /// ```rust diff --git a/silent/src/route/handler_match.rs b/silent/src/route/handler_match.rs index be25a06..7c25937 100644 --- a/silent/src/route/handler_match.rs +++ b/silent/src/route/handler_match.rs @@ -36,14 +36,13 @@ enum SpecialPath { FullPath(String), } -impl<'a> From<&'a str> for SpecialPath { +impl From<&str> for SpecialPath { fn from(value: &str) -> Self { // 去除首尾的尖括号 let value = &value[1..value.len() - 1]; let mut type_str = value.splitn(2, ':'); let key = type_str.next().unwrap_or(""); let path_type = type_str.next().unwrap_or(""); - println!("key: {}, path_type: {}", key, path_type); match path_type { "**" => SpecialPath::FullPath(key.to_string()), "*" => SpecialPath::Path(key.to_string()), @@ -130,23 +129,13 @@ impl Match for Route { self.last_matched(req, last_url) } SpecialPath::FullPath(key) => { - println!("SpecialPath::FullPath: path: {}", path); req.set_path_params(key, PathParam::Path(path.to_string())); match self.last_matched(req, last_url) { - RouteMatched::Matched(route) => { - println!("SpecialPath::FullPath: matched: {}", route.path); - RouteMatched::Matched(route) - } - RouteMatched::Unmatched => { - println!( - "SpecialPath::FullPath: Unmatched matched: {}", - self.handler.len() - ); - match self.handler.is_empty() { - true => RouteMatched::Unmatched, - false => RouteMatched::Matched(self.clone()), - } - } + RouteMatched::Matched(route) => RouteMatched::Matched(route), + RouteMatched::Unmatched => match self.handler.is_empty() { + true => RouteMatched::Unmatched, + false => RouteMatched::Matched(self.clone()), + }, } } } @@ -195,7 +184,7 @@ impl Match for RootRoute { mod tests { use super::*; use crate::prelude::HandlerAppend; - use crate::SilentError; + use crate::{Handler, SilentError}; use bytes::Bytes; use http_body_util::BodyExt; @@ -301,11 +290,13 @@ mod tests { let mut routes = RootRoute::new(); routes.push(route); let mut req = Request::empty(); + req.set_remote("127.0.0.1:8080".parse().unwrap()); *req.uri_mut() = "/hello/world".parse().unwrap(); assert_eq!( routes - .handle(req, "127.0.0.1:8000".parse().unwrap()) + .call(req) .await + .unwrap() .body .frame() .await @@ -325,11 +316,13 @@ mod tests { let mut routes = RootRoute::new(); routes.push(route); let mut req = Request::empty(); + req.set_remote("127.0.0.1:8080".parse().unwrap()); *req.uri_mut() = "/hello/world1".parse().unwrap(); assert_eq!( routes - .handle(req, "127.0.0.1:8000".parse().unwrap()) + .call(req) .await + .unwrap() .body .frame() .await diff --git a/silent/src/service/hyper_service.rs b/silent/src/service/hyper_service.rs index 7912dc8..e7b9983 100644 --- a/silent/src/service/hyper_service.rs +++ b/silent/src/service/hyper_service.rs @@ -7,19 +7,18 @@ use hyper::{Request as HyperRequest, Response as HyperResponse}; use crate::core::{adapt::RequestAdapt, adapt::ResponseAdapt, res_body::ResBody}; use crate::prelude::ReqBody; -use crate::route::RootRoute; -use crate::{Request, Response}; +use crate::{Handler, Request, Response}; #[doc(hidden)] #[derive(Clone)] -pub struct HyperServiceHandler { +pub struct HyperServiceHandler { pub(crate) remote_addr: SocketAddr, - pub(crate) routes: RootRoute, + pub(crate) routes: H, } -impl HyperServiceHandler { +impl HyperServiceHandler { #[inline] - pub fn new(remote_addr: SocketAddr, routes: RootRoute) -> Self { + pub fn new(remote_addr: SocketAddr, routes: H) -> Self { Self { remote_addr, routes, @@ -27,16 +26,18 @@ impl HyperServiceHandler { } /// Handle [`Request`] and returns [`Response`]. #[inline] - pub fn handle(&self, req: Request) -> impl Future { + pub fn handle(&self, mut req: Request) -> impl Future { let Self { remote_addr, routes, } = self.clone(); - async move { routes.clone().handle(req, remote_addr).await } + req.set_remote(remote_addr); + let routes = routes.clone(); + async move { routes.call(req).await.unwrap_or_else(Into::into) } } } -impl HyperService> for HyperServiceHandler +impl HyperService> for HyperServiceHandler where B: Into, { @@ -57,6 +58,8 @@ where } #[cfg(test)] mod tests { + use crate::route::RootRoute; + use super::*; #[tokio::test] diff --git a/silent/src/sse/mod.rs b/silent/src/sse/mod.rs index 42d74da..7d584d0 100644 --- a/silent/src/sse/mod.rs +++ b/silent/src/sse/mod.rs @@ -28,8 +28,7 @@ //! //! let route = Route::new("push-notifications") //! .get(|req| async { -//! let mut res = sse_reply(sse_events()); -//! Ok(res) +//! sse_reply(sse_events()) //! }); //! ``` //! From a5655d5b10ca7d086a53e1365b25177fee343334 Mon Sep 17 00:00:00 2001 From: hubertshelley Date: Tue, 3 Dec 2024 15:09:18 +0800 Subject: [PATCH 49/49] =?UTF-8?q?=E5=90=88=E5=B9=B6=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/grpc_streaming/src/client.rs | 2 +- examples/path_params/src/main.rs | 2 +- examples/scheduler/src/main.rs | 2 +- examples/sse-chat/src/main.rs | 2 +- examples/timeout/src/main.rs | 4 ++-- silent/src/configs/mod.rs | 8 ++++---- silent/src/scheduler/mod.rs | 1 - silent/src/scheduler/task.rs | 5 ++--- silent/src/security/argon2.rs | 3 ++- silent/src/security/pbkdf2.rs | 3 ++- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/grpc_streaming/src/client.rs b/examples/grpc_streaming/src/client.rs index 65416cc..bd00627 100644 --- a/examples/grpc_streaming/src/client.rs +++ b/examples/grpc_streaming/src/client.rs @@ -70,7 +70,7 @@ async fn main() -> Result<(), Box> { println!("Streaming echo:"); streaming_echo(&mut client, 5).await; - tokio::time::sleep(Duration::from_secs(1)).await; //do not mess server println functions + tokio::time::sleep(Duration::from_secs(1)).await; //do not mess server info functions // Echo stream that sends 17 requests then graceful end that connection println!("\r\nBidirectional stream echo:"); diff --git a/examples/path_params/src/main.rs b/examples/path_params/src/main.rs index 0e89569..9d7779d 100644 --- a/examples/path_params/src/main.rs +++ b/examples/path_params/src/main.rs @@ -13,7 +13,7 @@ fn main() { .append(Route::new("/**").get(hello_world)) .append(Route::new("").get(hello_world)) .append(Route::new("/other").get(hello_world)); - println!("{:?}", route); + info!("{:?}", route); Server::new().run(route); } diff --git a/examples/scheduler/src/main.rs b/examples/scheduler/src/main.rs index 9bcd149..4150d04 100644 --- a/examples/scheduler/src/main.rs +++ b/examples/scheduler/src/main.rs @@ -12,7 +12,7 @@ fn main() { "task description".to_string(), Arc::new(|| { Box::pin(async { - println!("task run: {:?}", Utc::now()); + info!("task run: {:?}", Utc::now()); Ok(()) }) }), diff --git a/examples/sse-chat/src/main.rs b/examples/sse-chat/src/main.rs index 44a6372..f50ada1 100644 --- a/examples/sse-chat/src/main.rs +++ b/examples/sse-chat/src/main.rs @@ -48,7 +48,7 @@ struct Msg { async fn chat_send(mut req: Request) -> Result { let my_id = req.get_path_params::("id")?; let msg = req.json_parse::().await?; - println!("chat_send: my_id: {}, msg: {:?}", my_id, msg); + info!("chat_send: my_id: {}, msg: {:?}", my_id, msg); user_message(my_id as usize, msg.msg.as_str()); Ok(Response::empty()) } diff --git a/examples/timeout/src/main.rs b/examples/timeout/src/main.rs index 259204d..8e9a78f 100644 --- a/examples/timeout/src/main.rs +++ b/examples/timeout/src/main.rs @@ -7,9 +7,9 @@ fn main() { let route = Route::new("") .hook(Timeout::new(Duration::from_secs(1))) .get(|_req| async { - println!("sleeping for 2 seconds"); + info!("sleeping for 2 seconds"); tokio::time::sleep(Duration::from_secs(2)).await; - println!("done sleeping"); + info!("done sleeping"); Ok("hello world") }); Server::new().run(route); diff --git a/silent/src/configs/mod.rs b/silent/src/configs/mod.rs index a4c5722..74bdb50 100644 --- a/silent/src/configs/mod.rs +++ b/silent/src/configs/mod.rs @@ -208,14 +208,14 @@ mod tests { let configs = configs.clone(); if let Some(my_type) = configs.get::() { // my_type.0 = i.to_string(); - println!("Ok: i:{}, v:{}", i, my_type.0) + info!("Ok: i:{}, v:{}", i, my_type.0) } else { - println!("Err: i:{}", i) + info!("Err: i:{}", i) } } else if let Some(my_type) = configs.get::() { - println!("Ok: i:{}, v:{}", i, my_type.0) + info!("Ok: i:{}, v:{}", i, my_type.0) } else { - println!("Err: i:{}", i) + info!("Err: i:{}", i) } }); } diff --git a/silent/src/scheduler/mod.rs b/silent/src/scheduler/mod.rs index c3d0c30..b93a96c 100644 --- a/silent/src/scheduler/mod.rs +++ b/silent/src/scheduler/mod.rs @@ -148,7 +148,6 @@ mod tests { }), ); scheduler.add_task(async_task.clone()).unwrap(); - println!("{:?}", scheduler.get_tasks()); assert_eq!(scheduler.get_tasks().len(), 2); assert!(scheduler.add_task(async_task.clone()).is_err()); scheduler.remove_task(&async_task.id); diff --git a/silent/src/scheduler/task.rs b/silent/src/scheduler/task.rs index 9cc694d..e164f9e 100644 --- a/silent/src/scheduler/task.rs +++ b/silent/src/scheduler/task.rs @@ -38,7 +38,7 @@ impl Task { match self.is_async { true => Err(anyhow::anyhow!("async task not support run")), false => match self.process_time.is_active() { - true => (self.action.clone())(), + true => self.action.clone()(), false => Ok(()), }, } @@ -46,7 +46,7 @@ impl Task { pub(crate) async fn run_async(&self) -> Result<()> { match self.is_async { true => match self.process_time.is_active() { - true => (self.action_async.clone())().await, + true => self.action_async.clone()().await, false => Ok(()), }, false => Err(anyhow::anyhow!("sync task not support run_async")), @@ -113,7 +113,6 @@ mod tests { Ok(()) }), ); - println!("{:?}", task); task.run().unwrap(); assert_eq!(counter.load(Ordering::SeqCst), 0); task.process_time = ProcessTime::try_from("2023-01-01T00:00:00Z".to_string()).unwrap(); diff --git a/silent/src/security/argon2.rs b/silent/src/security/argon2.rs index b3ce3fa..257ae3b 100644 --- a/silent/src/security/argon2.rs +++ b/silent/src/security/argon2.rs @@ -31,12 +31,13 @@ pub fn verify_password(password_hash: String, password: String) -> Result #[cfg(test)] mod test { use super::*; + use tracing::info; #[test] fn hash_test() { let password = "hello_password".to_string(); let password_hash = make_password(password.clone()).unwrap(); - println!("{}", password_hash); + info!("{}", password_hash); assert!(verify_password(password_hash, password,).is_ok()) } } diff --git a/silent/src/security/pbkdf2.rs b/silent/src/security/pbkdf2.rs index be385bc..5a84c39 100644 --- a/silent/src/security/pbkdf2.rs +++ b/silent/src/security/pbkdf2.rs @@ -31,12 +31,13 @@ pub fn verify_password(password_hash: String, password: String) -> Result #[cfg(test)] mod test { use super::*; + use tracing::info; #[test] fn hash_test() { let password = "hello_password".to_string(); let password_hash = make_password(password.clone()).unwrap(); - println!("{}", password_hash); + info!("{}", password_hash); assert!(verify_password(password_hash, password).is_ok()) } }