Skip to main content

PathPolicy

Enum PathPolicy 

Source
pub enum PathPolicy {
    Strict,
    Redirect,
    Collapse,
}
Expand description

What to do when a request path is a non-canonical spelling of a route.

Interior empty segments — //a, /a//b — were once collapsed silently, which gave every resource several URLs. That is not a traversal problem, but it is an aliasing one, and aliases have teeth:

  • Middleware, guards and proxy rules that key on a literal prefix (path.starts_with("/admin")) are bypassable with //admin.
  • Caches key on the URL, so one resource occupies several entries and an intermediary can disagree with the origin about identity.

A trailing slash is not treated as an alias — see canonical_path for why removing it would break directory listings.

Whatever the policy, %2F is never decoded into a separator: an encoded slash is data inside one segment, and decoding it early is how a normalisation change turns into a traversal bug.

use churust_core::{Churust, Call, PathPolicy, TestClient};
let app = Churust::server()
    .path_policy(PathPolicy::Redirect)
    .routing(|r| { r.get("/a", |_c: Call| async { "a" }); })
    .build();
let res = TestClient::new(app).get("//a").send().await;
assert_eq!(res.status(), http::StatusCode::PERMANENT_REDIRECT);
assert_eq!(res.header("location"), Some("/a"));

Variants§

§

Strict

Refuse an alias: //a is not /a, and gets 404.

The default. Fewest surprises and the strongest cache identity — one resource, one URL.

§

Redirect

Redirect an alias to its canonical form with 308 Permanent Redirect.

Kind to hand-typed and hand-written URLs. 308 rather than 301 so a POST stays a POST; 301 permits a client to retry as GET, which silently drops the body.

§

Collapse

Collapse aliases silently, serving them as if canonical.

The pre-PathPolicy behaviour, kept so an application with alias-shaped links has somewhere to stand while it fixes them. It creates URL aliases by design; prefer Strict or Redirect.

The collapsing happens in the router, for matching only: the URI is not rewritten, so call.path() still reports the spelling the client sent — //admin/secret, not /admin/secret — in middleware and in the handler alike. That is what makes the prefix-check bypass above live under this policy and only under this policy. Under Strict and Redirect a middleware also sees the raw spelling, but the request is refused or redirected before any handler runs, so nothing is served.

Trait Implementations§

Source§

impl Clone for PathPolicy

Source§

fn clone(&self) -> PathPolicy

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for PathPolicy

Source§

impl Debug for PathPolicy

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for PathPolicy

Source§

fn default() -> PathPolicy

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for PathPolicy

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Eq for PathPolicy

Source§

impl PartialEq for PathPolicy

Source§

fn eq(&self, other: &PathPolicy) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for PathPolicy

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more