Skip to main content

http_acl/
acl.rs

1//! Contains the [`HttpAcl`], [`HttpAclBuilder`],
2//! and related types.
3//!
4//! Each category an [`HttpAcl`] checks (scheme, method, host, port, IP, header, URL
5//! path) is evaluated the same way: the allow-list is checked first, then the
6//! deny-list, and if neither matches, the category's configured default (set via
7//! e.g. [`HttpAclBuilder::host_acl_default`]) decides the outcome. The allow-list
8//! always wins over the deny-list, so a broad allow entry can shadow a narrower deny
9//! entry in the same category. Every check returns an [`AclClassification`] rather
10//! than a plain `bool`, so callers can tell *why* something was allowed or denied;
11//! use [`AclClassification::is_allowed`]/[`AclClassification::is_denied`] where only
12//! the outcome matters.
13
14#[cfg(feature = "hashbrown")]
15use hashbrown::{HashMap, HashSet, hash_map::Entry};
16#[cfg(not(feature = "hashbrown"))]
17use std::collections::{HashMap, HashSet, hash_map::Entry};
18use std::hash::Hash;
19use std::net::{IpAddr, SocketAddr};
20use std::ops::RangeInclusive;
21use std::sync::Arc;
22
23use matchit::Router;
24#[cfg(feature = "serde")]
25use serde::{Deserialize, Serialize};
26
27use crate::{
28    error::AddError,
29    mutation::{ModifyRequestFn, ModifyResponseFn, RequestMutation, ResponseMutation},
30    utils::{
31        self, IntoIpRange,
32        authority::Authority,
33        host_pattern::{HostPattern, is_wildcard_host},
34    },
35};
36
37/// A function that validates an HTTP request against an ACL.
38///
39/// Called by [`HttpAcl::is_valid`] with the request's scheme, authority (host and
40/// port), headers, and optional body, in that order. It is the only hook for checks
41/// that don't fit the built-in categories (e.g. inspecting the body, or applying
42/// custom cross-field logic). Return [`AclClassification::DeniedUserAcl`] or
43/// [`AclClassification::Denied`] to reject the request, or
44/// [`AclClassification::AllowedDefault`] to let it through.
45///
46/// A `ValidateFn` is attached via [`HttpAclBuilder::build_full`] or
47/// [`HttpAclBuilder::try_build_full`] rather than a dedicated builder setter, since
48/// it is typically a closure that captures state from outside the builder.
49pub type ValidateFn = Arc<
50    dyn for<'h> Fn(
51            &str,
52            &Authority,
53            Box<dyn Iterator<Item = (&'h str, &'h str)> + Send + Sync + 'h>,
54            Option<&[u8]>,
55        ) -> AclClassification
56        + Send
57        + Sync,
58>;
59
60#[derive(Clone)]
61/// Represents an HTTP ACL.
62///
63/// Built via [`HttpAcl::builder`] (an [`HttpAclBuilder`]) rather than constructed
64/// directly. Once built, an `HttpAcl` is immutable; the various `is_*_allowed`
65/// methods check a single aspect of a request (scheme, method, host, port, IP,
66/// header, or URL path) and return an [`AclClassification`]. See the module-level
67/// documentation for how allow-lists, deny-lists, and per-category defaults combine.
68pub struct HttpAcl {
69    allow_http: bool,
70    allow_https: bool,
71    allowed_methods: HashSet<HttpRequestMethod>,
72    denied_methods: HashSet<HttpRequestMethod>,
73    allowed_hosts: HashSet<Box<str>>,
74    denied_hosts: HashSet<Box<str>>,
75    allowed_host_patterns: Box<[HostPattern]>,
76    denied_host_patterns: Box<[HostPattern]>,
77    allowed_port_ranges: Box<[RangeInclusive<u16>]>,
78    denied_port_ranges: Box<[RangeInclusive<u16>]>,
79    allowed_ip_ranges: Box<[RangeInclusive<IpAddr>]>,
80    denied_ip_ranges: Box<[RangeInclusive<IpAddr>]>,
81    static_dns_mapping: HashMap<Box<str>, SocketAddr>,
82    trusted_static_dns_mapping: HashMap<Box<str>, SocketAddr>,
83    allowed_headers: HashMap<Box<str>, Option<Box<str>>>,
84    denied_headers: HashMap<Box<str>, Option<Box<str>>>,
85    allowed_url_paths_router: Router<()>,
86    denied_url_paths_router: Router<()>,
87    validate_fn: Option<ValidateFn>,
88    modify_request_fn: Option<ModifyRequestFn>,
89    modify_response_fn: Option<ModifyResponseFn>,
90    allow_non_global_ip_ranges: bool,
91    method_acl_default: bool,
92    host_acl_default: bool,
93    port_acl_default: bool,
94    ip_acl_default: bool,
95    header_acl_default: bool,
96    url_path_acl_default: bool,
97}
98
99impl std::fmt::Debug for HttpAcl {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        f.debug_struct("HttpAcl")
102            .field("allow_http", &self.allow_http)
103            .field("allow_https", &self.allow_https)
104            .field("allowed_methods", &self.allowed_methods)
105            .field("denied_methods", &self.denied_methods)
106            .field("allowed_hosts", &self.allowed_hosts)
107            .field("denied_hosts", &self.denied_hosts)
108            .field("allowed_port_ranges", &self.allowed_port_ranges)
109            .field("denied_port_ranges", &self.denied_port_ranges)
110            .field("allowed_ip_ranges", &self.allowed_ip_ranges)
111            .field("denied_ip_ranges", &self.denied_ip_ranges)
112            .field("static_dns_mapping", &self.static_dns_mapping)
113            .field(
114                "trusted_static_dns_mapping",
115                &self.trusted_static_dns_mapping,
116            )
117            .field("allowed_headers", &self.allowed_headers)
118            .field("denied_headers", &self.denied_headers)
119            .field(
120                "allow_non_global_ip_ranges",
121                &self.allow_non_global_ip_ranges,
122            )
123            .field("method_acl_default", &self.method_acl_default)
124            .field("host_acl_default", &self.host_acl_default)
125            .field("port_acl_default", &self.port_acl_default)
126            .field("ip_acl_default", &self.ip_acl_default)
127            .field("header_acl_default", &self.header_acl_default)
128            .field("url_path_acl_default", &self.url_path_acl_default)
129            .finish()
130    }
131}
132
133impl PartialEq for HttpAcl {
134    fn eq(&self, other: &Self) -> bool {
135        self.allow_http == other.allow_http
136            && self.allow_https == other.allow_https
137            && self.allowed_methods == other.allowed_methods
138            && self.denied_methods == other.denied_methods
139            && self.allowed_hosts == other.allowed_hosts
140            && self.denied_hosts == other.denied_hosts
141            && self.allowed_port_ranges == other.allowed_port_ranges
142            && self.denied_port_ranges == other.denied_port_ranges
143            && self.allowed_ip_ranges == other.allowed_ip_ranges
144            && self.denied_ip_ranges == other.denied_ip_ranges
145            && self.static_dns_mapping == other.static_dns_mapping
146            && self.trusted_static_dns_mapping == other.trusted_static_dns_mapping
147            && self.allowed_headers == other.allowed_headers
148            && self.denied_headers == other.denied_headers
149            && self.allow_non_global_ip_ranges == other.allow_non_global_ip_ranges
150            && self.method_acl_default == other.method_acl_default
151            && self.host_acl_default == other.host_acl_default
152            && self.port_acl_default == other.port_acl_default
153            && self.ip_acl_default == other.ip_acl_default
154            && self.header_acl_default == other.header_acl_default
155            && self.url_path_acl_default == other.url_path_acl_default
156    }
157}
158
159impl std::default::Default for HttpAcl {
160    fn default() -> Self {
161        Self {
162            allow_http: true,
163            allow_https: true,
164            allowed_methods: [
165                HttpRequestMethod::CONNECT,
166                HttpRequestMethod::DELETE,
167                HttpRequestMethod::GET,
168                HttpRequestMethod::HEAD,
169                HttpRequestMethod::OPTIONS,
170                HttpRequestMethod::PATCH,
171                HttpRequestMethod::POST,
172                HttpRequestMethod::PUT,
173                HttpRequestMethod::TRACE,
174            ]
175            .into_iter()
176            .collect(),
177            denied_methods: HashSet::new(),
178            allowed_hosts: HashSet::new(),
179            denied_hosts: HashSet::new(),
180            allowed_host_patterns: Box::new([]),
181            denied_host_patterns: Box::new([]),
182            allowed_port_ranges: vec![80..=80, 443..=443].into_boxed_slice(),
183            denied_port_ranges: Vec::new().into_boxed_slice(),
184            allowed_ip_ranges: Vec::new().into_boxed_slice(),
185            denied_ip_ranges: Vec::new().into_boxed_slice(),
186            static_dns_mapping: HashMap::new(),
187            trusted_static_dns_mapping: HashMap::new(),
188            allowed_headers: HashMap::new(),
189            denied_headers: HashMap::new(),
190            allowed_url_paths_router: Router::new(),
191            denied_url_paths_router: Router::new(),
192            validate_fn: None,
193            modify_request_fn: None,
194            modify_response_fn: None,
195            allow_non_global_ip_ranges: false,
196            method_acl_default: false,
197            host_acl_default: false,
198            port_acl_default: false,
199            ip_acl_default: false,
200            header_acl_default: true,
201            url_path_acl_default: true,
202        }
203    }
204}
205
206impl HttpAcl {
207    /// Returns a new [`HttpAclBuilder`].
208    pub fn builder() -> HttpAclBuilder {
209        HttpAclBuilder::new()
210    }
211
212    /// Returns whether the scheme is allowed.
213    ///
214    /// Unlike the other `is_*_allowed` methods, this is a plain per-scheme flag (set
215    /// via [`HttpAclBuilder::http`]/[`HttpAclBuilder::https`]) rather than an
216    /// allow/deny/default check, so it only ever returns
217    /// [`AclClassification::AllowedUserAcl`] or [`AclClassification::DeniedUserAcl`].
218    /// Any scheme other than `"http"`/`"https"` is denied.
219    pub fn is_scheme_allowed(&self, scheme: &str) -> AclClassification {
220        if scheme == "http" && self.allow_http || scheme == "https" && self.allow_https {
221            AclClassification::AllowedUserAcl
222        } else {
223            AclClassification::DeniedUserAcl
224        }
225    }
226
227    /// Returns whether the method is allowed.
228    ///
229    /// Note: If you pass a string ensure it is uppercased first.
230    pub fn is_method_allowed(&self, method: impl Into<HttpRequestMethod>) -> AclClassification {
231        let method = method.into();
232        if self.allowed_methods.contains(&method) {
233            AclClassification::AllowedUserAcl
234        } else if self.denied_methods.contains(&method) {
235            AclClassification::DeniedUserAcl
236        } else if self.method_acl_default {
237            AclClassification::AllowedDefault
238        } else {
239            AclClassification::DeniedDefault
240        }
241    }
242
243    /// Returns whether the host is allowed.
244    ///
245    /// Hosts may be exact hostnames or wildcard patterns (see
246    /// [`HttpAclBuilder::add_allowed_host`] for the wildcard syntax).
247    ///
248    /// Note: The host should be in its canonical form (lowercase, punycode for IDN).
249    pub fn is_host_allowed(&self, host: &str) -> AclClassification {
250        if self.allowed_hosts.contains(host)
251            || self.allowed_host_patterns.iter().any(|p| p.matches(host))
252        {
253            AclClassification::AllowedUserAcl
254        } else if self.denied_hosts.contains(host)
255            || self.denied_host_patterns.iter().any(|p| p.matches(host))
256        {
257            AclClassification::DeniedUserAcl
258        } else if self.host_acl_default {
259            AclClassification::AllowedDefault
260        } else {
261            AclClassification::DeniedDefault
262        }
263    }
264
265    /// Returns whether the port is allowed.
266    pub fn is_port_allowed(&self, port: u16) -> AclClassification {
267        if Self::is_port_in_ranges(port, &self.allowed_port_ranges) {
268            AclClassification::AllowedUserAcl
269        } else if Self::is_port_in_ranges(port, &self.denied_port_ranges) {
270            AclClassification::DeniedUserAcl
271        } else if self.port_acl_default {
272            AclClassification::AllowedDefault
273        } else {
274            AclClassification::DeniedDefault
275        }
276    }
277
278    /// Returns whether an IP is allowed.
279    ///
280    /// A non-global IP (private, loopback, link-local, and other special-use
281    /// addresses) is denied with [`AclClassification::DeniedNotGlobal`] before the
282    /// allow/deny lists are even checked, unless
283    /// [`HttpAclBuilder::non_global_ip_ranges`] was set to `true`.
284    pub fn is_ip_allowed(&self, ip: &IpAddr) -> AclClassification {
285        if !utils::ip::is_global_ip(ip) && !self.allow_non_global_ip_ranges {
286            AclClassification::DeniedNotGlobal
287        } else if Self::is_ip_in_ranges(ip, &self.allowed_ip_ranges) {
288            AclClassification::AllowedUserAcl
289        } else if Self::is_ip_in_ranges(ip, &self.denied_ip_ranges) {
290            AclClassification::DeniedUserAcl
291        } else if self.ip_acl_default {
292            AclClassification::AllowedDefault
293        } else {
294            AclClassification::DeniedDefault
295        }
296    }
297
298    /// Resolve a static DNS mapping.
299    ///
300    /// The returned address is still subject to the IP and port ACL - callers must
301    /// check it with [`Self::is_ip_allowed`] and [`Self::is_port_allowed`] themselves.
302    /// Use [`Self::resolve_trusted_static_dns_mapping`] for mappings that should
303    /// bypass those checks entirely.
304    ///
305    /// Note: The host should be in its canonical form (lowercase, punycode for IDN).
306    pub fn resolve_static_dns_mapping(&self, host: &str) -> Option<SocketAddr> {
307        self.static_dns_mapping.get(host).copied()
308    }
309
310    /// Resolve a trusted static DNS mapping.
311    ///
312    /// Unlike [`Self::resolve_static_dns_mapping`], the returned address is meant to
313    /// bypass the IP and port ACL entirely - only use this for mappings you trust
314    /// regardless of what the ACL would otherwise say (e.g. pinning a hostname to an
315    /// internal address on purpose).
316    ///
317    /// Note: The host should be in its canonical form (lowercase, punycode for IDN).
318    pub fn resolve_trusted_static_dns_mapping(&self, host: &str) -> Option<SocketAddr> {
319        self.trusted_static_dns_mapping.get(host).copied()
320    }
321
322    /// Returns whether a header is allowed.
323    ///
324    /// Note: Header names are case-insensitive, but this function assumes the caller provides them in a consistent case.
325    pub fn is_header_allowed(&self, header_name: &str, header_value: &str) -> AclClassification {
326        self.is_header_allowed_bytes(header_name, header_value.as_bytes())
327    }
328
329    /// Returns whether a header is allowed, given its value as raw bytes.
330    ///
331    /// The same check as [`HttpAcl::is_header_allowed`], for header values that are
332    /// not valid UTF-8 (RFC 9110 `obs-text`, bytes 0x80-0xFF). A configured value is
333    /// compared byte for byte, never against a lossy decode, so a value that only
334    /// decodes lossily to an allowed value (e.g. via U+FFFD) does not match it.
335    ///
336    /// Note: Header names are case-insensitive, but this function assumes the caller provides them in a consistent case.
337    pub fn is_header_allowed_bytes(
338        &self,
339        header_name: &str,
340        header_value: &[u8],
341    ) -> AclClassification {
342        if let Some(allowed_value) = self.allowed_headers.get(header_name) {
343            if allowed_value.as_deref().map(str::as_bytes) == Some(header_value)
344                || allowed_value.is_none()
345            {
346                AclClassification::AllowedUserAcl
347            } else {
348                AclClassification::DeniedUserAcl
349            }
350        } else if let Some(denied_value) = self.denied_headers.get(header_name) {
351            if denied_value.as_deref().map(str::as_bytes) == Some(header_value)
352                || denied_value.is_none()
353            {
354                AclClassification::DeniedUserAcl
355            } else {
356                AclClassification::AllowedUserAcl
357            }
358        } else if self.header_acl_default {
359            AclClassification::AllowedDefault
360        } else {
361            AclClassification::DeniedDefault
362        }
363    }
364
365    /// Returns whether a URL path is allowed.
366    ///
367    /// Note: The URL path should be percent-decoded before passing it to this function.
368    pub fn is_url_path_allowed(&self, url_path: &str) -> AclClassification {
369        if self.allowed_url_paths_router.at(url_path).is_ok() {
370            AclClassification::AllowedUserAcl
371        } else if self.denied_url_paths_router.at(url_path).is_ok() {
372            AclClassification::DeniedUserAcl
373        } else if self.url_path_acl_default {
374            AclClassification::AllowedDefault
375        } else {
376            AclClassification::DeniedDefault
377        }
378    }
379
380    /// Runs the [`ValidateFn`] attached to this ACL, if any, against a request.
381    ///
382    /// Returns [`AclClassification::AllowedDefault`] when no `ValidateFn` was
383    /// attached (the default for an `HttpAcl` built without one), so calling this is
384    /// always safe even if you never configured custom validation.
385    pub fn is_valid<'h>(
386        &self,
387        scheme: &str,
388        authority: &Authority,
389        headers: impl Iterator<Item = (&'h str, &'h str)> + Send + Sync + 'h,
390        body: Option<&[u8]>,
391    ) -> AclClassification {
392        if let Some(validate_fn) = &self.validate_fn {
393            validate_fn(scheme, authority, Box::new(headers), body)
394        } else {
395            AclClassification::AllowedDefault
396        }
397    }
398
399    /// Returns whether a [`ModifyRequestFn`] is attached to this ACL.
400    ///
401    /// Cheap (a single field read). Check this before doing any work to make a
402    /// request's body/headers available for mutation (e.g. buffering a streaming
403    /// body), so that omitting a `ModifyRequestFn` costs nothing at request time.
404    pub fn has_modify_request(&self) -> bool {
405        self.modify_request_fn.is_some()
406    }
407
408    /// Returns whether a [`ModifyResponseFn`] is attached to this ACL.
409    ///
410    /// See [`Self::has_modify_request`] - same rationale, for the response side.
411    pub fn has_modify_response(&self) -> bool {
412        self.modify_response_fn.is_some()
413    }
414
415    /// Runs the [`ModifyRequestFn`] attached to this ACL, if any, against
416    /// `mutation`, mutating it in place.
417    ///
418    /// Does nothing when no `ModifyRequestFn` was attached, so calling this is
419    /// always safe even if you never configured one - though see
420    /// [`Self::has_modify_request`] if you want to skip preparing `mutation` at all
421    /// in that case.
422    pub fn modify_request(
423        &self,
424        scheme: &str,
425        authority: &Authority,
426        mutation: &mut RequestMutation,
427    ) {
428        if let Some(modify_request_fn) = &self.modify_request_fn {
429            modify_request_fn(scheme, authority, mutation);
430        }
431    }
432
433    /// Runs the [`ModifyResponseFn`] attached to this ACL, if any, against
434    /// `mutation`, mutating it in place. See [`Self::modify_request`].
435    pub fn modify_response(
436        &self,
437        scheme: &str,
438        authority: &Authority,
439        mutation: &mut ResponseMutation,
440    ) {
441        if let Some(modify_response_fn) = &self.modify_response_fn {
442            modify_response_fn(scheme, authority, mutation);
443        }
444    }
445
446    /// Checks if an ip is in a list of ip ranges.
447    fn is_ip_in_ranges(ip: &IpAddr, ranges: &[RangeInclusive<IpAddr>]) -> bool {
448        ranges.iter().any(|range| range.contains(ip))
449    }
450
451    /// Checks if a port is in a list of port ranges.
452    fn is_port_in_ranges(port: u16, ranges: &[RangeInclusive<u16>]) -> bool {
453        ranges.iter().any(|range| range.contains(&port))
454    }
455}
456
457/// Represents the outcome of an ACL check, and why it was reached.
458///
459/// Every `is_*_allowed` method on [`HttpAcl`] returns one of these instead of a plain
460/// `bool`, so the reason for an outcome is preserved for logging or error messages.
461/// Use [`Self::is_allowed`] or [`Self::is_denied`] to collapse it to a `bool` once you
462/// only care about the outcome.
463#[non_exhaustive]
464#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
465pub enum AclClassification {
466    /// The entity is allowed according to the allowed ACL.
467    AllowedUserAcl,
468    /// The entity is allowed because the default is to allow if no ACL match is found.
469    AllowedDefault,
470    /// The entity is denied according to the denied ACL.
471    DeniedUserAcl,
472    /// The entity is denied because the default is to deny if no ACL match is found.
473    DeniedDefault,
474    /// The entity is denied for a custom reason.
475    ///
476    /// Not produced by any built-in check; this exists for a [`ValidateFn`] to return
477    /// a denial with a human-readable explanation of its own.
478    Denied(String),
479    /// The IP is denied because it is not global.
480    DeniedNotGlobal,
481}
482
483impl std::fmt::Display for AclClassification {
484    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
485        match self {
486            AclClassification::AllowedUserAcl => {
487                write!(f, "The entity is allowed according to the allowed ACL.")
488            }
489            AclClassification::AllowedDefault => write!(
490                f,
491                "The entity is allowed because the default is to allow if no ACL match is found."
492            ),
493            AclClassification::DeniedUserAcl => {
494                write!(f, "The entity is denied according to the denied ACL.")
495            }
496            AclClassification::DeniedNotGlobal => {
497                write!(f, "The ip is denied because it is not global.")
498            }
499            AclClassification::DeniedDefault => write!(
500                f,
501                "The entity is denied because the default is to deny if no ACL match is found."
502            ),
503            AclClassification::Denied(reason) => {
504                write!(f, "The entity is denied because {reason}.")
505            }
506        }
507    }
508}
509
510impl AclClassification {
511    /// Returns whether the classification is allowed.
512    pub fn is_allowed(&self) -> bool {
513        matches!(
514            self,
515            AclClassification::AllowedUserAcl | AclClassification::AllowedDefault
516        )
517    }
518
519    /// Returns whether the classification is denied.
520    pub fn is_denied(&self) -> bool {
521        matches!(
522            self,
523            AclClassification::DeniedUserAcl
524                | AclClassification::Denied(_)
525                | AclClassification::DeniedDefault
526                | AclClassification::DeniedNotGlobal
527        )
528    }
529}
530
531/// Represents an HTTP request method.
532#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
533#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
534pub enum HttpRequestMethod {
535    /// The CONNECT method.
536    CONNECT,
537    /// The DELETE method.
538    DELETE,
539    /// The GET method.
540    GET,
541    /// The HEAD method.
542    HEAD,
543    /// The OPTIONS method.
544    OPTIONS,
545    /// The PATCH method.
546    PATCH,
547    /// The POST method.
548    POST,
549    /// The PUT method.
550    PUT,
551    /// The TRACE method.
552    TRACE,
553    /// Any other method.
554    OTHER(Box<str>),
555}
556
557impl From<&str> for HttpRequestMethod {
558    fn from(method: &str) -> Self {
559        match method {
560            "CONNECT" => HttpRequestMethod::CONNECT,
561            "DELETE" => HttpRequestMethod::DELETE,
562            "GET" => HttpRequestMethod::GET,
563            "HEAD" => HttpRequestMethod::HEAD,
564            "OPTIONS" => HttpRequestMethod::OPTIONS,
565            "PATCH" => HttpRequestMethod::PATCH,
566            "POST" => HttpRequestMethod::POST,
567            "PUT" => HttpRequestMethod::PUT,
568            "TRACE" => HttpRequestMethod::TRACE,
569            _ => HttpRequestMethod::OTHER(method.into()),
570        }
571    }
572}
573
574impl HttpRequestMethod {
575    /// Return the method as a `&str`.
576    pub fn as_str(&self) -> &str {
577        match self {
578            HttpRequestMethod::CONNECT => "CONNECT",
579            HttpRequestMethod::DELETE => "DELETE",
580            HttpRequestMethod::GET => "GET",
581            HttpRequestMethod::HEAD => "HEAD",
582            HttpRequestMethod::OPTIONS => "OPTIONS",
583            HttpRequestMethod::PATCH => "PATCH",
584            HttpRequestMethod::POST => "POST",
585            HttpRequestMethod::PUT => "PUT",
586            HttpRequestMethod::TRACE => "TRACE",
587            HttpRequestMethod::OTHER(other) => other,
588        }
589    }
590}
591
592/// The runtime hooks attached to an [`HttpAcl`] at build time, via
593/// [`HttpAclBuilder::build_full`]/[`HttpAclBuilder::try_build_full`].
594///
595/// Each field is typically a closure that captures state from outside the builder
596/// (a database handle, a secret, and so on), which is why these aren't set through
597/// dedicated builder setter methods the way most of [`HttpAclBuilder`]'s other
598/// configuration is.
599#[derive(Clone, Default)]
600pub struct HttpAclHooks {
601    /// See [`ValidateFn`].
602    pub validate_fn: Option<ValidateFn>,
603    /// See [`ModifyRequestFn`].
604    pub modify_request_fn: Option<ModifyRequestFn>,
605    /// See [`ModifyResponseFn`].
606    pub modify_response_fn: Option<ModifyResponseFn>,
607}
608
609/// A builder for [`HttpAcl`].
610///
611/// Most categories (methods, hosts, port ranges, IP ranges, headers, URL paths,
612/// static DNS mappings) follow the same set of methods: `add_allowed_*`/
613/// `add_denied_*` to add a single entry, `remove_allowed_*`/`remove_denied_*` to
614/// remove one, `allowed_*`/`denied_*` to replace the whole list at once, and
615/// `clear_allowed_*`/`clear_denied_*` to empty it. The fallible variants return
616/// [`AddError`] rather than panicking, e.g. when an entry is already present on the
617/// opposite list, so a host (or header, port range, and so on) can never end up
618/// allowed and denied at the same time.
619///
620/// Call [`Self::build`] or [`Self::try_build`] to finish. Only the latter validates
621/// the finished configuration (uniqueness, overlaps, non-global IP ranges); see
622/// their docs for when each applies.
623#[derive(Default, Clone)]
624#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
625pub struct HttpAclBuilder {
626    allow_http: bool,
627    allow_https: bool,
628    allowed_methods: Vec<HttpRequestMethod>,
629    denied_methods: Vec<HttpRequestMethod>,
630    allowed_hosts: Vec<String>,
631    denied_hosts: Vec<String>,
632    #[cfg_attr(feature = "serde", serde(skip))]
633    allowed_host_patterns: Vec<HostPattern>,
634    #[cfg_attr(feature = "serde", serde(skip))]
635    denied_host_patterns: Vec<HostPattern>,
636    allowed_port_ranges: Vec<RangeInclusive<u16>>,
637    denied_port_ranges: Vec<RangeInclusive<u16>>,
638    allowed_ip_ranges: Vec<RangeInclusive<IpAddr>>,
639    denied_ip_ranges: Vec<RangeInclusive<IpAddr>>,
640    static_dns_mapping: HashMap<String, SocketAddr>,
641    trusted_static_dns_mapping: HashMap<String, SocketAddr>,
642    allowed_headers: HashMap<String, Option<String>>,
643    denied_headers: HashMap<String, Option<String>>,
644    allowed_url_paths: Vec<String>,
645    #[cfg_attr(feature = "serde", serde(skip))]
646    allowed_url_paths_router: Router<()>,
647    denied_url_paths: Vec<String>,
648    #[cfg_attr(feature = "serde", serde(skip))]
649    denied_url_paths_router: Router<()>,
650    allow_non_global_ip_ranges: bool,
651    method_acl_default: bool,
652    host_acl_default: bool,
653    port_acl_default: bool,
654    ip_acl_default: bool,
655    header_acl_default: bool,
656    url_path_acl_default: bool,
657}
658
659impl std::fmt::Debug for HttpAclBuilder {
660    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
661        f.debug_struct("HttpAclBuilder")
662            .field("allow_http", &self.allow_http)
663            .field("allow_https", &self.allow_https)
664            .field("allowed_methods", &self.allowed_methods)
665            .field("denied_methods", &self.denied_methods)
666            .field("allowed_hosts", &self.allowed_hosts)
667            .field("denied_hosts", &self.denied_hosts)
668            .field("allowed_port_ranges", &self.allowed_port_ranges)
669            .field("denied_port_ranges", &self.denied_port_ranges)
670            .field("allowed_ip_ranges", &self.allowed_ip_ranges)
671            .field("denied_ip_ranges", &self.denied_ip_ranges)
672            .field("static_dns_mapping", &self.static_dns_mapping)
673            .field(
674                "trusted_static_dns_mapping",
675                &self.trusted_static_dns_mapping,
676            )
677            .field("allowed_headers", &self.allowed_headers)
678            .field("denied_headers", &self.denied_headers)
679            .field("allowed_url_paths", &self.allowed_url_paths)
680            .field("denied_url_paths", &self.denied_url_paths)
681            .field(
682                "allow_non_global_ip_ranges",
683                &self.allow_non_global_ip_ranges,
684            )
685            .field("method_acl_default", &self.method_acl_default)
686            .field("host_acl_default", &self.host_acl_default)
687            .field("port_acl_default", &self.port_acl_default)
688            .field("ip_acl_default", &self.ip_acl_default)
689            .field("header_acl_default", &self.header_acl_default)
690            .field("url_path_acl_default", &self.url_path_acl_default)
691            .finish()
692    }
693}
694
695impl PartialEq for HttpAclBuilder {
696    fn eq(&self, other: &Self) -> bool {
697        self.allow_http == other.allow_http
698            && self.allow_https == other.allow_https
699            && self.allowed_methods == other.allowed_methods
700            && self.denied_methods == other.denied_methods
701            && self.allowed_hosts == other.allowed_hosts
702            && self.denied_hosts == other.denied_hosts
703            && self.allowed_port_ranges == other.allowed_port_ranges
704            && self.denied_port_ranges == other.denied_port_ranges
705            && self.allowed_ip_ranges == other.allowed_ip_ranges
706            && self.denied_ip_ranges == other.denied_ip_ranges
707            && self.static_dns_mapping == other.static_dns_mapping
708            && self.trusted_static_dns_mapping == other.trusted_static_dns_mapping
709            && self.allowed_headers == other.allowed_headers
710            && self.denied_headers == other.denied_headers
711            && self.allowed_url_paths == other.allowed_url_paths
712            && self.denied_url_paths == other.denied_url_paths
713            && self.allow_non_global_ip_ranges == other.allow_non_global_ip_ranges
714            && self.method_acl_default == other.method_acl_default
715            && self.host_acl_default == other.host_acl_default
716            && self.port_acl_default == other.port_acl_default
717            && self.ip_acl_default == other.ip_acl_default
718            && self.header_acl_default == other.header_acl_default
719            && self.url_path_acl_default == other.url_path_acl_default
720    }
721}
722
723impl HttpAclBuilder {
724    /// Create a new [`HttpAclBuilder`].
725    pub fn new() -> Self {
726        Self {
727            allow_http: true,
728            allow_https: true,
729            allowed_methods: vec![
730                HttpRequestMethod::CONNECT,
731                HttpRequestMethod::DELETE,
732                HttpRequestMethod::GET,
733                HttpRequestMethod::HEAD,
734                HttpRequestMethod::OPTIONS,
735                HttpRequestMethod::PATCH,
736                HttpRequestMethod::POST,
737                HttpRequestMethod::PUT,
738                HttpRequestMethod::TRACE,
739            ],
740            denied_methods: Vec::new(),
741            allowed_hosts: Vec::new(),
742            denied_hosts: Vec::new(),
743            allowed_host_patterns: Vec::new(),
744            denied_host_patterns: Vec::new(),
745            allowed_port_ranges: vec![80..=80, 443..=443],
746            denied_port_ranges: Vec::new(),
747            allowed_ip_ranges: Vec::new(),
748            denied_ip_ranges: Vec::new(),
749            allowed_headers: HashMap::new(),
750            denied_headers: HashMap::new(),
751            allowed_url_paths: Vec::new(),
752            allowed_url_paths_router: Router::new(),
753            denied_url_paths: Vec::new(),
754            denied_url_paths_router: Router::new(),
755            allow_non_global_ip_ranges: false,
756            static_dns_mapping: HashMap::new(),
757            trusted_static_dns_mapping: HashMap::new(),
758            method_acl_default: false,
759            host_acl_default: false,
760            port_acl_default: false,
761            ip_acl_default: false,
762            header_acl_default: true,
763            url_path_acl_default: true,
764        }
765    }
766
767    /// Sets whether HTTP is allowed.
768    pub fn http(mut self, allow: bool) -> Self {
769        self.allow_http = allow;
770        self
771    }
772
773    /// Sets whether HTTPS is allowed.
774    pub fn https(mut self, allow: bool) -> Self {
775        self.allow_https = allow;
776        self
777    }
778
779    /// Sets whether non-global IP ranges are allowed.
780    ///
781    /// Non-global IP ranges include private, loopback, link-local, and other special-use addresses.
782    pub fn non_global_ip_ranges(mut self, allow: bool) -> Self {
783        self.allow_non_global_ip_ranges = allow;
784        self
785    }
786
787    /// Set default action for HTTP methods if no ACL match is found.
788    pub fn method_acl_default(mut self, allow: bool) -> Self {
789        self.method_acl_default = allow;
790        self
791    }
792
793    /// Set default action for hosts if no ACL match is found.
794    pub fn host_acl_default(mut self, allow: bool) -> Self {
795        self.host_acl_default = allow;
796        self
797    }
798
799    /// Set default action for ports if no ACL match is found.
800    pub fn port_acl_default(mut self, allow: bool) -> Self {
801        self.port_acl_default = allow;
802        self
803    }
804
805    /// Set default action for IPs if no ACL match is found.
806    pub fn ip_acl_default(mut self, allow: bool) -> Self {
807        self.ip_acl_default = allow;
808        self
809    }
810
811    /// Set default action for headers if no ACL match is found.
812    pub fn header_acl_default(mut self, allow: bool) -> Self {
813        self.header_acl_default = allow;
814        self
815    }
816
817    /// Set default action for URL paths if no ACL match is found.
818    pub fn url_path_acl_default(mut self, allow: bool) -> Self {
819        self.url_path_acl_default = allow;
820        self
821    }
822
823    /// Adds a method to the allowed methods.
824    ///
825    /// Note: If you pass a string ensure it is uppercased first.
826    pub fn add_allowed_method(
827        mut self,
828        method: impl Into<HttpRequestMethod>,
829    ) -> Result<Self, AddError> {
830        let method = method.into();
831        if self.denied_methods.contains(&method) {
832            Err(AddError::AlreadyDeniedMethod(method))
833        } else if self.allowed_methods.contains(&method) {
834            Err(AddError::AlreadyAllowedMethod(method))
835        } else {
836            self.allowed_methods.push(method);
837            Ok(self)
838        }
839    }
840
841    /// Removes a method from the allowed methods.
842    ///
843    /// Note: If you pass a string ensure it is uppercased first.
844    pub fn remove_allowed_method(mut self, method: impl Into<HttpRequestMethod>) -> Self {
845        let method = method.into();
846        self.allowed_methods.retain(|m| m != &method);
847        self
848    }
849
850    /// Sets the allowed methods.
851    ///
852    /// Note: If you pass strings ensure they are uppercased first.
853    pub fn allowed_methods(
854        mut self,
855        methods: Vec<impl Into<HttpRequestMethod>>,
856    ) -> Result<Self, AddError> {
857        let methods = methods.into_iter().map(|m| m.into()).collect::<Vec<_>>();
858
859        for method in &methods {
860            if self.denied_methods.contains(method) {
861                return Err(AddError::AlreadyDeniedMethod(method.clone()));
862            }
863        }
864        self.allowed_methods = methods;
865        Ok(self)
866    }
867
868    /// Clears the allowed methods.
869    pub fn clear_allowed_methods(mut self) -> Self {
870        self.allowed_methods.clear();
871        self
872    }
873
874    /// Adds a method to the denied methods.
875    ///
876    /// Note: If you pass a string ensure it is uppercased first.
877    pub fn add_denied_method(
878        mut self,
879        method: impl Into<HttpRequestMethod>,
880    ) -> Result<Self, AddError> {
881        let method = method.into();
882        if self.allowed_methods.contains(&method) {
883            Err(AddError::AlreadyAllowedMethod(method))
884        } else if self.denied_methods.contains(&method) {
885            Err(AddError::AlreadyDeniedMethod(method))
886        } else {
887            self.denied_methods.push(method);
888            Ok(self)
889        }
890    }
891
892    /// Removes a method from the denied methods.
893    ///
894    /// Note: If you pass a string ensure it is uppercased first.
895    pub fn remove_denied_method(mut self, method: impl Into<HttpRequestMethod>) -> Self {
896        let method = method.into();
897        self.denied_methods.retain(|m| m != &method);
898        self
899    }
900
901    /// Sets the denied methods.
902    ///
903    /// Note: If you pass strings ensure they are uppercased first.
904    pub fn denied_methods(
905        mut self,
906        methods: Vec<impl Into<HttpRequestMethod>>,
907    ) -> Result<Self, AddError> {
908        let methods = methods.into_iter().map(|m| m.into()).collect::<Vec<_>>();
909
910        for method in &methods {
911            if self.allowed_methods.contains(method) {
912                return Err(AddError::AlreadyAllowedMethod(method.clone()));
913            }
914        }
915        self.denied_methods = methods;
916        Ok(self)
917    }
918
919    /// Clears the denied methods.
920    pub fn clear_denied_methods(mut self) -> Self {
921        self.denied_methods.clear();
922        self
923    }
924
925    /// Adds a host to the allowed hosts.
926    ///
927    /// `host` may be an exact hostname, or a wildcard pattern where each label
928    /// (dot-separated segment) is either literal or one of:
929    ///
930    /// - `?` - matches exactly one label (e.g. `?.example.com` matches `foo.example.com`
931    ///   but not `foo.bar.example.com` or bare `example.com`).
932    /// - `*` - matches one or more labels (e.g. `*.example.com` matches `foo.example.com`
933    ///   and `foo.bar.example.com`, but not bare `example.com`).
934    ///
935    /// A wildcard must occupy an entire label; `foo*.example.com` is not a valid pattern.
936    ///
937    /// Note: The host should be in its canonical form (lowercase, punycode for IDN).
938    pub fn add_allowed_host(mut self, host: String) -> Result<Self, AddError> {
939        let pattern = Self::validate_host_or_pattern(&host)?;
940
941        if self.denied_hosts.contains(&host) {
942            return Err(AddError::AlreadyDeniedHost(host));
943        }
944        if self.allowed_hosts.contains(&host) {
945            return Err(AddError::AlreadyAllowedHost(host));
946        }
947
948        if let Some(pattern) = pattern {
949            self.allowed_host_patterns.push(pattern);
950        }
951        self.allowed_hosts.push(host);
952        Ok(self)
953    }
954
955    /// Removes a host from the allowed hosts.
956    ///
957    /// Note: The host should be in its canonical form (lowercase, punycode for IDN).
958    pub fn remove_allowed_host(mut self, host: String) -> Self {
959        self.allowed_hosts.retain(|h| h != &host);
960        self.allowed_host_patterns = Self::compile_host_patterns(&self.allowed_hosts);
961        self
962    }
963
964    /// Sets the allowed hosts.
965    ///
966    /// See [`Self::add_allowed_host`] for the wildcard pattern syntax.
967    ///
968    /// Note: The hosts should be in their canonical form (lowercase, punycode for IDN).
969    pub fn allowed_hosts(mut self, hosts: Vec<String>) -> Result<Self, AddError> {
970        let mut patterns = Vec::new();
971        for host in &hosts {
972            if let Some(pattern) = Self::validate_host_or_pattern(host)? {
973                patterns.push(pattern);
974            }
975            if self.denied_hosts.contains(host) {
976                return Err(AddError::AlreadyDeniedHost(host.clone()));
977            }
978        }
979        self.allowed_host_patterns = patterns;
980        self.allowed_hosts = hosts;
981        Ok(self)
982    }
983
984    /// Clears the allowed hosts.
985    pub fn clear_allowed_hosts(mut self) -> Self {
986        self.allowed_hosts.clear();
987        self.allowed_host_patterns.clear();
988        self
989    }
990
991    /// Adds a host to the denied hosts.
992    ///
993    /// See [`Self::add_allowed_host`] for the wildcard pattern syntax.
994    ///
995    /// Note: The host should be in its canonical form (lowercase, punycode for IDN).
996    pub fn add_denied_host(mut self, host: String) -> Result<Self, AddError> {
997        let pattern = Self::validate_host_or_pattern(&host)?;
998
999        if self.allowed_hosts.contains(&host) {
1000            return Err(AddError::AlreadyAllowedHost(host));
1001        }
1002        if self.denied_hosts.contains(&host) {
1003            return Err(AddError::AlreadyDeniedHost(host));
1004        }
1005
1006        if let Some(pattern) = pattern {
1007            self.denied_host_patterns.push(pattern);
1008        }
1009        self.denied_hosts.push(host);
1010        Ok(self)
1011    }
1012
1013    /// Removes a host from the denied hosts.
1014    ///
1015    /// Note: The host should be in its canonical form (lowercase, punycode for IDN).
1016    pub fn remove_denied_host(mut self, host: String) -> Self {
1017        self.denied_hosts.retain(|h| h != &host);
1018        self.denied_host_patterns = Self::compile_host_patterns(&self.denied_hosts);
1019        self
1020    }
1021
1022    /// Sets the denied hosts.
1023    ///
1024    /// See [`Self::add_allowed_host`] for the wildcard pattern syntax.
1025    ///
1026    /// Note: The hosts should be in their canonical form (lowercase, punycode for IDN).
1027    pub fn denied_hosts(mut self, hosts: Vec<String>) -> Result<Self, AddError> {
1028        let mut patterns = Vec::new();
1029        for host in &hosts {
1030            if let Some(pattern) = Self::validate_host_or_pattern(host)? {
1031                patterns.push(pattern);
1032            }
1033            if self.allowed_hosts.contains(host) {
1034                return Err(AddError::AlreadyAllowedHost(host.clone()));
1035            }
1036        }
1037        self.denied_host_patterns = patterns;
1038        self.denied_hosts = hosts;
1039        Ok(self)
1040    }
1041
1042    /// Clears the denied hosts.
1043    pub fn clear_denied_hosts(mut self) -> Self {
1044        self.denied_hosts.clear();
1045        self.denied_host_patterns.clear();
1046        self
1047    }
1048
1049    /// Validates a host string, returning its compiled [`HostPattern`] if it is a
1050    /// wildcard pattern, or `None` if it's a literal host.
1051    fn validate_host_or_pattern(host: &str) -> Result<Option<HostPattern>, AddError> {
1052        if is_wildcard_host(host) {
1053            match HostPattern::parse(host) {
1054                Some(pattern) => Ok(Some(pattern)),
1055                None => Err(AddError::InvalidEntity(host.to_string())),
1056            }
1057        } else if utils::authority::is_valid_host(host) {
1058            Ok(None)
1059        } else {
1060            Err(AddError::InvalidEntity(host.to_string()))
1061        }
1062    }
1063
1064    /// Compiles the wildcard patterns out of a list of (already-validated) host strings.
1065    fn compile_host_patterns(hosts: &[String]) -> Vec<HostPattern> {
1066        hosts
1067            .iter()
1068            .filter(|h| is_wildcard_host(h))
1069            .filter_map(|h| HostPattern::parse(h))
1070            .collect()
1071    }
1072
1073    /// Adds a port range to the allowed port ranges.
1074    pub fn add_allowed_port_range(
1075        mut self,
1076        port_range: RangeInclusive<u16>,
1077    ) -> Result<Self, AddError> {
1078        if self.denied_port_ranges.contains(&port_range) {
1079            Err(AddError::AlreadyDeniedPortRange(port_range))
1080        } else if self.allowed_port_ranges.contains(&port_range) {
1081            Err(AddError::AlreadyAllowedPortRange(port_range))
1082        } else if utils::range_overlaps(&self.allowed_port_ranges, &port_range, None)
1083            || utils::range_overlaps(&self.denied_port_ranges, &port_range, None)
1084        {
1085            Err(AddError::Overlaps(format!("{port_range:?}")))
1086        } else {
1087            self.allowed_port_ranges.push(port_range);
1088            Ok(self)
1089        }
1090    }
1091
1092    /// Removes a port range from the allowed port ranges.
1093    pub fn remove_allowed_port_range(mut self, port_range: RangeInclusive<u16>) -> Self {
1094        self.allowed_port_ranges.retain(|p| p != &port_range);
1095        self
1096    }
1097
1098    /// Sets the allowed port ranges.
1099    pub fn allowed_port_ranges(
1100        mut self,
1101        port_ranges: Vec<RangeInclusive<u16>>,
1102    ) -> Result<Self, AddError> {
1103        for (i, port_range) in port_ranges.iter().enumerate() {
1104            if self.denied_port_ranges.contains(port_range) {
1105                return Err(AddError::AlreadyDeniedPortRange(port_range.clone()));
1106            } else if utils::range_overlaps(&port_ranges, port_range, Some(i))
1107                || utils::range_overlaps(&self.denied_port_ranges, port_range, None)
1108            {
1109                return Err(AddError::Overlaps(format!("{port_range:?}")));
1110            }
1111        }
1112        self.allowed_port_ranges = port_ranges;
1113        Ok(self)
1114    }
1115
1116    /// Clears the allowed port ranges.
1117    pub fn clear_allowed_port_ranges(mut self) -> Self {
1118        self.allowed_port_ranges.clear();
1119        self
1120    }
1121
1122    /// Adds a port range to the denied port ranges.
1123    pub fn add_denied_port_range(
1124        mut self,
1125        port_range: RangeInclusive<u16>,
1126    ) -> Result<Self, AddError> {
1127        if self.allowed_port_ranges.contains(&port_range) {
1128            Err(AddError::AlreadyAllowedPortRange(port_range))
1129        } else if self.denied_port_ranges.contains(&port_range) {
1130            Err(AddError::AlreadyDeniedPortRange(port_range))
1131        } else if utils::range_overlaps(&self.allowed_port_ranges, &port_range, None)
1132            || utils::range_overlaps(&self.denied_port_ranges, &port_range, None)
1133        {
1134            Err(AddError::Overlaps(format!("{port_range:?}")))
1135        } else {
1136            self.denied_port_ranges.push(port_range);
1137            Ok(self)
1138        }
1139    }
1140
1141    /// Removes a port range from the denied port ranges.
1142    pub fn remove_denied_port_range(mut self, port_range: RangeInclusive<u16>) -> Self {
1143        self.denied_port_ranges.retain(|p| p != &port_range);
1144        self
1145    }
1146
1147    /// Sets the denied port ranges.
1148    pub fn denied_port_ranges(
1149        mut self,
1150        port_ranges: Vec<RangeInclusive<u16>>,
1151    ) -> Result<Self, AddError> {
1152        for (i, port_range) in port_ranges.iter().enumerate() {
1153            if self.allowed_port_ranges.contains(port_range) {
1154                return Err(AddError::AlreadyAllowedPortRange(port_range.clone()));
1155            } else if utils::range_overlaps(&port_ranges, port_range, Some(i))
1156                || utils::range_overlaps(&self.allowed_port_ranges, port_range, None)
1157            {
1158                return Err(AddError::Overlaps(format!("{port_range:?}")));
1159            }
1160        }
1161        self.denied_port_ranges = port_ranges;
1162        Ok(self)
1163    }
1164
1165    /// Clears the denied port ranges.
1166    pub fn clear_denied_port_ranges(mut self) -> Self {
1167        self.denied_port_ranges.clear();
1168        self
1169    }
1170
1171    /// Adds an IP range to the allowed IP ranges.
1172    pub fn add_allowed_ip_range<Ip: IntoIpRange>(mut self, ip_range: Ip) -> Result<Self, AddError> {
1173        let ip_range = ip_range
1174            .into_range()
1175            .ok_or_else(|| AddError::InvalidEntity("Invalid IP range".to_string()))?;
1176        if self.denied_ip_ranges.contains(&ip_range) {
1177            return Err(AddError::AlreadyDeniedIpRange(ip_range));
1178        } else if self.allowed_ip_ranges.contains(&ip_range) {
1179            return Err(AddError::AlreadyAllowedIpRange(ip_range));
1180        } else if utils::range_overlaps(&self.allowed_ip_ranges, &ip_range, None)
1181            || utils::range_overlaps(&self.denied_ip_ranges, &ip_range, None)
1182        {
1183            return Err(AddError::Overlaps(format!("{ip_range:?}")));
1184        }
1185        self.allowed_ip_ranges.push(ip_range);
1186        Ok(self)
1187    }
1188
1189    /// Removes an IP range from the allowed IP ranges.
1190    pub fn remove_allowed_ip_range<Ip: IntoIpRange>(
1191        mut self,
1192        ip_range: Ip,
1193    ) -> Result<Self, AddError> {
1194        let ip_range = ip_range
1195            .into_range()
1196            .ok_or_else(|| AddError::InvalidEntity("Invalid IP range".to_string()))?;
1197        self.allowed_ip_ranges.retain(|ip| ip != &ip_range);
1198        Ok(self)
1199    }
1200
1201    /// Sets the allowed IP ranges.
1202    pub fn allowed_ip_ranges<Ip: IntoIpRange>(
1203        mut self,
1204        ip_ranges: Vec<Ip>,
1205    ) -> Result<Self, AddError> {
1206        let ip_ranges = ip_ranges
1207            .into_iter()
1208            .map(|ip| ip.into_range())
1209            .collect::<Option<Vec<_>>>()
1210            .ok_or_else(|| AddError::InvalidEntity("Invalid IP range".to_string()))?;
1211        for (i, ip_range) in ip_ranges.iter().enumerate() {
1212            if self.denied_ip_ranges.contains(ip_range) {
1213                return Err(AddError::AlreadyDeniedIpRange(ip_range.clone()));
1214            } else if utils::range_overlaps(&ip_ranges, ip_range, Some(i))
1215                || utils::range_overlaps(&self.denied_ip_ranges, ip_range, None)
1216            {
1217                return Err(AddError::Overlaps(format!("{ip_range:?}")));
1218            }
1219        }
1220        self.allowed_ip_ranges = ip_ranges;
1221        Ok(self)
1222    }
1223
1224    /// Clears the allowed IP ranges.
1225    pub fn clear_allowed_ip_ranges(mut self) -> Self {
1226        self.allowed_ip_ranges.clear();
1227        self
1228    }
1229
1230    /// Adds an IP range to the denied IP ranges.
1231    pub fn add_denied_ip_range<Ip: IntoIpRange>(mut self, ip_range: Ip) -> Result<Self, AddError> {
1232        let ip_range = ip_range
1233            .into_range()
1234            .ok_or_else(|| AddError::InvalidEntity("Invalid IP range".to_string()))?;
1235        if self.allowed_ip_ranges.contains(&ip_range) {
1236            return Err(AddError::AlreadyAllowedIpRange(ip_range));
1237        } else if self.denied_ip_ranges.contains(&ip_range) {
1238            return Err(AddError::AlreadyDeniedIpRange(ip_range));
1239        } else if utils::range_overlaps(&self.allowed_ip_ranges, &ip_range, None)
1240            || utils::range_overlaps(&self.denied_ip_ranges, &ip_range, None)
1241        {
1242            return Err(AddError::Overlaps(format!("{ip_range:?}")));
1243        }
1244        self.denied_ip_ranges.push(ip_range);
1245        Ok(self)
1246    }
1247
1248    /// Removes an IP range from the denied IP ranges.
1249    pub fn remove_denied_ip_range<Ip: IntoIpRange>(
1250        mut self,
1251        ip_range: Ip,
1252    ) -> Result<Self, AddError> {
1253        let ip_range = ip_range
1254            .into_range()
1255            .ok_or_else(|| AddError::InvalidEntity("Invalid IP range".to_string()))?;
1256        self.denied_ip_ranges.retain(|ip| ip != &ip_range);
1257        Ok(self)
1258    }
1259
1260    /// Sets the denied IP ranges.
1261    pub fn denied_ip_ranges<Ip: IntoIpRange>(
1262        mut self,
1263        ip_ranges: Vec<Ip>,
1264    ) -> Result<Self, AddError> {
1265        let ip_ranges = ip_ranges
1266            .into_iter()
1267            .map(|ip| ip.into_range())
1268            .collect::<Option<Vec<_>>>()
1269            .ok_or_else(|| AddError::InvalidEntity("Invalid IP range".to_string()))?;
1270        for (i, ip_range) in ip_ranges.iter().enumerate() {
1271            if self.allowed_ip_ranges.contains(ip_range) {
1272                return Err(AddError::AlreadyAllowedIpRange(ip_range.clone()));
1273            } else if utils::range_overlaps(&ip_ranges, ip_range, Some(i))
1274                || utils::range_overlaps(&self.allowed_ip_ranges, ip_range, None)
1275            {
1276                return Err(AddError::Overlaps(format!("{ip_range:?}")));
1277            }
1278        }
1279        self.denied_ip_ranges = ip_ranges;
1280        Ok(self)
1281    }
1282
1283    /// Clears the denied IP ranges.
1284    pub fn clear_denied_ip_ranges(mut self) -> Self {
1285        self.denied_ip_ranges.clear();
1286        self
1287    }
1288
1289    /// Add a static DNS mapping.
1290    ///
1291    /// The resolved address is still subject to the IP and port ACL. Use
1292    /// [`Self::add_trusted_static_dns_mapping`] for a mapping that should bypass
1293    /// those checks entirely.
1294    ///
1295    /// Note: The host should be in its canonical form (lowercase, punycode for IDN).
1296    pub fn add_static_dns_mapping(
1297        mut self,
1298        host: String,
1299        sock_addr: SocketAddr,
1300    ) -> Result<Self, AddError> {
1301        if !utils::authority::is_valid_host(&host) {
1302            return Err(AddError::InvalidEntity(host));
1303        }
1304        if self.trusted_static_dns_mapping.contains_key(&host) {
1305            return Err(AddError::AlreadyPresentTrustedStaticDnsMapping(
1306                host, sock_addr,
1307            ));
1308        }
1309        if let Entry::Vacant(e) = self.static_dns_mapping.entry(host.clone()) {
1310            e.insert(sock_addr);
1311            Ok(self)
1312        } else {
1313            Err(AddError::AlreadyPresentStaticDnsMapping(host, sock_addr))
1314        }
1315    }
1316
1317    /// Removes a static DNS mapping.
1318    ///
1319    /// Note: The host should be in its canonical form (lowercase, punycode for IDN).
1320    pub fn remove_static_dns_mapping(mut self, host: &str) -> Self {
1321        self.static_dns_mapping.remove(host);
1322        self
1323    }
1324
1325    /// Sets the static DNS mappings.
1326    ///
1327    /// Note: The hosts should be in their canonical form (lowercase, punycode for IDN).
1328    pub fn static_dns_mappings(
1329        mut self,
1330        mappings: HashMap<String, SocketAddr>,
1331    ) -> Result<Self, AddError> {
1332        for (host, ip) in &mappings {
1333            if !utils::authority::is_valid_host(host) {
1334                return Err(AddError::InvalidEntity(host.clone()));
1335            }
1336            if self.trusted_static_dns_mapping.contains_key(host) {
1337                return Err(AddError::AlreadyPresentTrustedStaticDnsMapping(
1338                    host.clone(),
1339                    *ip,
1340                ));
1341            }
1342            if self.static_dns_mapping.contains_key(host) {
1343                return Err(AddError::AlreadyPresentStaticDnsMapping(host.clone(), *ip));
1344            }
1345            self.static_dns_mapping.insert(host.to_string(), *ip);
1346        }
1347        Ok(self)
1348    }
1349
1350    /// Clears the static DNS mappings.
1351    pub fn clear_static_dns_mappings(mut self) -> Self {
1352        self.static_dns_mapping.clear();
1353        self
1354    }
1355
1356    /// Add a trusted static DNS mapping.
1357    ///
1358    /// Unlike [`Self::add_static_dns_mapping`], the resolved address is meant to
1359    /// bypass the IP and port ACL entirely - only use this for mappings you trust
1360    /// regardless of what the ACL would otherwise say (e.g. pinning a hostname to an
1361    /// internal address on purpose).
1362    ///
1363    /// Note: The host should be in its canonical form (lowercase, punycode for IDN).
1364    pub fn add_trusted_static_dns_mapping(
1365        mut self,
1366        host: String,
1367        sock_addr: SocketAddr,
1368    ) -> Result<Self, AddError> {
1369        if !utils::authority::is_valid_host(&host) {
1370            return Err(AddError::InvalidEntity(host));
1371        }
1372        if self.static_dns_mapping.contains_key(&host) {
1373            return Err(AddError::AlreadyPresentStaticDnsMapping(host, sock_addr));
1374        }
1375        if let Entry::Vacant(e) = self.trusted_static_dns_mapping.entry(host.clone()) {
1376            e.insert(sock_addr);
1377            Ok(self)
1378        } else {
1379            Err(AddError::AlreadyPresentTrustedStaticDnsMapping(
1380                host, sock_addr,
1381            ))
1382        }
1383    }
1384
1385    /// Removes a trusted static DNS mapping.
1386    ///
1387    /// Note: The host should be in its canonical form (lowercase, punycode for IDN).
1388    pub fn remove_trusted_static_dns_mapping(mut self, host: &str) -> Self {
1389        self.trusted_static_dns_mapping.remove(host);
1390        self
1391    }
1392
1393    /// Sets the trusted static DNS mappings.
1394    ///
1395    /// Note: The hosts should be in their canonical form (lowercase, punycode for IDN).
1396    pub fn trusted_static_dns_mappings(
1397        mut self,
1398        mappings: HashMap<String, SocketAddr>,
1399    ) -> Result<Self, AddError> {
1400        for (host, ip) in &mappings {
1401            if !utils::authority::is_valid_host(host) {
1402                return Err(AddError::InvalidEntity(host.clone()));
1403            }
1404            if self.static_dns_mapping.contains_key(host) {
1405                return Err(AddError::AlreadyPresentStaticDnsMapping(host.clone(), *ip));
1406            }
1407            if self.trusted_static_dns_mapping.contains_key(host) {
1408                return Err(AddError::AlreadyPresentTrustedStaticDnsMapping(
1409                    host.clone(),
1410                    *ip,
1411                ));
1412            }
1413            self.trusted_static_dns_mapping
1414                .insert(host.to_string(), *ip);
1415        }
1416        Ok(self)
1417    }
1418
1419    /// Clears the trusted static DNS mappings.
1420    pub fn clear_trusted_static_dns_mappings(mut self) -> Self {
1421        self.trusted_static_dns_mapping.clear();
1422        self
1423    }
1424
1425    /// Adds a header to the allowed headers.
1426    ///
1427    /// If `value` is `None`, any value for the header is allowed.
1428    ///
1429    /// Note: Ensure header names are lowercased.
1430    pub fn add_allowed_header(
1431        mut self,
1432        header: String,
1433        value: Option<String>,
1434    ) -> Result<Self, AddError> {
1435        if self.denied_headers.contains_key(&header) {
1436            Err(AddError::AlreadyDeniedHeader(header, value.clone()))
1437        } else if let Entry::Vacant(e) = self.allowed_headers.entry(header.clone()) {
1438            e.insert(value);
1439            Ok(self)
1440        } else {
1441            Err(AddError::AlreadyAllowedHeader(header, value))
1442        }
1443    }
1444
1445    /// Removes a header from the allowed headers.
1446    ///
1447    /// Note: Ensure header names are lowercased.
1448    pub fn remove_allowed_header(mut self, header: &str) -> Self {
1449        self.allowed_headers.remove(header);
1450        self
1451    }
1452
1453    /// Sets the allowed headers.
1454    ///
1455    /// Note: Ensure header names are lowercased.
1456    pub fn allowed_headers(
1457        mut self,
1458        headers: HashMap<String, Option<String>>,
1459    ) -> Result<Self, AddError> {
1460        for (header, value) in &headers {
1461            if self.denied_headers.contains_key(header) {
1462                return Err(AddError::AlreadyDeniedHeader(header.clone(), value.clone()));
1463            }
1464        }
1465        self.allowed_headers = headers;
1466        Ok(self)
1467    }
1468
1469    /// Clears the allowed headers.
1470    pub fn clear_allowed_headers(mut self) -> Self {
1471        self.allowed_headers.clear();
1472        self
1473    }
1474
1475    /// Adds a header to the denied headers.
1476    ///
1477    /// If `value` is `None`, any value for the header is denied.
1478    ///
1479    /// Note: Ensure header names are lowercased.
1480    pub fn add_denied_header(
1481        mut self,
1482        header: String,
1483        value: Option<String>,
1484    ) -> Result<Self, AddError> {
1485        if self.allowed_headers.contains_key(&header) {
1486            Err(AddError::AlreadyAllowedHeader(header, value.clone()))
1487        } else if let Entry::Vacant(e) = self.denied_headers.entry(header.clone()) {
1488            e.insert(value);
1489            Ok(self)
1490        } else {
1491            Err(AddError::AlreadyDeniedHeader(header, value))
1492        }
1493    }
1494
1495    /// Removes a header from the denied headers.
1496    ///
1497    /// Note: Ensure header names are lowercased.
1498    pub fn remove_denied_header(mut self, header: &str) -> Self {
1499        self.denied_headers.remove(header);
1500        self
1501    }
1502
1503    /// Sets the denied headers.
1504    ///
1505    /// Note: Ensure header names are lowercased.
1506    pub fn denied_headers(
1507        mut self,
1508        headers: HashMap<String, Option<String>>,
1509    ) -> Result<Self, AddError> {
1510        for (header, value) in &headers {
1511            if self.allowed_headers.contains_key(header) {
1512                return Err(AddError::AlreadyAllowedHeader(
1513                    header.clone(),
1514                    value.clone(),
1515                ));
1516            }
1517        }
1518        self.denied_headers = headers;
1519        Ok(self)
1520    }
1521
1522    /// Clears the denied headers.
1523    pub fn clear_denied_headers(mut self) -> Self {
1524        self.denied_headers.clear();
1525        self
1526    }
1527
1528    /// Adds a URL path to the allowed URL paths.
1529    ///
1530    /// Note: URL paths should start with a '/' and be properly URL-encoded.
1531    pub fn add_allowed_url_path(mut self, url_path: String) -> Result<Self, AddError> {
1532        if self.denied_url_paths.contains(&url_path)
1533            || self.denied_url_paths_router.at(&url_path).is_ok()
1534        {
1535            Err(AddError::AlreadyDeniedUrlPath(url_path))
1536        } else if self.allowed_url_paths.contains(&url_path)
1537            || self.allowed_url_paths_router.at(&url_path).is_ok()
1538        {
1539            Err(AddError::AlreadyAllowedUrlPath(url_path))
1540        } else {
1541            self.allowed_url_paths.push(url_path.clone());
1542            self.allowed_url_paths_router
1543                .insert(url_path, ())
1544                .map_err(|_| AddError::InvalidEntity("Invalid URL path".to_string()))?;
1545            Ok(self)
1546        }
1547    }
1548
1549    /// Removes a URL path from the allowed URL paths.
1550    ///
1551    /// Note: URL paths should start with a '/' and be properly URL-encoded.
1552    pub fn remove_allowed_url_path(mut self, url_path: &str) -> Self {
1553        self.allowed_url_paths.retain(|p| p != url_path);
1554        self.allowed_url_paths_router = {
1555            let mut router = Router::new();
1556            for url_path in &self.allowed_url_paths {
1557                router
1558                    .insert(url_path.clone(), ())
1559                    .expect("failed to insert url path");
1560            }
1561            router
1562        };
1563        self
1564    }
1565
1566    /// Sets the allowed URL paths.
1567    ///
1568    /// Note: URL paths should start with a '/' and be properly URL-encoded.
1569    pub fn allowed_url_paths(mut self, url_paths: Vec<String>) -> Result<Self, AddError> {
1570        for url_path in &url_paths {
1571            if self.denied_url_paths.contains(url_path)
1572                || self.denied_url_paths_router.at(url_path).is_ok()
1573            {
1574                return Err(AddError::AlreadyDeniedUrlPath(url_path.clone()));
1575            }
1576        }
1577        self.allowed_url_paths_router = Router::new();
1578        for url_path in &url_paths {
1579            self.allowed_url_paths_router
1580                .insert(url_path.clone(), ())
1581                .map_err(|_| AddError::InvalidEntity(format!("Invalid URL path: {url_path}")))?;
1582        }
1583        self.allowed_url_paths = url_paths;
1584        Ok(self)
1585    }
1586
1587    /// Clears the allowed URL paths.
1588    pub fn clear_allowed_url_paths(mut self) -> Self {
1589        self.allowed_url_paths.clear();
1590        self.allowed_url_paths_router = Router::new();
1591        self
1592    }
1593
1594    /// Adds a URL path to the denied URL paths.
1595    ///
1596    /// Note: URL paths should start with a '/' and be properly URL-encoded.
1597    pub fn add_denied_url_path(mut self, url_path: String) -> Result<Self, AddError> {
1598        if self.allowed_url_paths.contains(&url_path)
1599            || self.allowed_url_paths_router.at(&url_path).is_ok()
1600        {
1601            Err(AddError::AlreadyAllowedUrlPath(url_path))
1602        } else if self.denied_url_paths.contains(&url_path)
1603            || self.denied_url_paths_router.at(&url_path).is_ok()
1604        {
1605            Err(AddError::AlreadyDeniedUrlPath(url_path))
1606        } else {
1607            self.denied_url_paths.push(url_path.clone());
1608            self.denied_url_paths_router
1609                .insert(url_path, ())
1610                .map_err(|_| AddError::InvalidEntity("Invalid URL path".to_string()))?;
1611            Ok(self)
1612        }
1613    }
1614
1615    /// Removes a URL path from the denied URL paths.
1616    ///
1617    /// Note: URL paths should start with a '/' and be properly URL-encoded.
1618    pub fn remove_denied_url_path(mut self, url_path: &str) -> Self {
1619        self.denied_url_paths.retain(|p| p != url_path);
1620        self.denied_url_paths_router = {
1621            let mut router = Router::new();
1622            for url_path in &self.denied_url_paths {
1623                router
1624                    .insert(url_path.clone(), ())
1625                    .expect("failed to insert url path");
1626            }
1627            router
1628        };
1629        self
1630    }
1631
1632    /// Sets the denied URL paths.
1633    ///
1634    /// Note: URL paths should start with a '/' and be properly URL-encoded.
1635    pub fn denied_url_paths(mut self, url_paths: Vec<String>) -> Result<Self, AddError> {
1636        for url_path in &url_paths {
1637            if self.allowed_url_paths.contains(url_path)
1638                || self.allowed_url_paths_router.at(url_path).is_ok()
1639            {
1640                return Err(AddError::AlreadyAllowedUrlPath(url_path.clone()));
1641            }
1642        }
1643        self.denied_url_paths_router = Router::new();
1644        for url_path in &url_paths {
1645            self.denied_url_paths_router
1646                .insert(url_path.clone(), ())
1647                .map_err(|_| AddError::InvalidEntity(format!("Invalid URL path: {url_path}")))?;
1648        }
1649        self.denied_url_paths = url_paths;
1650        Ok(self)
1651    }
1652
1653    /// Clears the denied URL paths.
1654    pub fn clear_denied_url_paths(mut self) -> Self {
1655        self.denied_url_paths.clear();
1656        self.denied_url_paths_router = Router::new();
1657        self
1658    }
1659
1660    /// Builds the [`HttpAcl`], without any [`HttpAclHooks`] attached.
1661    ///
1662    /// This does not validate the configuration (uniqueness, overlaps, non-global IP
1663    /// ranges); use [`Self::try_build`] instead if the builder wasn't assembled
1664    /// entirely through this type's own fallible `add_*`/`allowed_*`/`denied_*`
1665    /// methods, e.g. if it was deserialized. See [`Self::try_build`] for details.
1666    pub fn build(self) -> HttpAcl {
1667        self.build_full(HttpAclHooks::default())
1668    }
1669
1670    /// Builds the [`HttpAcl`] with the given [`HttpAclHooks`] attached.
1671    ///
1672    /// This is the only way to attach a `ValidateFn`, `ModifyRequestFn`, or
1673    /// `ModifyResponseFn`; there is no dedicated builder setter for any of them.
1674    /// Like [`Self::build`], this does not validate the configuration; use
1675    /// [`Self::try_build_full`] for that.
1676    pub fn build_full(self, hooks: HttpAclHooks) -> HttpAcl {
1677        HttpAcl {
1678            allow_http: self.allow_http,
1679            allow_https: self.allow_https,
1680            allowed_methods: self.allowed_methods.into_iter().collect(),
1681            denied_methods: self.denied_methods.into_iter().collect(),
1682            allowed_hosts: self
1683                .allowed_hosts
1684                .into_iter()
1685                .filter(|h| !is_wildcard_host(h))
1686                .map(|x| x.into_boxed_str())
1687                .collect(),
1688            denied_hosts: self
1689                .denied_hosts
1690                .into_iter()
1691                .filter(|h| !is_wildcard_host(h))
1692                .map(|x| x.into_boxed_str())
1693                .collect(),
1694            allowed_host_patterns: self.allowed_host_patterns.into_boxed_slice(),
1695            denied_host_patterns: self.denied_host_patterns.into_boxed_slice(),
1696            allowed_port_ranges: self.allowed_port_ranges.into_boxed_slice(),
1697            denied_port_ranges: self.denied_port_ranges.into_boxed_slice(),
1698            allowed_ip_ranges: self.allowed_ip_ranges.into_boxed_slice(),
1699            denied_ip_ranges: self.denied_ip_ranges.into_boxed_slice(),
1700            allowed_headers: self
1701                .allowed_headers
1702                .into_iter()
1703                .map(|(k, v)| (k.into_boxed_str(), v.map(|s| s.into_boxed_str())))
1704                .collect(),
1705            denied_headers: self
1706                .denied_headers
1707                .into_iter()
1708                .map(|(k, v)| (k.into_boxed_str(), v.map(|s| s.into_boxed_str())))
1709                .collect(),
1710            allowed_url_paths_router: self.allowed_url_paths_router,
1711            denied_url_paths_router: self.denied_url_paths_router,
1712            static_dns_mapping: self
1713                .static_dns_mapping
1714                .into_iter()
1715                .map(|(k, v)| (k.into_boxed_str(), v))
1716                .collect(),
1717            trusted_static_dns_mapping: self
1718                .trusted_static_dns_mapping
1719                .into_iter()
1720                .map(|(k, v)| (k.into_boxed_str(), v))
1721                .collect(),
1722            validate_fn: hooks.validate_fn,
1723            modify_request_fn: hooks.modify_request_fn,
1724            modify_response_fn: hooks.modify_response_fn,
1725            allow_non_global_ip_ranges: self.allow_non_global_ip_ranges,
1726            method_acl_default: self.method_acl_default,
1727            host_acl_default: self.host_acl_default,
1728            port_acl_default: self.port_acl_default,
1729            ip_acl_default: self.ip_acl_default,
1730            header_acl_default: self.header_acl_default,
1731            url_path_acl_default: self.url_path_acl_default,
1732        }
1733    }
1734
1735    /// Builds the [`HttpAcl`] with the given [`HttpAclHooks`] attached, validating
1736    /// the configuration first.
1737    ///
1738    /// Checks each category for unique entries, non-overlapping ranges, and no host
1739    /// (or port range, IP range, header, and so on) present on both the allowed and
1740    /// denied lists, returning [`AddError`] on the first problem found. It also
1741    /// enforces that IP ranges are global unless [`Self::non_global_ip_ranges`] was
1742    /// set to `true`, which [`Self::add_allowed_ip_range`]/
1743    /// [`Self::add_denied_ip_range`] do not check themselves.
1744    ///
1745    /// Prefer this over [`Self::build_full`] whenever the builder wasn't assembled
1746    /// entirely through this type's own fallible methods, most notably a builder
1747    /// deserialized from an untrusted source: deserialization writes fields directly
1748    /// and bypasses the checks each `add_*` method normally performs, so this is also
1749    /// what rebuilds the URL path routers and wildcard host patterns skipped for that
1750    /// reason.
1751    pub fn try_build_full(mut self, hooks: HttpAclHooks) -> Result<HttpAcl, AddError> {
1752        if !utils::has_unique_elements(&self.allowed_methods) {
1753            return Err(AddError::NotUnique(
1754                "Allowed methods must be unique.".to_string(),
1755            ));
1756        }
1757        for method in &self.allowed_methods {
1758            if self.denied_methods.contains(method) {
1759                return Err(AddError::BothAllowedAndDenied(format!(
1760                    "Method `{}`",
1761                    method.as_str()
1762                )));
1763            }
1764        }
1765        if !utils::has_unique_elements(&self.denied_methods) {
1766            return Err(AddError::NotUnique(
1767                "Denied methods must be unique.".to_string(),
1768            ));
1769        }
1770        for method in &self.denied_methods {
1771            if self.allowed_methods.contains(method) {
1772                return Err(AddError::BothAllowedAndDenied(format!(
1773                    "Method `{}`",
1774                    method.as_str()
1775                )));
1776            }
1777        }
1778        if !utils::has_unique_elements(&self.allowed_hosts) {
1779            return Err(AddError::NotUnique(
1780                "Allowed hosts must be unique.".to_string(),
1781            ));
1782        }
1783        for host in &self.allowed_hosts {
1784            if is_wildcard_host(host) {
1785                match HostPattern::parse(host) {
1786                    Some(pattern) => {
1787                        if !self.allowed_host_patterns.contains(&pattern) {
1788                            self.allowed_host_patterns.push(pattern);
1789                        }
1790                    }
1791                    None => return Err(AddError::InvalidEntity(host.to_string())),
1792                }
1793            } else if !utils::authority::is_valid_host(host) {
1794                return Err(AddError::InvalidEntity(host.to_string()));
1795            }
1796            if self.denied_hosts.contains(host) {
1797                return Err(AddError::BothAllowedAndDenied(format!("Host `{host}`")));
1798            }
1799        }
1800        if !utils::has_unique_elements(&self.denied_hosts) {
1801            return Err(AddError::NotUnique(
1802                "Denied hosts must be unique.".to_string(),
1803            ));
1804        }
1805        for host in &self.denied_hosts {
1806            if is_wildcard_host(host) {
1807                match HostPattern::parse(host) {
1808                    Some(pattern) => {
1809                        if !self.denied_host_patterns.contains(&pattern) {
1810                            self.denied_host_patterns.push(pattern);
1811                        }
1812                    }
1813                    None => return Err(AddError::InvalidEntity(host.to_string())),
1814                }
1815            } else if !utils::authority::is_valid_host(host) {
1816                return Err(AddError::InvalidEntity(host.to_string()));
1817            }
1818            if self.allowed_hosts.contains(host) {
1819                return Err(AddError::BothAllowedAndDenied(format!("Host `{host}`")));
1820            }
1821        }
1822        if !utils::has_unique_elements(&self.allowed_port_ranges) {
1823            return Err(AddError::NotUnique(
1824                "Allowed port ranges must be unique.".to_string(),
1825            ));
1826        }
1827        if utils::has_overlapping_ranges(&self.allowed_port_ranges) {
1828            return Err(AddError::Overlaps(
1829                "Allowed port ranges must not overlap.".to_string(),
1830            ));
1831        }
1832        for port_range in &self.allowed_port_ranges {
1833            if self.denied_port_ranges.contains(port_range) {
1834                return Err(AddError::BothAllowedAndDenied(format!(
1835                    "Port range `{port_range:?}`"
1836                )));
1837            }
1838        }
1839        if !utils::has_unique_elements(&self.denied_port_ranges) {
1840            return Err(AddError::NotUnique(
1841                "Denied port ranges must be unique.".to_string(),
1842            ));
1843        }
1844        if utils::has_overlapping_ranges(&self.denied_port_ranges) {
1845            return Err(AddError::Overlaps(
1846                "Denied port ranges must not overlap.".to_string(),
1847            ));
1848        }
1849        for port_range in &self.denied_port_ranges {
1850            if self.allowed_port_ranges.contains(port_range) {
1851                return Err(AddError::BothAllowedAndDenied(format!(
1852                    "Port range `{port_range:?}`"
1853                )));
1854            }
1855        }
1856        if !utils::has_unique_elements(&self.allowed_ip_ranges) {
1857            return Err(AddError::NotUnique(
1858                "Allowed IP ranges must be unique.".to_string(),
1859            ));
1860        }
1861        if utils::has_overlapping_ranges(&self.allowed_ip_ranges) {
1862            return Err(AddError::Overlaps(
1863                "Allowed IP ranges must not overlap.".to_string(),
1864            ));
1865        }
1866        for ip_range in &self.allowed_ip_ranges {
1867            if self.denied_ip_ranges.contains(ip_range) {
1868                return Err(AddError::BothAllowedAndDenied(format!(
1869                    "IP range `{ip_range:?}`"
1870                )));
1871            }
1872
1873            if (!utils::ip::is_global_ip(ip_range.start())
1874                || !utils::ip::is_global_ip(ip_range.end()))
1875                && !self.allow_non_global_ip_ranges
1876            {
1877                return Err(AddError::NonGlobalIpRange(ip_range.clone()));
1878            }
1879        }
1880        if !utils::has_unique_elements(&self.denied_ip_ranges) {
1881            return Err(AddError::NotUnique(
1882                "Denied IP ranges must be unique.".to_string(),
1883            ));
1884        }
1885        if utils::has_overlapping_ranges(&self.denied_ip_ranges) {
1886            return Err(AddError::Overlaps(
1887                "Denied IP ranges must not overlap.".to_string(),
1888            ));
1889        }
1890        for ip_range in &self.denied_ip_ranges {
1891            if self.allowed_ip_ranges.contains(ip_range) {
1892                return Err(AddError::BothAllowedAndDenied(format!(
1893                    "IP range `{ip_range:?}`"
1894                )));
1895            }
1896
1897            if (!utils::ip::is_global_ip(ip_range.start())
1898                || !utils::ip::is_global_ip(ip_range.end()))
1899                && !self.allow_non_global_ip_ranges
1900            {
1901                return Err(AddError::NonGlobalIpRange(ip_range.clone()));
1902            }
1903        }
1904        if !utils::has_unique_elements(&self.static_dns_mapping) {
1905            return Err(AddError::NotUnique(
1906                "Static DNS mapping must be unique.".to_string(),
1907            ));
1908        }
1909        for (host, addr) in &self.static_dns_mapping {
1910            if !utils::authority::is_valid_host(host) {
1911                return Err(AddError::InvalidEntity(host.to_string()));
1912            }
1913            if self.trusted_static_dns_mapping.contains_key(host) {
1914                return Err(AddError::AlreadyPresentTrustedStaticDnsMapping(
1915                    host.to_string(),
1916                    *addr,
1917                ));
1918            }
1919        }
1920        if !utils::has_unique_elements(&self.trusted_static_dns_mapping) {
1921            return Err(AddError::NotUnique(
1922                "Trusted static DNS mapping must be unique.".to_string(),
1923            ));
1924        }
1925        for host in self.trusted_static_dns_mapping.keys() {
1926            if !utils::authority::is_valid_host(host) {
1927                return Err(AddError::InvalidEntity(host.to_string()));
1928            }
1929        }
1930        if !utils::has_unique_elements(&self.allowed_url_paths) {
1931            return Err(AddError::NotUnique(
1932                "Allowed URL paths must be unique.".to_string(),
1933            ));
1934        }
1935        for url_path in &self.allowed_url_paths {
1936            if self.denied_url_paths.contains(url_path)
1937                || self.denied_url_paths_router.at(url_path).is_ok()
1938            {
1939                return Err(AddError::BothAllowedAndDenied(format!(
1940                    "URL path `{url_path}`"
1941                )));
1942            } else if self.allowed_url_paths_router.at(url_path).is_err() {
1943                self.allowed_url_paths_router
1944                    .insert(url_path.clone(), ())
1945                    .map_err(|_| {
1946                        AddError::InvalidEntity(format!(
1947                            "Failed to insert allowed URL path `{url_path}`."
1948                        ))
1949                    })?;
1950            }
1951        }
1952        if !utils::has_unique_elements(&self.denied_url_paths) {
1953            return Err(AddError::NotUnique(
1954                "Denied URL paths must be unique.".to_string(),
1955            ));
1956        }
1957        for url_path in &self.denied_url_paths {
1958            if self.allowed_url_paths.contains(url_path)
1959                || self.allowed_url_paths_router.at(url_path).is_ok()
1960            {
1961                return Err(AddError::BothAllowedAndDenied(format!(
1962                    "URL path `{url_path}`"
1963                )));
1964            } else if self.denied_url_paths_router.at(url_path).is_err() {
1965                self.denied_url_paths_router
1966                    .insert(url_path.clone(), ())
1967                    .map_err(|_| {
1968                        AddError::InvalidEntity(format!(
1969                            "Failed to insert denied URL path `{url_path}`."
1970                        ))
1971                    })?;
1972            }
1973        }
1974        Ok(self.build_full(hooks))
1975    }
1976
1977    /// Builds the [`HttpAcl`], without any [`HttpAclHooks`] attached, validating
1978    /// the configuration first. See [`Self::try_build_full`] for what is validated
1979    /// and when to prefer this over [`Self::build`].
1980    pub fn try_build(self) -> Result<HttpAcl, AddError> {
1981        self.try_build_full(HttpAclHooks::default())
1982    }
1983}