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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
//! Defines the type `RouteMatcher` and default implementations.
mod accept;
mod access_control_request_method;
mod and;
mod any;
mod content_type;
pub use self::accept::AcceptHeaderRouteMatcher;
pub use self::access_control_request_method::AccessControlRequestMethodMatcher;
pub use self::and::AndRouteMatcher;
pub use self::any::AnyRouteMatcher;
pub use self::content_type::ContentTypeHeaderRouteMatcher;
mod lookup_table;
use self::lookup_table::{LookupTable, LookupTableFromTypes};
use std::panic::RefUnwindSafe;
use hyper::{Method, StatusCode};
use log::trace;
use crate::router::non_match::RouteNonMatch;
use crate::state::{request_id, FromState, State};
/// Determines if conditions required for the associated `Route` to be invoked by the `Router` have
/// been met.
pub trait RouteMatcher: RefUnwindSafe + Clone {
/// Determines if the `Request` meets pre-defined conditions.
fn is_match(&self, state: &State) -> Result<(), RouteNonMatch>;
}
/// Allow various types to represent themselves as a `RouteMatcher`
pub trait IntoRouteMatcher {
/// The concrete RouteMatcher each implementation will provide.
type Output: RouteMatcher;
/// Transform into a `RouteMatcher` of the the associated type identified by `Output`.
fn into_route_matcher(self) -> Self::Output;
}
impl IntoRouteMatcher for Vec<Method> {
type Output = MethodOnlyRouteMatcher;
fn into_route_matcher(self) -> Self::Output {
MethodOnlyRouteMatcher::new(self)
}
}
impl<M> IntoRouteMatcher for M
where
M: RouteMatcher + Send + Sync + 'static,
{
type Output = M;
fn into_route_matcher(self) -> Self::Output {
self
}
}
/// A `RouteMatcher` that succeeds when the `Request` has been made with an accepted HTTP request
/// method.
///
/// # Examples
///
/// ```rust
/// # extern crate gotham;
/// # extern crate hyper;
/// # fn main() {
/// # use hyper::Method;
/// # use gotham::state::State;
/// # use gotham::router::route::matcher::{RouteMatcher, MethodOnlyRouteMatcher};
/// #
/// # State::with_new(|state| {
/// #
/// let methods = vec![Method::GET, Method::HEAD];
/// let matcher = MethodOnlyRouteMatcher::new(methods);
///
/// state.put(Method::GET);
/// assert!(matcher.is_match(&state).is_ok());
///
/// state.put(Method::POST);
/// assert!(matcher.is_match(&state).is_err());
/// # });
/// # }
/// ```
#[derive(Clone)]
pub struct MethodOnlyRouteMatcher {
methods: Vec<Method>,
}
impl MethodOnlyRouteMatcher {
/// Creates a new `MethodOnlyRouteMatcher`.
pub fn new(methods: Vec<Method>) -> Self {
MethodOnlyRouteMatcher { methods }
}
}
impl RouteMatcher for MethodOnlyRouteMatcher {
/// Determines if the `Request` was made using a `Method` the instance contains.
fn is_match(&self, state: &State) -> Result<(), RouteNonMatch> {
let method = Method::borrow_from(state);
if self.methods.iter().any(|m| m == method) {
trace!(
"[{}] matched request method {} to permitted method",
request_id(state),
method
);
Ok(())
} else {
trace!(
"[{}] did not match request method {}",
request_id(state),
method
);
Err(RouteNonMatch::new(StatusCode::METHOD_NOT_ALLOWED)
.with_allow_list(self.methods.as_slice()))
}
}
}