1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-28 09:42:40 +01:00
actix-extras/src/pred.rs

405 lines
11 KiB
Rust
Raw Normal View History

2017-12-04 22:32:05 +01:00
//! Route match predicates
#![allow(non_snake_case)]
use http;
use http::{header, HttpTryFrom};
use httpmessage::HttpMessage;
2017-12-04 22:32:05 +01:00
use httprequest::HttpRequest;
2018-04-14 01:02:01 +02:00
use std::marker::PhantomData;
2017-12-04 22:32:05 +01:00
/// Trait defines resource route predicate.
/// Predicate can modify request object. It is also possible to
2018-03-31 18:18:25 +02:00
/// to store extra attributes on request by using `Extensions` container,
/// Extensions container available via `HttpRequest::extensions()` method.
2017-12-04 22:32:05 +01:00
pub trait Predicate<S> {
/// Check if request matches predicate
fn check(&self, &mut HttpRequest<S>) -> bool;
}
/// Return predicate that matches if any of supplied predicate matches.
2017-12-20 22:23:50 +01:00
///
/// ```rust
/// # extern crate actix_web;
2018-03-31 09:16:55 +02:00
/// use actix_web::{pred, App, HttpResponse};
2017-12-20 22:23:50 +01:00
///
/// fn main() {
2018-06-01 18:37:14 +02:00
/// App::new().resource("/index.html", |r| {
/// r.route()
2018-03-02 03:32:31 +01:00
/// .filter(pred::Any(pred::Get()).or(pred::Post()))
2018-06-01 18:37:14 +02:00
/// .f(|r| HttpResponse::MethodNotAllowed())
/// });
2017-12-20 22:23:50 +01:00
/// }
/// ```
2018-04-14 01:02:01 +02:00
pub fn Any<S: 'static, P: Predicate<S> + 'static>(pred: P) -> AnyPredicate<S> {
2017-12-20 22:23:50 +01:00
AnyPredicate(vec![Box::new(pred)])
2017-12-04 22:32:05 +01:00
}
2017-12-20 22:23:50 +01:00
/// Matches if any of supplied predicate matches.
pub struct AnyPredicate<S>(Vec<Box<Predicate<S>>>);
impl<S> AnyPredicate<S> {
/// Add new predicate to list of predicates to check
pub fn or<P: Predicate<S> + 'static>(mut self, pred: P) -> Self {
self.0.push(Box::new(pred));
self
}
}
2017-12-04 22:32:05 +01:00
impl<S: 'static> Predicate<S> for AnyPredicate<S> {
fn check(&self, req: &mut HttpRequest<S>) -> bool {
for p in &self.0 {
if p.check(req) {
2018-04-14 01:02:01 +02:00
return true;
2017-12-04 22:32:05 +01:00
}
}
false
}
}
/// Return predicate that matches if all of supplied predicate matches.
2017-12-20 22:23:50 +01:00
///
/// ```rust
/// # extern crate actix_web;
2018-04-09 19:40:12 +02:00
/// use actix_web::{pred, App, HttpResponse};
2017-12-20 22:23:50 +01:00
///
/// fn main() {
2018-06-01 18:37:14 +02:00
/// App::new().resource("/index.html", |r| {
/// r.route()
/// .filter(
/// pred::All(pred::Get())
/// .and(pred::Header("content-type", "plain/text")),
/// )
/// .f(|_| HttpResponse::MethodNotAllowed())
/// });
2017-12-20 22:23:50 +01:00
/// }
/// ```
pub fn All<S: 'static, P: Predicate<S> + 'static>(pred: P) -> AllPredicate<S> {
AllPredicate(vec![Box::new(pred)])
2017-12-04 22:32:05 +01:00
}
2017-12-20 22:23:50 +01:00
/// Matches if all of supplied predicate matches.
pub struct AllPredicate<S>(Vec<Box<Predicate<S>>>);
impl<S> AllPredicate<S> {
/// Add new predicate to list of predicates to check
pub fn and<P: Predicate<S> + 'static>(mut self, pred: P) -> Self {
self.0.push(Box::new(pred));
self
}
}
2017-12-04 22:32:05 +01:00
impl<S: 'static> Predicate<S> for AllPredicate<S> {
fn check(&self, req: &mut HttpRequest<S>) -> bool {
for p in &self.0 {
if !p.check(req) {
2018-04-14 01:02:01 +02:00
return false;
2017-12-04 22:32:05 +01:00
}
}
true
}
}
/// Return predicate that matches if supplied predicate does not match.
2018-04-14 01:02:01 +02:00
pub fn Not<S: 'static, P: Predicate<S> + 'static>(pred: P) -> NotPredicate<S> {
2017-12-20 22:23:50 +01:00
NotPredicate(Box::new(pred))
2017-12-04 22:32:05 +01:00
}
2017-12-20 22:23:50 +01:00
#[doc(hidden)]
pub struct NotPredicate<S>(Box<Predicate<S>>);
2017-12-04 22:32:05 +01:00
impl<S: 'static> Predicate<S> for NotPredicate<S> {
fn check(&self, req: &mut HttpRequest<S>) -> bool {
!self.0.check(req)
}
}
/// Http method predicate
2017-12-20 22:23:50 +01:00
#[doc(hidden)]
pub struct MethodPredicate<S>(http::Method, PhantomData<S>);
2017-12-04 22:32:05 +01:00
impl<S: 'static> Predicate<S> for MethodPredicate<S> {
fn check(&self, req: &mut HttpRequest<S>) -> bool {
*req.method() == self.0
}
}
/// Predicate to match *GET* http method
2017-12-20 22:23:50 +01:00
pub fn Get<S: 'static>() -> MethodPredicate<S> {
MethodPredicate(http::Method::GET, PhantomData)
2017-12-04 22:32:05 +01:00
}
/// Predicate to match *POST* http method
2017-12-20 22:23:50 +01:00
pub fn Post<S: 'static>() -> MethodPredicate<S> {
MethodPredicate(http::Method::POST, PhantomData)
2017-12-04 22:32:05 +01:00
}
/// Predicate to match *PUT* http method
2017-12-20 22:23:50 +01:00
pub fn Put<S: 'static>() -> MethodPredicate<S> {
MethodPredicate(http::Method::PUT, PhantomData)
2017-12-04 22:32:05 +01:00
}
/// Predicate to match *DELETE* http method
2017-12-20 22:23:50 +01:00
pub fn Delete<S: 'static>() -> MethodPredicate<S> {
MethodPredicate(http::Method::DELETE, PhantomData)
2017-12-04 22:32:05 +01:00
}
/// Predicate to match *HEAD* http method
2017-12-20 22:23:50 +01:00
pub fn Head<S: 'static>() -> MethodPredicate<S> {
MethodPredicate(http::Method::HEAD, PhantomData)
2017-12-04 22:32:05 +01:00
}
/// Predicate to match *OPTIONS* http method
2017-12-20 22:23:50 +01:00
pub fn Options<S: 'static>() -> MethodPredicate<S> {
MethodPredicate(http::Method::OPTIONS, PhantomData)
2017-12-04 22:32:05 +01:00
}
/// Predicate to match *CONNECT* http method
2017-12-20 22:23:50 +01:00
pub fn Connect<S: 'static>() -> MethodPredicate<S> {
MethodPredicate(http::Method::CONNECT, PhantomData)
2017-12-04 22:32:05 +01:00
}
/// Predicate to match *PATCH* http method
2017-12-20 22:23:50 +01:00
pub fn Patch<S: 'static>() -> MethodPredicate<S> {
MethodPredicate(http::Method::PATCH, PhantomData)
2017-12-04 22:32:05 +01:00
}
/// Predicate to match *TRACE* http method
2017-12-20 22:23:50 +01:00
pub fn Trace<S: 'static>() -> MethodPredicate<S> {
MethodPredicate(http::Method::TRACE, PhantomData)
2017-12-04 22:32:05 +01:00
}
/// Predicate to match specified http method
2017-12-20 22:23:50 +01:00
pub fn Method<S: 'static>(method: http::Method) -> MethodPredicate<S> {
MethodPredicate(method, PhantomData)
2017-12-04 22:32:05 +01:00
}
2018-04-14 01:02:01 +02:00
/// Return predicate that matches if request contains specified header and
/// value.
pub fn Header<S: 'static>(
2018-04-29 07:55:47 +02:00
name: &'static str, value: &'static str,
2018-04-14 01:02:01 +02:00
) -> HeaderPredicate<S> {
HeaderPredicate(
header::HeaderName::try_from(name).unwrap(),
header::HeaderValue::from_static(value),
PhantomData,
)
2017-12-04 22:32:05 +01:00
}
2017-12-20 22:23:50 +01:00
#[doc(hidden)]
2018-05-17 21:20:20 +02:00
pub struct HeaderPredicate<S>(header::HeaderName, header::HeaderValue, PhantomData<S>);
2017-12-04 22:32:05 +01:00
impl<S: 'static> Predicate<S> for HeaderPredicate<S> {
fn check(&self, req: &mut HttpRequest<S>) -> bool {
if let Some(val) = req.headers().get(&self.0) {
2018-04-14 01:02:01 +02:00
return val == self.1;
2017-12-04 22:32:05 +01:00
}
false
}
}
2017-12-08 21:51:44 +01:00
2018-06-08 05:00:54 +02:00
/// Return predicate that matches if request contains specified Host name.
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::{pred, App, HttpResponse};
///
/// fn main() {
/// App::new().resource("/index.html", |r| {
/// r.route()
/// .filter(pred::Host("www.rust-lang.org"))
/// .f(|_| HttpResponse::MethodNotAllowed())
/// });
/// }
/// ```
pub fn Host<S: 'static, H: AsRef<str>>(host: H) -> HostPredicate<S> {
HostPredicate(host.as_ref().to_string(), None, PhantomData)
}
#[doc(hidden)]
pub struct HostPredicate<S>(String, Option<String>, PhantomData<S>);
impl<S> HostPredicate<S> {
/// Set reuest scheme to match
pub fn scheme<H: AsRef<str>>(&mut self, scheme: H) {
self.1 = Some(scheme.as_ref().to_string())
}
}
impl<S: 'static> Predicate<S> for HostPredicate<S> {
fn check(&self, req: &mut HttpRequest<S>) -> bool {
let info = req.connection_info();
if let Some(ref scheme) = self.1 {
self.0 == info.host() && scheme == info.scheme()
} else {
self.0 == info.host()
}
}
}
2017-12-08 21:51:44 +01:00
#[cfg(test)]
mod tests {
use super::*;
use http::header::{self, HeaderMap};
2018-04-14 01:02:01 +02:00
use http::{Method, Uri, Version};
use std::str::FromStr;
2017-12-08 21:51:44 +01:00
#[test]
fn test_header() {
let mut headers = HeaderMap::new();
2018-04-14 01:02:01 +02:00
headers.insert(
header::TRANSFER_ENCODING,
header::HeaderValue::from_static("chunked"),
);
2017-12-08 21:51:44 +01:00
let mut req = HttpRequest::new(
2018-04-14 01:02:01 +02:00
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
2017-12-08 21:51:44 +01:00
let pred = Header("transfer-encoding", "chunked");
assert!(pred.check(&mut req));
let pred = Header("transfer-encoding", "other");
assert!(!pred.check(&mut req));
2017-12-09 00:25:37 +01:00
let pred = Header("content-type", "other");
2017-12-09 00:25:37 +01:00
assert!(!pred.check(&mut req));
2017-12-08 21:51:44 +01:00
}
2018-06-08 05:00:54 +02:00
#[test]
fn test_host() {
let mut headers = HeaderMap::new();
headers.insert(
header::HOST,
header::HeaderValue::from_static("www.rust-lang.org"),
);
let mut req = HttpRequest::new(
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
let pred = Host("www.rust-lang.org");
assert!(pred.check(&mut req));
let pred = Host("localhost");
assert!(!pred.check(&mut req));
}
2017-12-08 21:51:44 +01:00
#[test]
fn test_methods() {
let mut req = HttpRequest::new(
2018-04-14 01:02:01 +02:00
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
2017-12-08 21:51:44 +01:00
let mut req2 = HttpRequest::new(
2018-04-14 01:02:01 +02:00
Method::POST,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
2017-12-08 21:51:44 +01:00
assert!(Get().check(&mut req));
assert!(!Get().check(&mut req2));
assert!(Post().check(&mut req2));
assert!(!Post().check(&mut req));
let mut r = HttpRequest::new(
2018-04-14 01:02:01 +02:00
Method::PUT,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
2017-12-08 21:51:44 +01:00
assert!(Put().check(&mut r));
assert!(!Put().check(&mut req));
let mut r = HttpRequest::new(
2018-04-14 01:02:01 +02:00
Method::DELETE,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
2017-12-08 21:51:44 +01:00
assert!(Delete().check(&mut r));
assert!(!Delete().check(&mut req));
let mut r = HttpRequest::new(
2018-04-14 01:02:01 +02:00
Method::HEAD,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
2017-12-08 21:51:44 +01:00
assert!(Head().check(&mut r));
assert!(!Head().check(&mut req));
let mut r = HttpRequest::new(
2018-04-14 01:02:01 +02:00
Method::OPTIONS,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
2017-12-08 21:51:44 +01:00
assert!(Options().check(&mut r));
assert!(!Options().check(&mut req));
let mut r = HttpRequest::new(
2018-04-14 01:02:01 +02:00
Method::CONNECT,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
2017-12-08 21:51:44 +01:00
assert!(Connect().check(&mut r));
assert!(!Connect().check(&mut req));
let mut r = HttpRequest::new(
2018-04-14 01:02:01 +02:00
Method::PATCH,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
2017-12-08 21:51:44 +01:00
assert!(Patch().check(&mut r));
assert!(!Patch().check(&mut req));
let mut r = HttpRequest::new(
2018-04-14 01:02:01 +02:00
Method::TRACE,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
2017-12-08 21:51:44 +01:00
assert!(Trace().check(&mut r));
assert!(!Trace().check(&mut req));
}
#[test]
fn test_preds() {
let mut r = HttpRequest::new(
2018-04-14 01:02:01 +02:00
Method::TRACE,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
2017-12-08 21:51:44 +01:00
assert!(Not(Get()).check(&mut r));
assert!(!Not(Trace()).check(&mut r));
2017-12-20 22:23:50 +01:00
assert!(All(Trace()).and(Trace()).check(&mut r));
assert!(!All(Get()).and(Trace()).check(&mut r));
2017-12-08 21:51:44 +01:00
2017-12-20 22:23:50 +01:00
assert!(Any(Get()).or(Trace()).check(&mut r));
assert!(!Any(Get()).or(Get()).check(&mut r));
2017-12-08 21:51:44 +01:00
}
}