Skip to main content

HttpAclMiddleware

Struct HttpAclMiddleware 

Source
pub struct HttpAclMiddleware { /* private fields */ }
Expand description

A reqwest middleware that enforces an HttpAcl.

On each request, checks (in order) the scheme, method, host or IP, port, headers, URL path, and finally any custom ValidateFn, returning reqwest_middleware::Error::Middleware on the first denial. This alone only covers the request as originally built: attach Self::dns_resolver to the Client as well, so domains are checked against the ACL as they resolve, and Self::redirect_policy, so redirect targets are checked too. See the crate-level documentation for a full example wiring all three together.

If the HttpAcl has a ModifyRequestFn and/or ModifyResponseFn attached (see HttpAclHooks), those run too - a ModifyRequestFn once all the checks above pass and just before the request is sent, and a ModifyResponseFn on the way back, before the caller ever sees the Response. Neither is applied when not configured - see HttpAcl::has_modify_request/has_modify_response. A configured ModifyResponseFn forces the whole response body to be buffered and the response rebuilt from scratch; see the crate README for the performance trade-off.

Implementations§

Source§

impl HttpAclMiddleware

Source

pub fn new(acl: HttpAcl) -> Self

Create a new HTTP ACL middleware from an already-built HttpAcl.

Source

pub fn acl(&self) -> Arc<HttpAcl> ⓘ

Get the HttpAcl this middleware enforces.

Source

pub fn dns_resolver(&self) -> Arc<HttpAclDnsResolver> ⓘ

Create a DNS resolver that enforces the ACL, using getaddrinfo to actually resolve hostnames.

Set via Client::builder().dns_resolver(...). Without this, a domain that resolves to a denied or non-global IP (the classic SSRF vector) is never checked, since HttpAclMiddleware only ever sees the request as built, not the address it eventually connects to.

Source

pub fn with_dns_resolver( &self, dns_resolver: Arc<dyn Resolve>, ) -> Arc<HttpAclDnsResolver> ⓘ

Same as Self::dns_resolver, but delegating actual resolution to a custom Resolve implementation instead of getaddrinfo.

Source

pub fn redirect_policy(&self) -> Policy

Create a redirect::Policy that enforces the ACL on every redirect hop.

§Why this is necessary

HttpAclMiddleware only validates the request it is given. By default reqwest follows HTTP redirects internally (up to 10 hops) before control ever returns to the middleware chain, so a server an allowed host redirects to - e.g. a 302 to http://169.254.169.254/ - is never re-checked against the ACL. Set this policy on the Client (in addition to Self::dns_resolver) to close that gap:

let client = reqwest::Client::builder()
    .dns_resolver(middleware.dns_resolver())
    .redirect(middleware.redirect_policy())
    .build()
    .unwrap();

Uses a maximum of 10 redirects, matching reqwest’s own default. Use Self::redirect_policy_with_max to customise this.

§Limitations

Only the scheme, host/IP, port, and URL path of each redirect target can be checked this way - reqwest’s redirect policy does not expose the headers or body of the redirected request, so denied headers, denied bodies, and any custom validate_fn are not re-evaluated per hop. ModifyResponseFn only ever sees the final response of a redirect chain, never an intermediate 3xx, for the same reason.

ModifyRequestFn is different: it’s called once, against the original outgoing request, before Middleware::handle hands it to reqwest, but a header it injects is - like any header set before send() - carried forward by reqwest’s own redirect handling to every subsequent hop (reqwest may still strip specific headers, e.g. Authorization, when a redirect crosses origins). So an injected header reaches the whole chain even though the closure itself does not run again.

Source

pub fn redirect_policy_with_max(&self, max_redirects: usize) -> Policy

Same as Self::redirect_policy, but with a custom maximum number of redirects.

Trait Implementations§

Source§

impl Clone for HttpAclMiddleware

Source§

fn clone(&self) -> HttpAclMiddleware

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 Debug for HttpAclMiddleware

Source§

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

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

impl Middleware for HttpAclMiddleware

Source§

fn handle<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, req: Request, extensions: &'life1 mut Extensions, next: Next<'life2>, ) -> Pin<Box<dyn Future<Output = Result<Response, Error>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Invoked with a request before sending it. If you want to continue processing the request, you should explicitly call next.run(req, extensions). Read more

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<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> 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> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
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<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