From 4802d121ae8504781116dd9b1f159d0222328907 Mon Sep 17 00:00:00 2001 From: FITAHIANA Nomeniavo Joe <24nomeniavo@gmail.com> Date: Sun, 2 Aug 2026 10:41:45 +0300 Subject: [PATCH 1/6] chore: first try --- src/lib.rs | 149 ++++++++++++++++++++++--------------------------- src/request.rs | 45 ++++++--------- 2 files changed, 84 insertions(+), 110 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index da5cbeb..dd99381 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ #![allow(unused_variables, non_snake_case)] use std::net::SocketAddr; -use std::ops::Deref; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; @@ -44,15 +43,15 @@ pyo3_stub_gen::export_verbatim!("oxapy", "from typing_extensions import Self"); pyo3_stub_gen::define_stub_info_gatherer!(stub_info); struct ProcessRequest { - wrapper: Option>>, - router: Option>, + tx: Sender, match_route: Option>, request: Arc, - tx: Sender, + router: Option>, + wrapper: Option>>, } #[derive(Clone)] -struct RequestContext { +struct Context { app_data: Option>>, wrapper: Option>>, channel_capacity: usize, @@ -61,6 +60,28 @@ struct RequestContext { template: Option>, } +struct ShutDownSignal { + rx: Receiver<()>, +} + +impl ShutDownSignal { + fn new() -> PyResult { + let running = Arc::new(AtomicBool::new(true)); + let (tx, rx) = channel::<()>(1); + ctrlc::set_handler(move || { + println!("\nShutting Down..."); + running.store(false, Ordering::SeqCst); + let _ = block_on(tx.send(()), None); + }) + .into_py_exception()?; + Ok(Self { rx }) + } + + async fn wait(&mut self) { + self.rx.recv().await; + } +} + /// HTTP Server for handling web requests. /// /// The HttpServer is the main entry point for creating web applications with OxAPY. @@ -430,22 +451,12 @@ impl HttpServer { impl HttpServer { async fn run_server(&self) -> PyResult<()> { - let (listener, shutdown) = self.setup_serve().await?; - let (ctx, rx) = self.create_request_context(); - self.spawn_connection_handler(listener, Arc::new(ctx)).await; - self.process_requests(shutdown, rx).await - } - - async fn setup_serve(&self) -> PyResult<(TcpListener, ShutDownSignal)> { let listener = TcpListener::bind(self.addr).await?; println!("Listening on {}", self.addr); let shutdown = ShutDownSignal::new()?; - Ok((listener, shutdown)) - } - fn create_request_context(&self) -> (RequestContext, Receiver) { let (tx, rx) = channel::(self.channel_capacity); - let ctx = RequestContext { + let ctx = Context { app_data: self.app_data.clone(), wrapper: self.wrapper.clone(), channel_capacity: self.channel_capacity, @@ -453,10 +464,12 @@ impl HttpServer { request_sender: tx, template: self.template.clone(), }; - (ctx, rx) + + self.spawn_connection_handler(listener, Arc::new(ctx)).await; + self.process_requests(shutdown, rx).await } - async fn spawn_connection_handler(&self, listener: TcpListener, ctx: Arc) { + async fn spawn_connection_handler(&self, listener: TcpListener, ctx: Arc) { let running = self.running.clone(); let max_connection = self.max_connections.clone(); tokio::spawn(async move { @@ -473,7 +486,7 @@ impl HttpServer { fn spawn_request_handler( io: hyper_util::rt::TokioIo, - ctx: Arc, + ctx: Arc, _permit: tokio::sync::OwnedSemaphorePermit, ) { tokio::spawn(async move { @@ -509,44 +522,18 @@ impl HttpServer { ) -> PyResult<()> { loop { tokio::select! { - Some(req) = rx.recv() => self.handle_request(req).await?, + Some(req) = rx.recv() => { + let response = call_python_handler(&req.router, &req.match_route, &req.request, self.is_async) + .await + .unwrap_or_else(Response::from) + .call_wrapper(&req); + let _ = req.tx.send(response).await; + }, _ = shutdown.wait() => break, } } Ok(()) } - - async fn handle_request(&self, req: ProcessRequest) -> PyResult<()> { - let response = - call_python_handler(&req.router, &req.match_route, &req.request, self.is_async) - .await - .unwrap_or_else(Response::from) - .call_wrapper(&req); - let _ = req.tx.send(response).await; - Ok(()) - } -} - -struct ShutDownSignal { - rx: Receiver<()>, -} - -impl ShutDownSignal { - fn new() -> PyResult { - let running = Arc::new(AtomicBool::new(true)); - let (tx, rx) = channel::<()>(1); - ctrlc::set_handler(move || { - println!("\nShutting Down..."); - running.store(false, Ordering::SeqCst); - let _ = block_on(tx.send(()), None); - }) - .into_py_exception()?; - Ok(Self { rx }) - } - - async fn wait(&mut self) { - self.rx.recv().await; - } } async fn call_python_handler<'l>( @@ -555,40 +542,36 @@ async fn call_python_handler<'l>( request: &Request, is_async: bool, ) -> PyResult { - match (match_route, router) { - (Some(route), Some(router)) => { - let mut result = execute_route_handler(route, router, request)?; - if is_async { - result = Python::attach(|py| into_future(result.into_bound(py)))?.await?; + if let Some(match_route) = match_route + && let Some(router) = router + { + let mut result = Python::attach(|py| { + let route = match_route.value; + let params = &match_route.params; + let kwargs = build_route_params(py, params)?; + + if router.middlewares.is_empty() { + route.handler.call(py, (request.clone(),), Some(&kwargs)) + } else { + let chain = MiddlewareChain::new(&router.middlewares); + chain.execute( + py, + route.sequence, + &*route.handler, + (request.clone(),), + kwargs.clone(), + ) } - Python::attach(|py| into_response::convert_to_response(result, py)) - } - _ => Ok(Status::NOT_FOUND.into()), - } -} + })?; -fn execute_route_handler( - match_route: &MatchRoute, - router: &Router, - request: &Request, -) -> PyResult> { - Python::attach(|py| { - let route = match_route.value; - let params = &match_route.params; - let kwargs = build_route_params(py, params)?; - if router.middlewares.is_empty() { - route.handler.call(py, (request.clone(),), Some(&kwargs)) - } else { - let chain = MiddlewareChain::new(&router.middlewares); - chain.execute( - py, - route.sequence, - route.handler.deref(), - (request.clone(),), - kwargs.clone(), - ) + if is_async { + result = Python::attach(|py| into_future(result.into_bound(py)))?.await?; } - }) + + Python::attach(|py| into_response::convert_to_response(result, py)) + } else { + Ok(Status::NOT_FOUND.into()) + } } fn build_route_params<'py>( diff --git a/src/request.rs b/src/request.rs index a8c4a6b..6e9336b 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use tokio::sync::mpsc::Sender; use ahash::HashMap; use http_body_util::BodyExt; @@ -15,7 +16,7 @@ use url::form_urlencoded; use crate::routing::MatchRoute; use crate::status::Status; use crate::{ - IntoPyException, ProcessRequest, RequestContext, json, multipart::File, templating::Template, + Context, IntoPyException, ProcessRequest, json, multipart::File, templating::Template, }; use crate::{multipart::parse_multipart, response::Body}; use crate::{response::Response, routing::Router}; @@ -246,32 +247,22 @@ impl Request { impl Request { pub(crate) async fn process( self, - ctx: Arc, + ctx: Arc, ) -> Result, hyper::http::Error> { - if let Some(response) = self.try_handle_route(&ctx).await { - return response; - } - self.handle_not_found(&ctx).await - } - - async fn try_handle_route( - &self, - ctx: &RequestContext, - ) -> Option, hyper::http::Error>> { for router in &ctx.routers { if let Some(match_route) = router.find(&self.method, &self.uri) { let response = self - .process_matched_route(ctx, router.clone(), match_route) + .handle_found_route(&ctx, router.clone(), match_route) .await; - return Some(response); + return response; } } - None + self.handle_not_found(&ctx).await } - async fn process_matched_route( + async fn handle_found_route( &self, - ctx: &RequestContext, + ctx: &Context, router: Arc, match_route: MatchRoute<'_>, ) -> Result, hyper::http::Error> { @@ -280,19 +271,19 @@ impl Request { let transmutate_route: MatchRoute<'static> = unsafe { std::mem::transmute(match_route) }; let process_request = ProcessRequest { + tx, + match_route: Some(transmutate_route), request: Arc::new(self.clone()), router: Some(router), - match_route: Some(transmutate_route), - tx, wrapper: ctx.wrapper.clone(), }; - Self::send_and_wait_response(ctx, process_request, rx).await + Self::send_and_wait_response(&ctx.request_sender, process_request, rx).await } async fn handle_not_found( self, - ctx: &RequestContext, + ctx: &Context, ) -> Result, hyper::http::Error> { let (tx, rx) = tokio::sync::mpsc::channel(ctx.channel_capacity); @@ -304,20 +295,20 @@ impl Request { wrapper: ctx.wrapper.clone(), }; - Self::send_and_wait_response(ctx, process_request, rx).await + Self::send_and_wait_response(&ctx.request_sender, process_request, rx).await } - async fn send_and_wait_response( - ctx: &RequestContext, - process_request: ProcessRequest, + async fn send_and_wait_response( + request_sender: &Sender, + process_request: T, mut rx: tokio::sync::mpsc::Receiver, ) -> Result, hyper::http::Error> { - if ctx.request_sender.send(process_request).await.is_ok() + if request_sender.send(process_request).await.is_ok() && let Some(response) = rx.recv().await { return response.try_into(); } - Response::from(Status::NOT_FOUND).try_into() + Response::from(Status::INTERNAL_SERVER_ERROR).try_into() } } From f5101ee1d8cd81781b63c511b577129a33bbb70d Mon Sep 17 00:00:00 2001 From: FITAHIANA Nomeniavo Joe <24nomeniavo@gmail.com> Date: Sun, 2 Aug 2026 10:56:38 +0300 Subject: [PATCH 2/6] chore: pass middleware instead of router --- src/lib.rs | 35 ++++++++++++++++++----------------- src/request.rs | 14 +++++++------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dd99381..94ae629 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,8 @@ use routing::*; use status::Status; use templating::Template; +use crate::middleware::Middleware; + mod cors; #[macro_use] mod exceptions; @@ -46,7 +48,7 @@ struct ProcessRequest { tx: Sender, match_route: Option>, request: Arc, - router: Option>, + middlewares: Option>, wrapper: Option>>, } @@ -523,7 +525,7 @@ impl HttpServer { loop { tokio::select! { Some(req) = rx.recv() => { - let response = call_python_handler(&req.router, &req.match_route, &req.request, self.is_async) + let response = call_python_handler(&req.middlewares, &req.match_route, &req.request, self.is_async) .await .unwrap_or_else(Response::from) .call_wrapper(&req); @@ -537,30 +539,29 @@ impl HttpServer { } async fn call_python_handler<'l>( - router: &Option>, + middlewares: &Option>, match_route: &Option>, request: &Request, is_async: bool, ) -> PyResult { - if let Some(match_route) = match_route - && let Some(router) = router - { + if let Some(match_route) = match_route { let mut result = Python::attach(|py| { let route = match_route.value; let params = &match_route.params; let kwargs = build_route_params(py, params)?; - if router.middlewares.is_empty() { - route.handler.call(py, (request.clone(),), Some(&kwargs)) - } else { - let chain = MiddlewareChain::new(&router.middlewares); - chain.execute( - py, - route.sequence, - &*route.handler, - (request.clone(),), - kwargs.clone(), - ) + match middlewares { + Some(middlewares) => { + let chain = MiddlewareChain::new(middlewares); + chain.execute( + py, + route.sequence, + &*route.handler, + (request.clone(),), + kwargs.clone(), + ) + } + None => route.handler.call(py, (request.clone(),), Some(&kwargs)), } })?; diff --git a/src/request.rs b/src/request.rs index 6e9336b..252512b 100644 --- a/src/request.rs +++ b/src/request.rs @@ -13,13 +13,13 @@ use hyper::Uri; use pyo3_stub_gen::derive::*; use url::form_urlencoded; -use crate::routing::MatchRoute; +use crate::response::Response; use crate::status::Status; use crate::{ Context, IntoPyException, ProcessRequest, json, multipart::File, templating::Template, }; +use crate::{middleware::Middleware, routing::MatchRoute}; use crate::{multipart::parse_multipart, response::Body}; -use crate::{response::Response, routing::Router}; /// HTTP request object containing information about the incoming request. /// @@ -252,7 +252,7 @@ impl Request { for router in &ctx.routers { if let Some(match_route) = router.find(&self.method, &self.uri) { let response = self - .handle_found_route(&ctx, router.clone(), match_route) + .handle_found_route(&ctx, match_route, router.middlewares.clone()) .await; return response; } @@ -263,8 +263,8 @@ impl Request { async fn handle_found_route( &self, ctx: &Context, - router: Arc, match_route: MatchRoute<'_>, + middlewares: Vec, ) -> Result, hyper::http::Error> { let (tx, rx) = tokio::sync::mpsc::channel(ctx.channel_capacity); @@ -274,7 +274,7 @@ impl Request { tx, match_route: Some(transmutate_route), request: Arc::new(self.clone()), - router: Some(router), + middlewares: Some(middlewares), wrapper: ctx.wrapper.clone(), }; @@ -288,10 +288,10 @@ impl Request { let (tx, rx) = tokio::sync::mpsc::channel(ctx.channel_capacity); let process_request = ProcessRequest { + tx, request: Arc::new(self), - router: None, + middlewares: None, match_route: None, - tx, wrapper: ctx.wrapper.clone(), }; From 30351016cb9a324eb43d3bcaaddf7b6ffd60227e Mon Sep 17 00:00:00 2001 From: FITAHIANA Nomeniavo Joe <24nomeniavo@gmail.com> Date: Sun, 2 Aug 2026 12:53:45 +0300 Subject: [PATCH 3/6] chore: make test sync --- tests/app.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/app.py b/tests/app.py index 371ad31..01fd5f0 100644 --- a/tests/app.py +++ b/tests/app.py @@ -1,5 +1,3 @@ -import asyncio - from oxapy import Oxapy, Router, get @@ -8,13 +6,12 @@ def greet(_r, name: str): return f"Hello, {name}!" -async def main(): +def main(): oxapy = Oxapy(("0.0.0.0", 5555)) oxapy.set_patterns(["*.py", "*.html"]) oxapy.attach(Router().route(greet).route(get("/health", lambda _: "Good"))) - oxapy.async_mode() - await oxapy.run(reload=True) + oxapy.run(reload=True) if __name__ == "__main__": - asyncio.run(main()) + main() From 5d354a94cbc4b23935bc28a4481b9f839ac8cec2 Mon Sep 17 00:00:00 2001 From: FITAHIANA Nomeniavo Joe <24nomeniavo@gmail.com> Date: Sun, 2 Aug 2026 16:40:30 +0300 Subject: [PATCH 4/6] chore: slice middlewares --- src/lib.rs | 49 +++++++++++++++++++++++------------------------ src/middleware.rs | 28 +++++++++++++-------------- src/request.rs | 22 ++++++++++----------- src/routing.rs | 4 +--- 4 files changed, 50 insertions(+), 53 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 94ae629..0d4987a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,21 +45,21 @@ pyo3_stub_gen::export_verbatim!("oxapy", "from typing_extensions import Self"); pyo3_stub_gen::define_stub_info_gatherer!(stub_info); struct ProcessRequest { - tx: Sender, match_route: Option>, + middlewares: Option>, request: Arc, - middlewares: Option>, + response_sender: Sender, wrapper: Option>>, } #[derive(Clone)] struct Context { app_data: Option>>, - wrapper: Option>>, channel_capacity: usize, - routers: Vec>, request_sender: Sender, + routers: Vec>, template: Option>, + wrapper: Option>>, } struct ShutDownSignal { @@ -457,18 +457,18 @@ impl HttpServer { println!("Listening on {}", self.addr); let shutdown = ShutDownSignal::new()?; - let (tx, rx) = channel::(self.channel_capacity); + let (request_sender, request_receiver) = channel::(self.channel_capacity); let ctx = Context { app_data: self.app_data.clone(), - wrapper: self.wrapper.clone(), channel_capacity: self.channel_capacity, + request_sender, routers: self.routers.clone(), - request_sender: tx, template: self.template.clone(), + wrapper: self.wrapper.clone(), }; self.spawn_connection_handler(listener, Arc::new(ctx)).await; - self.process_requests(shutdown, rx).await + self.process_requests(shutdown, request_receiver).await } async fn spawn_connection_handler(&self, listener: TcpListener, ctx: Arc) { @@ -502,13 +502,14 @@ impl HttpServer { hyper::service::service_fn(move |req| { let ctx = ctx.clone(); async move { - let request = RequestBuilder::new(req) + RequestBuilder::new(req) .with_app_data(&ctx.app_data) .with_template(&ctx.template) .build() .await - .unwrap(); - request.process(ctx).await + .unwrap() + .process(ctx) + .await } }), ) @@ -520,16 +521,16 @@ impl HttpServer { async fn process_requests( &self, mut shutdown: ShutDownSignal, - mut rx: Receiver, + mut request_receiver: Receiver, ) -> PyResult<()> { loop { tokio::select! { - Some(req) = rx.recv() => { + Some(req) = request_receiver.recv() => { let response = call_python_handler(&req.middlewares, &req.match_route, &req.request, self.is_async) .await .unwrap_or_else(Response::from) .call_wrapper(&req); - let _ = req.tx.send(response).await; + let _ = req.response_sender.send(response).await; }, _ = shutdown.wait() => break, } @@ -539,7 +540,7 @@ impl HttpServer { } async fn call_python_handler<'l>( - middlewares: &Option>, + middlewares: &Option>, match_route: &Option>, request: &Request, is_async: bool, @@ -551,16 +552,14 @@ async fn call_python_handler<'l>( let kwargs = build_route_params(py, params)?; match middlewares { - Some(middlewares) => { - let chain = MiddlewareChain::new(middlewares); - chain.execute( - py, - route.sequence, - &*route.handler, - (request.clone(),), - kwargs.clone(), - ) - } + Some(middlewares) => MiddlewareChain::execute( + py, + middlewares, + route.sequence, + &*route.handler, + (request.clone(),), + kwargs.clone(), + ), None => route.handler.call(py, (request.clone(),), Some(&kwargs)), } })?; diff --git a/src/middleware.rs b/src/middleware.rs index 0396faa..b042887 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -17,18 +17,12 @@ impl Middleware { } } -pub struct MiddlewareChain<'l> { - middlewares: &'l [Middleware], -} - -impl<'l> MiddlewareChain<'l> { - pub fn new(middlewares: &'l [Middleware]) -> Self { - Self { middlewares } - } +pub struct MiddlewareChain; +impl MiddlewareChain { pub fn execute<'py, A>( - &self, py: Python<'py>, + middlewares: &[Middleware], route_sequence: usize, route_handler: &Py, args: A, @@ -37,25 +31,31 @@ impl<'l> MiddlewareChain<'l> { where A: PyCallArgs<'py>, { - let handler = self.build_middleware_chain(py, route_sequence, route_handler, 0)?; + let handler = + Self::build_middleware_chain(py, middlewares, route_sequence, route_handler, 0)?; handler.call(py, args, Some(&kwargs)) } fn build_middleware_chain( - &self, py: Python<'_>, + middlewares: &[Middleware], route_sequence: usize, route_handler: &Py, index: usize, ) -> PyResult> { - let Some(middleware) = self - .middlewares + let Some(middleware) = middlewares .get(index) .filter(|m| m.sequence <= route_sequence) else { return Ok(route_handler.clone_ref(py)); }; - let next = self.build_middleware_chain(py, route_sequence, route_handler, index + 1)?; + let next = Self::build_middleware_chain( + py, + middlewares, + route_sequence, + route_handler, + index + 1, + )?; let globals = PyDict::new(py); globals.set_item("middleware", middleware.handler.clone_ref(py))?; globals.set_item("next", next)?; diff --git a/src/request.rs b/src/request.rs index 252512b..2c0964a 100644 --- a/src/request.rs +++ b/src/request.rs @@ -252,7 +252,7 @@ impl Request { for router in &ctx.routers { if let Some(match_route) = router.find(&self.method, &self.uri) { let response = self - .handle_found_route(&ctx, match_route, router.middlewares.clone()) + .handle_found_route(&ctx, match_route, &router.middlewares) .await; return response; } @@ -264,38 +264,38 @@ impl Request { &self, ctx: &Context, match_route: MatchRoute<'_>, - middlewares: Vec, + middlewares: &[Middleware], ) -> Result, hyper::http::Error> { - let (tx, rx) = tokio::sync::mpsc::channel(ctx.channel_capacity); + let (response_sender, response_receiver) = tokio::sync::mpsc::channel(ctx.channel_capacity); let transmutate_route: MatchRoute<'static> = unsafe { std::mem::transmute(match_route) }; let process_request = ProcessRequest { - tx, match_route: Some(transmutate_route), + middlewares: Some(Arc::from(middlewares)), request: Arc::new(self.clone()), - middlewares: Some(middlewares), + response_sender, wrapper: ctx.wrapper.clone(), }; - Self::send_and_wait_response(&ctx.request_sender, process_request, rx).await + Self::send_and_wait_response(&ctx.request_sender, process_request, response_receiver).await } async fn handle_not_found( self, ctx: &Context, ) -> Result, hyper::http::Error> { - let (tx, rx) = tokio::sync::mpsc::channel(ctx.channel_capacity); + let (response_sender, response_receiver) = tokio::sync::mpsc::channel(ctx.channel_capacity); let process_request = ProcessRequest { - tx, - request: Arc::new(self), - middlewares: None, match_route: None, + middlewares: None, + request: Arc::new(self), + response_sender, wrapper: ctx.wrapper.clone(), }; - Self::send_and_wait_response(&ctx.request_sender, process_request, rx).await + Self::send_and_wait_response(&ctx.request_sender, process_request, response_receiver).await } async fn send_and_wait_response( diff --git a/src/routing.rs b/src/routing.rs index 9117313..7873d8d 100644 --- a/src/routing.rs +++ b/src/routing.rs @@ -393,9 +393,7 @@ impl Router { pub fn new(base_path: Option) -> Self { Router { base_path, - count: 0, - middlewares: Vec::new(), - routes: HashMap::default(), + ..Default::default() } } From f150d06282909516232483b5b2cb21f83444e7c8 Mon Sep 17 00:00:00 2001 From: FITAHIANA Nomeniavo Joe <24nomeniavo@gmail.com> Date: Sun, 2 Aug 2026 23:40:56 +0300 Subject: [PATCH 5/6] chore: remove transmute in find method --- src/routing.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/routing.rs b/src/routing.rs index 7873d8d..a5e9271 100644 --- a/src/routing.rs +++ b/src/routing.rs @@ -1,4 +1,4 @@ -use std::{mem::transmute, sync::Arc}; +use std::sync::Arc; use ahash::HashMap; use pyo3::{Py, PyAny, prelude::*}; @@ -358,7 +358,6 @@ impl Router { let path = uri.split('?').next().unwrap_or(uri); let router = self.routes.get(method)?; let route = router.at(path).ok()?; - let route: MatchRoute = unsafe { transmute(route) }; Some(route) } } From 14cca46d44a133274068df00d47a8386e5f9a9e7 Mon Sep 17 00:00:00 2001 From: FITAHIANA Nomeniavo Joe <24nomeniavo@gmail.com> Date: Mon, 3 Aug 2026 12:26:31 +0300 Subject: [PATCH 6/6] chore: improve code as possible --- src/lib.rs | 12 ++++++------ src/request.rs | 6 +++--- src/routing.rs | 11 +++++++++-- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0d4987a..7744df0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -525,12 +525,12 @@ impl HttpServer { ) -> PyResult<()> { loop { tokio::select! { - Some(req) = request_receiver.recv() => { - let response = call_python_handler(&req.middlewares, &req.match_route, &req.request, self.is_async) + Some(pr) = request_receiver.recv() => { + let response = call_python_handler(&pr.middlewares, &pr.match_route, &pr.request, self.is_async) .await .unwrap_or_else(Response::from) - .call_wrapper(&req); - let _ = req.response_sender.send(response).await; + .call_wrapper(&pr); + let _ = pr.response_sender.send(response).await; }, _ = shutdown.wait() => break, } @@ -556,9 +556,9 @@ async fn call_python_handler<'l>( py, middlewares, route.sequence, - &*route.handler, + &route.handler, (request.clone(),), - kwargs.clone(), + kwargs, ), None => route.handler.call(py, (request.clone(),), Some(&kwargs)), } diff --git a/src/request.rs b/src/request.rs index 2c0964a..ff112d7 100644 --- a/src/request.rs +++ b/src/request.rs @@ -252,7 +252,7 @@ impl Request { for router in &ctx.routers { if let Some(match_route) = router.find(&self.method, &self.uri) { let response = self - .handle_found_route(&ctx, match_route, &router.middlewares) + .handle_found_route(&ctx, match_route, router.middlewares.clone()) .await; return response; } @@ -264,7 +264,7 @@ impl Request { &self, ctx: &Context, match_route: MatchRoute<'_>, - middlewares: &[Middleware], + middlewares: Option>, ) -> Result, hyper::http::Error> { let (response_sender, response_receiver) = tokio::sync::mpsc::channel(ctx.channel_capacity); @@ -272,7 +272,7 @@ impl Request { let process_request = ProcessRequest { match_route: Some(transmutate_route), - middlewares: Some(Arc::from(middlewares)), + middlewares, request: Arc::new(self.clone()), response_sender, wrapper: ctx.wrapper.clone(), diff --git a/src/routing.rs b/src/routing.rs index a5e9271..73297d3 100644 --- a/src/routing.rs +++ b/src/routing.rs @@ -349,7 +349,7 @@ methods!( pub struct Router { pub base_path: Option, pub count: usize, - pub middlewares: Vec, + pub middlewares: Option>, pub routes: HashMap>, } @@ -422,7 +422,14 @@ impl Router { fn middleware(mut slf: PyRefMut<'_, Self>, middleware: Py) -> PyRefMut<'_, Self> { let middleware = Middleware::new(middleware, slf.count); slf.count += 1; - slf.middlewares.push(middleware); + let mut current_middlewares = slf + .middlewares + .take() + .map(|arc| arc.to_vec()) + .unwrap_or_default(); + + current_middlewares.push(middleware); + slf.middlewares = Some(Arc::from(current_middlewares)); slf }