1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
use http::*;
use utils::ToRegex;
use utils::RequestContinuation;
use utils::RequestContinuation::*;
use regex::Regex;
use std::sync::RwLock;

/// Struct representing the layering of middlewares in the server
pub struct MiddlewareStack {
    middlewares: RwLock<Vec<(MiddlewareRule, Box<Middleware>)>>
}

impl MiddlewareStack {
    ///
    pub fn new() -> Self {
        MiddlewareStack {
            middlewares: RwLock::new(Vec::new()),
        }
    }

    ///
    pub fn resolve(&self, req: &SyncRequest, res: &mut Response<Body>) -> RequestContinuation {
        let path = req.path();

        for &(ref rule, ref middleware) in self.middlewares.read().unwrap().iter() {
            if rule.validate_path(path) {
                if let None = middleware.resolve(req, res) {
                    return None;
                }
            }
        }

        Next
    }

    /// Method to apply a new middleware onto the stack where the `include_path` vec are all path affected by the middleware,
    /// and `exclude_path` are exclusion amongst the included paths.
    pub fn apply<M: 'static + Middleware>(&mut self, m: M, include_path: Vec<&str>, exclude_path: Option<Vec<&str>>) {
        let rule = MiddlewareRule::new(include_path, exclude_path);
        let boxed_m = Box::new(m);

        self.middlewares.write().unwrap().push((rule, boxed_m))
    }
}

/// The trait a struct need to `impl` to be considered as a middleware
pub trait Middleware: Send + Sync {
    /// This method will be invoked if the request is targeting an included path, (as defined when "applying" the middleware to the stack)
    /// and doesn't match any exclusion. Returning `RequestContinuation::Next` will allow the request to continue through the stack, and
    /// returning `RequestContinuation::None` will cease the request processing, returning as response the modified `res` param.
    fn resolve(&self, req: &SyncRequest, res: &mut Response<Body>) -> RequestContinuation;
}

struct MiddlewareRule {
    included_path: Vec<Regex>,
    excluded_path: Option<Vec<Regex>>,
}

impl MiddlewareRule {
    pub fn new<R: ToRegex>(include_path: Vec<R>, exclude_path: Option<Vec<R>>) -> Self {
        let mut included_path = Vec::new();
        for include in include_path.iter() {
            included_path.push(reg!(include));
        }

        let mut excluded_path: Option<Vec<Regex>> = Option::None;

        if let Some(excludes) = exclude_path {
            let mut excludes_vec = Vec::new();
            for exclude in excludes.iter() {
                excludes_vec.push(reg!(exclude));
            }

            excluded_path = Some(excludes_vec);
        }

        MiddlewareRule {
            included_path,
            excluded_path,
        }
    }

    pub fn validate_path(&self, path: &str) -> bool {
        let path_clone = path.clone();
        if self.included_path.iter().enumerate().find(
            move |&(_index, r)| {
                r.is_match(path_clone)
            }
        ).is_some() {
            if let Some(ref excluded_path) = self.excluded_path {
                return excluded_path.iter().enumerate().find(
                    move |&(_index, re)| {
                        re.is_match(path_clone)
                    }
                ).is_none();
            } else {
                return true;
            }
        }

        false
    }
}