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 {
/// Check if request matches predicate
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.
@ -58,29 +61,37 @@ pub trait Guard {
/// ```
pub fn fn_guard<F>(f: F) -> impl Guard
where
F: Fn(&RequestHead) -> bool,
F: Fn(&RequestHead) -> bool + Clone + 'static,
{
FnGuard(f)
}
struct FnGuard<F: Fn(&RequestHead) -> bool>(F);
struct FnGuard<F: Fn(&RequestHead) -> bool + Clone>(F);
impl<F> Guard for FnGuard<F>
where
F: Fn(&RequestHead) -> bool,
F: Fn(&RequestHead) -> bool + Clone + 'static,
{
fn check(&self, head: &RequestHead) -> bool {
(self.0)(head)
}
fn clone_guard(&self) -> Box<dyn Guard> {
Box::new(FnGuard(self.0.clone()))
}
}
impl<F> Guard for F
where
F: Fn(&RequestHead) -> bool,
F: Fn(&RequestHead) -> bool + Clone + 'static,
{
fn check(&self, head: &RequestHead) -> bool {
(self)(head)
}
fn clone_guard(&self) -> Box<dyn Guard> {
Box::new(FnGuard(self.clone()))
}
}
/// Return guard that matches if any of supplied guards.
@ -120,6 +131,10 @@ impl Guard for AnyGuard {
}
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.
@ -160,6 +175,10 @@ impl Guard for AllGuard {
}
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.
@ -174,6 +193,10 @@ impl Guard for NotGuard {
fn check(&self, request: &RequestHead) -> bool {
!self.0.check(request)
}
fn clone_guard(&self) -> Box<dyn Guard> {
self.0.clone_guard()
}
}
/// Http method guard
@ -184,6 +207,10 @@ impl Guard for MethodGuard {
fn check(&self, request: &RequestHead) -> bool {
request.method == self.0
}
fn clone_guard(&self) -> Box<dyn Guard> {
Box::new(MethodGuard(self.0.clone()))
}
}
/// Guard to match *GET* http method
@ -255,6 +282,10 @@ impl Guard for HeaderGuard {
}
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.
@ -319,6 +350,10 @@ impl Guard for HostGuard {
true
}
fn clone_guard(&self) -> Box<dyn Guard> {
Box::new(HostGuard(self.0.clone(), self.1.clone()))
}
}
#[cfg(test)]

View File

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