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