optimize Route guards

This commit is contained in:
fakeshadow 2021-01-17 10:11:08 +08:00
parent 1c95fc2654
commit b187851a32
2 changed files with 50 additions and 18 deletions

View File

@ -38,6 +38,9 @@ use actix_http::RequestHead;
pub trait Guard { pub trait Guard {
/// Check if request matches predicate /// Check if request matches predicate
fn check(&self, request: &RequestHead) -> bool; fn check(&self, request: &RequestHead) -> bool;
/// Clone self into a new boxed guard.
fn clone_guard(&self) -> Box<dyn Guard>;
} }
/// Create guard object for supplied function. /// Create guard object for supplied function.
@ -58,29 +61,37 @@ pub trait Guard {
/// ``` /// ```
pub fn fn_guard<F>(f: F) -> impl Guard pub fn fn_guard<F>(f: F) -> impl Guard
where where
F: Fn(&RequestHead) -> bool, F: Fn(&RequestHead) -> bool + Clone + 'static,
{ {
FnGuard(f) FnGuard(f)
} }
struct FnGuard<F: Fn(&RequestHead) -> bool>(F); struct FnGuard<F: Fn(&RequestHead) -> bool + Clone>(F);
impl<F> Guard for FnGuard<F> impl<F> Guard for FnGuard<F>
where where
F: Fn(&RequestHead) -> bool, F: Fn(&RequestHead) -> bool + Clone + 'static,
{ {
fn check(&self, head: &RequestHead) -> bool { fn check(&self, head: &RequestHead) -> bool {
(self.0)(head) (self.0)(head)
} }
fn clone_guard(&self) -> Box<dyn Guard> {
Box::new(FnGuard(self.0.clone()))
}
} }
impl<F> Guard for F impl<F> Guard for F
where where
F: Fn(&RequestHead) -> bool, F: Fn(&RequestHead) -> bool + Clone + 'static,
{ {
fn check(&self, head: &RequestHead) -> bool { fn check(&self, head: &RequestHead) -> bool {
(self)(head) (self)(head)
} }
fn clone_guard(&self) -> Box<dyn Guard> {
Box::new(FnGuard(self.clone()))
}
} }
/// Return guard that matches if any of supplied guards. /// Return guard that matches if any of supplied guards.
@ -120,6 +131,10 @@ impl Guard for AnyGuard {
} }
false false
} }
fn clone_guard(&self) -> Box<dyn Guard> {
Box::new(AnyGuard(self.0.iter().map(|g| g.clone_guard()).collect()))
}
} }
/// Return guard that matches if all of the supplied guards. /// Return guard that matches if all of the supplied guards.
@ -160,6 +175,10 @@ impl Guard for AllGuard {
} }
true true
} }
fn clone_guard(&self) -> Box<dyn Guard> {
Box::new(AllGuard(self.0.iter().map(|g| g.clone_guard()).collect()))
}
} }
/// Return guard that matches if supplied guard does not match. /// Return guard that matches if supplied guard does not match.
@ -174,6 +193,10 @@ impl Guard for NotGuard {
fn check(&self, request: &RequestHead) -> bool { fn check(&self, request: &RequestHead) -> bool {
!self.0.check(request) !self.0.check(request)
} }
fn clone_guard(&self) -> Box<dyn Guard> {
self.0.clone_guard()
}
} }
/// Http method guard /// Http method guard
@ -184,6 +207,10 @@ impl Guard for MethodGuard {
fn check(&self, request: &RequestHead) -> bool { fn check(&self, request: &RequestHead) -> bool {
request.method == self.0 request.method == self.0
} }
fn clone_guard(&self) -> Box<dyn Guard> {
Box::new(MethodGuard(self.0.clone()))
}
} }
/// Guard to match *GET* http method /// Guard to match *GET* http method
@ -255,6 +282,10 @@ impl Guard for HeaderGuard {
} }
false false
} }
fn clone_guard(&self) -> Box<dyn Guard> {
Box::new(HeaderGuard(self.0.clone(), self.1.clone()))
}
} }
/// Return predicate that matches if request contains specified Host name. /// Return predicate that matches if request contains specified Host name.
@ -319,6 +350,10 @@ impl Guard for HostGuard {
true true
} }
fn clone_guard(&self) -> Box<dyn Guard> {
Box::new(HostGuard(self.0.clone(), self.1.clone()))
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -2,7 +2,6 @@
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use actix_http::{http::Method, Error}; use actix_http::{http::Method, Error};
@ -43,7 +42,7 @@ type BoxedRouteNewService = Box<
/// If handler is not explicitly set, default *404 Not Found* handler is used. /// If handler is not explicitly set, default *404 Not Found* handler is used.
pub struct Route { pub struct Route {
service: BoxedRouteNewService, service: BoxedRouteNewService,
guards: Rc<Vec<Box<dyn Guard>>>, guards: Vec<Box<dyn Guard>>,
} }
impl Route { impl Route {
@ -54,34 +53,34 @@ impl Route {
service: Box::new(RouteNewService::new(HandlerService::new(|| { service: Box::new(RouteNewService::new(HandlerService::new(|| {
ready(HttpResponse::NotFound()) ready(HttpResponse::NotFound())
}))), }))),
guards: Rc::new(Vec::new()), guards: Vec::new(),
} }
} }
pub(crate) fn take_guards(&mut self) -> Vec<Box<dyn Guard>> { pub(crate) fn take_guards(&mut self) -> Vec<Box<dyn Guard>> {
std::mem::take(Rc::get_mut(&mut self.guards).unwrap()) std::mem::take(&mut self.guards)
} }
} }
impl ServiceFactory<ServiceRequest> for Route { impl ServiceFactory<ServiceRequest> for Route {
type Config = ();
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
type InitError = (); type Config = ();
type Service = RouteService; type Service = RouteService;
type InitError = ();
type Future = CreateRouteService; type Future = CreateRouteService;
fn new_service(&self, _: ()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
CreateRouteService { CreateRouteService {
fut: self.service.new_service(()), fut: self.service.new_service(()),
guards: self.guards.clone(), guards: self.guards.iter().map(|g| g.clone_guard()).collect(),
} }
} }
} }
pub struct CreateRouteService { pub struct CreateRouteService {
fut: LocalBoxFuture<'static, Result<BoxedRouteService, ()>>, fut: LocalBoxFuture<'static, Result<BoxedRouteService, ()>>,
guards: Rc<Vec<Box<dyn Guard>>>, guards: Vec<Box<dyn Guard>>,
} }
impl Future for CreateRouteService { impl Future for CreateRouteService {
@ -93,7 +92,7 @@ impl Future for CreateRouteService {
match this.fut.as_mut().poll(cx)? { match this.fut.as_mut().poll(cx)? {
Poll::Ready(service) => Poll::Ready(Ok(RouteService { Poll::Ready(service) => Poll::Ready(Ok(RouteService {
service, service,
guards: this.guards.clone(), guards: std::mem::take(&mut this.guards),
})), })),
Poll::Pending => Poll::Pending, Poll::Pending => Poll::Pending,
} }
@ -102,7 +101,7 @@ impl Future for CreateRouteService {
pub struct RouteService { pub struct RouteService {
service: BoxedRouteService, service: BoxedRouteService,
guards: Rc<Vec<Box<dyn Guard>>>, guards: Vec<Box<dyn Guard>>,
} }
impl RouteService { impl RouteService {
@ -145,9 +144,7 @@ impl Route {
/// # } /// # }
/// ``` /// ```
pub fn method(mut self, method: Method) -> Self { pub fn method(mut self, method: Method) -> Self {
Rc::get_mut(&mut self.guards) self.guards.push(Box::new(guard::Method(method)));
.unwrap()
.push(Box::new(guard::Method(method)));
self self
} }
@ -165,7 +162,7 @@ impl Route {
/// # } /// # }
/// ``` /// ```
pub fn guard<F: Guard + 'static>(mut self, f: F) -> Self { pub fn guard<F: Guard + 'static>(mut self, f: F) -> Self {
Rc::get_mut(&mut self.guards).unwrap().push(Box::new(f)); self.guards.push(Box::new(f));
self self
} }