mod and;
mod clone_body_fn;
mod filter_credentials;
mod limited;
mod or;
mod redirect_fn;
mod same_origin;
pub use self::{
and::And,
clone_body_fn::{clone_body_fn, CloneBodyFn},
filter_credentials::FilterCredentials,
limited::Limited,
or::Or,
redirect_fn::{redirect_fn, RedirectFn},
same_origin::SameOrigin,
};
use http::{uri::Scheme, Request, StatusCode, Uri};
pub trait Policy<B, E> {
fn redirect(&mut self, attempt: &Attempt<'_>) -> Result<Action, E>;
fn on_request(&mut self, _request: &mut Request<B>) {}
fn clone_body(&self, _body: &B) -> Option<B> {
None
}
}
impl<B, E, P> Policy<B, E> for &mut P
where
P: Policy<B, E> + ?Sized,
{
fn redirect(&mut self, attempt: &Attempt<'_>) -> Result<Action, E> {
(**self).redirect(attempt)
}
fn on_request(&mut self, request: &mut Request<B>) {
(**self).on_request(request)
}
fn clone_body(&self, body: &B) -> Option<B> {
(**self).clone_body(body)
}
}
impl<B, E, P> Policy<B, E> for Box<P>
where
P: Policy<B, E> + ?Sized,
{
fn redirect(&mut self, attempt: &Attempt<'_>) -> Result<Action, E> {
(**self).redirect(attempt)
}
fn on_request(&mut self, request: &mut Request<B>) {
(**self).on_request(request)
}
fn clone_body(&self, body: &B) -> Option<B> {
(**self).clone_body(body)
}
}
pub trait PolicyExt {
fn and<P, B, E>(self, other: P) -> And<Self, P>
where
Self: Policy<B, E> + Sized,
P: Policy<B, E>;
fn or<P, B, E>(self, other: P) -> Or<Self, P>
where
Self: Policy<B, E> + Sized,
P: Policy<B, E>;
}
impl<T> PolicyExt for T
where
T: ?Sized,
{
fn and<P, B, E>(self, other: P) -> And<Self, P>
where
Self: Policy<B, E> + Sized,
P: Policy<B, E>,
{
And::new(self, other)
}
fn or<P, B, E>(self, other: P) -> Or<Self, P>
where
Self: Policy<B, E> + Sized,
P: Policy<B, E>,
{
Or::new(self, other)
}
}
pub type Standard = And<Limited, FilterCredentials>;
pub struct Attempt<'a> {
pub(crate) status: StatusCode,
pub(crate) location: &'a Uri,
pub(crate) previous: &'a Uri,
}
impl<'a> Attempt<'a> {
pub fn status(&self) -> StatusCode {
self.status
}
pub fn location(&self) -> &'a Uri {
self.location
}
pub fn previous(&self) -> &'a Uri {
self.previous
}
}
#[derive(Clone, Copy, Debug)]
pub enum Action {
Follow,
Stop,
}
impl Action {
pub fn is_follow(&self) -> bool {
if let Action::Follow = self {
true
} else {
false
}
}
pub fn is_stop(&self) -> bool {
if let Action::Stop = self {
true
} else {
false
}
}
}
impl<B, E> Policy<B, E> for Action {
fn redirect(&mut self, _: &Attempt<'_>) -> Result<Action, E> {
Ok(*self)
}
}
impl<B, E> Policy<B, E> for Result<Action, E>
where
E: Clone,
{
fn redirect(&mut self, _: &Attempt<'_>) -> Result<Action, E> {
self.clone()
}
}
fn eq_origin(lhs: &Uri, rhs: &Uri) -> bool {
let default_port = match (lhs.scheme(), rhs.scheme()) {
(Some(l), Some(r)) if l == r => {
if l == &Scheme::HTTP {
80
} else if l == &Scheme::HTTPS {
443
} else {
return false;
}
}
_ => return false,
};
match (lhs.host(), rhs.host()) {
(Some(l), Some(r)) if l == r => {}
_ => return false,
}
lhs.port_u16().unwrap_or(default_port) == rhs.port_u16().unwrap_or(default_port)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn eq_origin_works() {
assert!(eq_origin(
&Uri::from_static("https://example.com/1"),
&Uri::from_static("https://example.com/2")
));
assert!(eq_origin(
&Uri::from_static("https://example.com:443/"),
&Uri::from_static("https://example.com/")
));
assert!(eq_origin(
&Uri::from_static("https://example.com/"),
&Uri::from_static("https://user@example.com/")
));
assert!(!eq_origin(
&Uri::from_static("https://example.com/"),
&Uri::from_static("https://www.example.com/")
));
assert!(!eq_origin(
&Uri::from_static("https://example.com/"),
&Uri::from_static("http://example.com/")
));
}
}