Skip to main content

rama_http/matcher/
mod.rs

1//! [`service::Matcher`]s implementations to match on [`rama_http_types::Request`]s.
2//!
3//! See [`service::matcher` module] for more information.
4//!
5//! [`service::Matcher`]: rama_core::matcher::Matcher
6//! [`rama_http_types::Request`]: crate::Request
7//! [`service::matcher` module]: rama_core
8use crate::Request;
9use rama_core::{extensions::Extensions, matcher::IteratorMatcherExt};
10use rama_net::{
11    address::{AsDomainRef, Domain},
12    stream::matcher::SocketMatcher,
13    uri::PathPattern,
14};
15use rama_utils::thirdparty::{regex::Regex, wildcard::Wildcard};
16use std::fmt;
17use std::sync::Arc;
18
19mod method;
20#[doc(inline)]
21pub use method::MethodMatcher;
22
23mod domain;
24#[doc(inline)]
25pub use domain::DomainMatcher;
26
27pub mod uri;
28pub use uri::UriMatcher;
29
30mod version;
31#[doc(inline)]
32pub use version::VersionMatcher;
33
34pub(crate) mod path;
35#[doc(inline)]
36pub use path::{UriParams, UriParamsDeserializeError};
37
38mod header;
39#[doc(inline)]
40pub use header::HeaderMatcher;
41
42mod subdomain_trie;
43#[doc(inline)]
44pub use subdomain_trie::SubdomainTrieMatcher;
45
46/// A matcher that is used to match an http [`Request`]
47pub struct HttpMatcher<Body> {
48    kind: HttpMatcherKind<Body>,
49    negate: bool,
50}
51
52impl<Body> Clone for HttpMatcher<Body> {
53    fn clone(&self) -> Self {
54        Self {
55            kind: self.kind.clone(),
56            negate: self.negate,
57        }
58    }
59}
60
61impl<Body> fmt::Debug for HttpMatcher<Body> {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        f.debug_struct("HttpMatcher")
64            .field("kind", &self.kind)
65            .field("negate", &self.negate)
66            .finish()
67    }
68}
69
70/// A matcher that is used to match an http [`Request`]
71pub enum HttpMatcherKind<Body> {
72    /// zero or more [`HttpMatcher`]s that all need to match in order for the matcher to return `true`.
73    All(Vec<HttpMatcher<Body>>),
74    /// [`MethodMatcher`], a matcher that matches one or more HTTP methods.
75    Method(MethodMatcher),
76    /// A matcher based on the URI path, compiled from a [`PathPattern`]
77    /// (`{name}` / `{*name}` captures, `{}` / `{*}` globs). Captures are
78    /// recorded as [`UriParams`].
79    Path(PathPattern),
80    /// [`DomainMatcher`], a matcher based on the (sub)domain of the request's URI.
81    Domain(DomainMatcher),
82    /// [`VersionMatcher`], a matcher based on the HTTP version of the request.
83    Version(VersionMatcher),
84    /// zero or more [`HttpMatcher`]s that at least one needs to match in order for the matcher to return `true`.
85    Any(Vec<HttpMatcher<Body>>),
86    /// [`UriMatcher`], a matcher the request's URI, using a substring or regex pattern.
87    Uri(UriMatcher),
88    /// [`HeaderMatcher`], a matcher based on the [`Request`]'s headers.
89    Header(HeaderMatcher),
90    /// [`SocketMatcher`], a matcher that matches on the [`SocketAddr`] of the peer.
91    ///
92    /// [`SocketAddr`]: std::net::SocketAddr
93    Socket(SocketMatcher<Request<Body>>),
94    /// [`SubdomainTrieMatcher`], a matcher based on domain and subdomains using a trie structure.
95    SubdomainTrie(SubdomainTrieMatcher),
96    /// A custom matcher that implements [`rama_core::matcher::Matcher`].
97    Custom(Arc<dyn rama_core::matcher::Matcher<Request<Body>>>),
98}
99
100impl<Body> Clone for HttpMatcherKind<Body> {
101    fn clone(&self) -> Self {
102        match self {
103            Self::All(inner) => Self::All(inner.clone()),
104            Self::Method(inner) => Self::Method(*inner),
105            Self::Path(inner) => Self::Path(inner.clone()),
106            Self::Domain(inner) => Self::Domain(inner.clone()),
107            Self::Version(inner) => Self::Version(*inner),
108            Self::Any(inner) => Self::Any(inner.clone()),
109            Self::Uri(inner) => Self::Uri(inner.clone()),
110            Self::Header(inner) => Self::Header(inner.clone()),
111            Self::Socket(inner) => Self::Socket(inner.clone()),
112            Self::SubdomainTrie(inner) => Self::SubdomainTrie(inner.clone()),
113            Self::Custom(inner) => Self::Custom(inner.clone()),
114        }
115    }
116}
117
118impl<Body> fmt::Debug for HttpMatcherKind<Body> {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        match self {
121            Self::All(inner) => f.debug_tuple("All").field(inner).finish(),
122            Self::Method(inner) => f.debug_tuple("Method").field(inner).finish(),
123            Self::Path(inner) => f.debug_tuple("Path").field(inner).finish(),
124            Self::Domain(inner) => f.debug_tuple("Domain").field(inner).finish(),
125            Self::Version(inner) => f.debug_tuple("Version").field(inner).finish(),
126            Self::Any(inner) => f.debug_tuple("Any").field(inner).finish(),
127            Self::Uri(inner) => f.debug_tuple("Uri").field(inner).finish(),
128            Self::Header(inner) => f.debug_tuple("Header").field(inner).finish(),
129            Self::Socket(inner) => f.debug_tuple("Socket").field(inner).finish(),
130            Self::SubdomainTrie(inner) => f.debug_tuple("SubdomainTrie").field(inner).finish(),
131            Self::Custom(_) => f.debug_tuple("Custom").finish(),
132        }
133    }
134}
135
136impl<Body> HttpMatcher<Body> {
137    /// Create a new matcher that matches one or more HTTP methods.
138    ///
139    /// See [`MethodMatcher`] for more information.
140    #[must_use]
141    pub fn method(method: MethodMatcher) -> Self {
142        Self {
143            kind: HttpMatcherKind::Method(method),
144            negate: false,
145        }
146    }
147
148    /// Create a matcher that also matches one or more HTTP methods on top of the existing [`HttpMatcher`] matchers.
149    ///
150    /// See [`MethodMatcher`] for more information.
151    #[must_use]
152    pub fn and_method(self, method: MethodMatcher) -> Self {
153        self.and(Self::method(method))
154    }
155
156    /// Create a matcher that can also match one or more HTTP methods as an alternative to the existing [`HttpMatcher`] matchers.
157    ///
158    /// See [`MethodMatcher`] for more information.
159    #[must_use]
160    pub fn or_method(self, method: MethodMatcher) -> Self {
161        self.or(Self::method(method))
162    }
163
164    /// Create a new matcher that matches [`MethodMatcher::DELETE`] requests.
165    ///
166    /// See [`MethodMatcher`] for more information.
167    #[must_use]
168    pub fn method_delete() -> Self {
169        Self {
170            kind: HttpMatcherKind::Method(MethodMatcher::DELETE),
171            negate: false,
172        }
173    }
174
175    /// Add a new matcher that also matches [`MethodMatcher::DELETE`] on top of the existing [`HttpMatcher`] matchers.
176    ///
177    /// See [`MethodMatcher`] for more information.
178    #[must_use]
179    pub fn and_method_delete(self) -> Self {
180        self.and(Self::method_delete())
181    }
182
183    /// Add a new matcher that can also match [`MethodMatcher::DELETE`]
184    /// as an alternative to the existing [`HttpMatcher`] matchers.
185    ///
186    /// See [`MethodMatcher`] for more information.
187    #[must_use]
188    pub fn or_method_delete(self) -> Self {
189        self.or(Self::method_delete())
190    }
191
192    /// Create a new matcher that matches [`MethodMatcher::GET`] requests.
193    ///
194    /// See [`MethodMatcher`] for more information.
195    #[must_use]
196    pub fn method_get() -> Self {
197        Self {
198            kind: HttpMatcherKind::Method(MethodMatcher::GET),
199            negate: false,
200        }
201    }
202
203    /// Add a new matcher that also matches [`MethodMatcher::GET`] on top of the existing [`HttpMatcher`] matchers.
204    ///
205    /// See [`MethodMatcher`] for more information.
206    #[must_use]
207    pub fn and_method_get(self) -> Self {
208        self.and(Self::method_get())
209    }
210
211    /// Add a new matcher that can also match [`MethodMatcher::GET`]
212    /// as an alternative to the existing [`HttpMatcher`] matchers.
213    ///
214    /// See [`MethodMatcher`] for more information.
215    #[must_use]
216    pub fn or_method_get(self) -> Self {
217        self.or(Self::method_get())
218    }
219
220    /// Create a new matcher that matches [`MethodMatcher::HEAD`] requests.
221    ///
222    /// See [`MethodMatcher`] for more information.
223    #[must_use]
224    pub fn method_head() -> Self {
225        Self {
226            kind: HttpMatcherKind::Method(MethodMatcher::HEAD),
227            negate: false,
228        }
229    }
230
231    /// Add a new matcher that also matches [`MethodMatcher::HEAD`] on top of the existing [`HttpMatcher`] matchers.
232    ///
233    /// See [`MethodMatcher`] for more information.
234    #[must_use]
235    pub fn and_method_head(self) -> Self {
236        self.and(Self::method_head())
237    }
238
239    /// Add a new matcher that can also match [`MethodMatcher::HEAD`]
240    /// as an alternative to the existing [`HttpMatcher`] matchers.
241    ///
242    /// See [`MethodMatcher`] for more information.
243    #[must_use]
244    pub fn or_method_head(self) -> Self {
245        self.or(Self::method_head())
246    }
247
248    /// Create a new matcher that matches [`MethodMatcher::OPTIONS`] requests.
249    ///
250    /// See [`MethodMatcher`] for more information.
251    #[must_use]
252    pub fn method_options() -> Self {
253        Self {
254            kind: HttpMatcherKind::Method(MethodMatcher::OPTIONS),
255            negate: false,
256        }
257    }
258
259    /// Add a new matcher that also matches [`MethodMatcher::OPTIONS`] on top of the existing [`HttpMatcher`] matchers.
260    ///
261    /// See [`MethodMatcher`] for more information.
262    #[must_use]
263    pub fn and_method_options(self) -> Self {
264        self.and(Self::method_options())
265    }
266
267    /// Add a new matcher that can also match [`MethodMatcher::OPTIONS`]
268    /// as an alternative to the existing [`HttpMatcher`] matchers.
269    ///
270    /// See [`MethodMatcher`] for more information.
271    #[must_use]
272    pub fn or_method_options(self) -> Self {
273        self.or(Self::method_options())
274    }
275
276    /// Create a new matcher that matches [`MethodMatcher::PATCH`] requests.
277    ///
278    /// See [`MethodMatcher`] for more information.
279    #[must_use]
280    pub fn method_patch() -> Self {
281        Self {
282            kind: HttpMatcherKind::Method(MethodMatcher::PATCH),
283            negate: false,
284        }
285    }
286
287    /// Add a new matcher that also matches [`MethodMatcher::PATCH`] on top of the existing [`HttpMatcher`] matchers.
288    ///
289    /// See [`MethodMatcher`] for more information.
290    #[must_use]
291    pub fn and_method_patch(self) -> Self {
292        self.and(Self::method_patch())
293    }
294
295    /// Add a new matcher that can also match [`MethodMatcher::PATCH`]
296    /// as an alternative to the existing [`HttpMatcher`] matchers.
297    ///
298    /// See [`MethodMatcher`] for more information.
299    #[must_use]
300    pub fn or_method_patch(self) -> Self {
301        self.or(Self::method_patch())
302    }
303
304    /// Create a new matcher that matches [`MethodMatcher::POST`] requests.
305    ///
306    /// See [`MethodMatcher`] for more information.
307    #[must_use]
308    pub fn method_post() -> Self {
309        Self {
310            kind: HttpMatcherKind::Method(MethodMatcher::POST),
311            negate: false,
312        }
313    }
314
315    /// Add a new matcher that also matches [`MethodMatcher::POST`] on top of the existing [`HttpMatcher`] matchers.
316    ///
317    /// See [`MethodMatcher`] for more information.
318    #[must_use]
319    pub fn and_method_post(self) -> Self {
320        self.and(Self::method_post())
321    }
322
323    /// Add a new matcher that can also match [`MethodMatcher::POST`]
324    /// as an alternative to the existing [`HttpMatcher`] matchers.
325    ///
326    /// See [`MethodMatcher`] for more information.
327    #[must_use]
328    pub fn or_method_post(self) -> Self {
329        self.or(Self::method_post())
330    }
331
332    /// Create a new matcher that matches [`MethodMatcher::PUT`] requests.
333    ///
334    /// See [`MethodMatcher`] for more information.
335    #[must_use]
336    pub fn method_put() -> Self {
337        Self {
338            kind: HttpMatcherKind::Method(MethodMatcher::PUT),
339            negate: false,
340        }
341    }
342
343    /// Add a new matcher that also matches [`MethodMatcher::PUT`] on top of the existing [`HttpMatcher`] matchers.
344    ///
345    /// See [`MethodMatcher`] for more information.
346    #[must_use]
347    pub fn and_method_put(self) -> Self {
348        self.and(Self::method_put())
349    }
350
351    /// Add a new matcher that can also match [`MethodMatcher::PUT`]
352    /// as an alternative to the existing [`HttpMatcher`] matchers.
353    ///
354    /// See [`MethodMatcher`] for more information.
355    #[must_use]
356    pub fn or_method_put(self) -> Self {
357        self.or(Self::method_put())
358    }
359
360    /// Create a new matcher that matches [`MethodMatcher::QUERY`] requests.
361    ///
362    /// See [`MethodMatcher`] for more information.
363    #[must_use]
364    pub fn method_query() -> Self {
365        Self {
366            kind: HttpMatcherKind::Method(MethodMatcher::QUERY),
367            negate: false,
368        }
369    }
370
371    /// Add a new matcher that also matches [`MethodMatcher::QUERY`] on top of the existing [`HttpMatcher`] matchers.
372    ///
373    /// See [`MethodMatcher`] for more information.
374    #[must_use]
375    pub fn and_method_query(self) -> Self {
376        self.and(Self::method_query())
377    }
378
379    /// Add a new matcher that can also match [`MethodMatcher::QUERY`]
380    /// as an alternative to the existing [`HttpMatcher`] matchers.
381    ///
382    /// See [`MethodMatcher`] for more information.
383    #[must_use]
384    pub fn or_method_query(self) -> Self {
385        self.or(Self::method_query())
386    }
387
388    /// Create a new matcher that matches [`MethodMatcher::TRACE`] requests.
389    ///
390    /// See [`MethodMatcher`] for more information.
391    #[must_use]
392    pub fn method_trace() -> Self {
393        Self {
394            kind: HttpMatcherKind::Method(MethodMatcher::TRACE),
395            negate: false,
396        }
397    }
398
399    /// Add a new matcher that also matches [`MethodMatcher::TRACE`] on top of the existing [`HttpMatcher`] matchers.
400    ///
401    /// See [`MethodMatcher`] for more information.
402    #[must_use]
403    pub fn and_method_trace(self) -> Self {
404        self.and(Self::method_trace())
405    }
406
407    /// Add a new matcher that can also match [`MethodMatcher::TRACE`]
408    /// as an alternative to the existing [`HttpMatcher`] matchers.
409    ///
410    /// See [`MethodMatcher`] for more information.
411    #[must_use]
412    pub fn or_method_trace(self) -> Self {
413        self.or(Self::method_trace())
414    }
415
416    /// Create a new matcher that matches [`MethodMatcher::CONNECT`] requests.
417    ///
418    /// See [`MethodMatcher`] for more information.
419    #[must_use]
420    pub fn method_connect() -> Self {
421        Self {
422            kind: HttpMatcherKind::Method(MethodMatcher::CONNECT),
423            negate: false,
424        }
425    }
426
427    /// Add a new matcher that also matches [`MethodMatcher::CONNECT`] on top of the existing [`HttpMatcher`] matchers.
428    ///
429    /// See [`MethodMatcher`] for more information.
430    #[must_use]
431    pub fn and_method_connect(self) -> Self {
432        self.and(Self::method_connect())
433    }
434
435    /// Add a new matcher that can also match [`MethodMatcher::CONNECT`]
436    /// as an alternative to the existing [`HttpMatcher`] matchers.
437    ///
438    /// See [`MethodMatcher`] for more information.
439    #[must_use]
440    pub fn or_method_connect(self) -> Self {
441        self.or(Self::method_connect())
442    }
443
444    /// Create a [`DomainMatcher`] matcher, matching on the exact given [`Domain`].
445    #[must_use]
446    pub fn domain(domain: Domain) -> Self {
447        Self {
448            kind: HttpMatcherKind::Domain(DomainMatcher::exact(domain)),
449            negate: false,
450        }
451    }
452
453    /// Create a [`DomainMatcher`] matcher, matching on the exact given [`Domain`]
454    /// or a subdomain of it.
455    #[must_use]
456    pub fn subdomain(domain: Domain) -> Self {
457        Self {
458            kind: HttpMatcherKind::Domain(DomainMatcher::sub(domain)),
459            negate: false,
460        }
461    }
462
463    /// Create a [`DomainMatcher`] matcher to also match on top of the existing set of [`HttpMatcher`] matchers.
464    ///
465    /// See [`Self::domain`] for more information.
466    #[must_use]
467    pub fn and_domain(self, domain: Domain) -> Self {
468        self.and(Self::domain(domain))
469    }
470
471    /// Create a sub [`DomainMatcher`] matcher to also match on top of the existing set of [`HttpMatcher`] matchers.
472    ///
473    /// See [`Self::subdomain`] for more information.
474    #[must_use]
475    pub fn and_subdomain(self, domain: Domain) -> Self {
476        self.and(Self::subdomain(domain))
477    }
478
479    /// Create a [`DomainMatcher`] matcher to match as an alternative to the existing set of [`HttpMatcher`] matchers.
480    ///
481    /// See [`Self::domain`] for more information.
482    #[must_use]
483    pub fn or_domain(self, domain: Domain) -> Self {
484        self.or(Self::domain(domain))
485    }
486
487    /// Create a sub [`DomainMatcher`] matcher to match as an alternative to the existing set of [`HttpMatcher`] matchers.
488    ///
489    /// See [`Self::subdomain`] for more information.
490    #[must_use]
491    pub fn or_subdomain(self, domain: Domain) -> Self {
492        self.or(Self::subdomain(domain))
493    }
494
495    /// Create a [`VersionMatcher`] matcher.
496    #[must_use]
497    pub fn version(version: VersionMatcher) -> Self {
498        Self {
499            kind: HttpMatcherKind::Version(version),
500            negate: false,
501        }
502    }
503
504    /// Add a [`VersionMatcher`] matcher to matcher on top of the existing set of [`HttpMatcher`] matchers.
505    ///
506    /// See [`VersionMatcher`] for more information.
507    #[must_use]
508    pub fn and_version(self, version: VersionMatcher) -> Self {
509        self.and(Self::version(version))
510    }
511
512    /// Create a [`VersionMatcher`] matcher to match as an alternative to the existing set of [`HttpMatcher`] matchers.
513    ///
514    /// See [`VersionMatcher`] for more information.
515    #[must_use]
516    pub fn or_version(self, version: VersionMatcher) -> Self {
517        self.or(Self::version(version))
518    }
519
520    /// Create a [`UriMatcher`] matcher using a regex pattern.
521    #[must_use]
522    pub fn uri_regex(re: Regex) -> Self {
523        Self {
524            kind: HttpMatcherKind::Uri(UriMatcher::regex(re)),
525            negate: false,
526        }
527    }
528
529    /// Create a [`UriMatcher`] matcher using a wildcard pattern.
530    #[must_use]
531    pub fn uri_wildcard(wc: Wildcard<'static>) -> Self {
532        Self {
533            kind: HttpMatcherKind::Uri(UriMatcher::wildcard(wc)),
534            negate: false,
535        }
536    }
537
538    /// Create a [`UriMatcher`] matcher to match on top of the existing set of [`HttpMatcher`] matchers.
539    ///
540    /// See [`UriMatcher`] for more information.
541    #[must_use]
542    pub fn and_uri_regex(self, re: Regex) -> Self {
543        self.and(Self::uri_regex(re))
544    }
545
546    /// Create a [`UriMatcher`] matcher to match on top of the existing set of [`HttpMatcher`] matchers.
547    ///
548    /// See [`UriMatcher`] for more information.
549    #[must_use]
550    pub fn and_uri_wildcard(self, wc: Wildcard<'static>) -> Self {
551        self.and(Self::uri_wildcard(wc))
552    }
553
554    /// Create a [`UriMatcher`] matcher to match as an alternative to the existing set of [`HttpMatcher`] matchers.
555    ///
556    /// See [`UriMatcher`] for more information.
557    #[must_use]
558    pub fn or_uri_regex(self, re: Regex) -> Self {
559        self.or(Self::uri_regex(re))
560    }
561
562    /// Create a [`UriMatcher`] matcher to match as an alternative to the existing set of [`HttpMatcher`] matchers.
563    ///
564    /// See [`UriMatcher`] for more information.
565    #[must_use]
566    pub fn or_uri_wildcard(self, wc: Wildcard<'static>) -> Self {
567        self.or(Self::uri_wildcard(wc))
568    }
569
570    /// Create a path matcher from a [`PathPattern`] (`{name}` / `{*name}`
571    /// captures, `{}` / `{*}` globs).
572    #[must_use]
573    pub fn path(path: impl AsRef<str>) -> Self {
574        Self {
575            kind: HttpMatcherKind::Path(path::compile_pattern(path.as_ref())),
576            negate: false,
577        }
578    }
579
580    /// Create a path matcher that matches any path beginning with `path`,
581    /// matched at `/` segment boundaries (case-insensitive): `/api` matches
582    /// `/api` and `/api/users`, but not `/apixyz`. Compiles to a prefix-mode
583    /// [`PathPattern`], so `{name}` / `{*name}` captures are honored too.
584    #[must_use]
585    pub fn path_prefix(path: impl AsRef<str>) -> Self {
586        Self {
587            kind: HttpMatcherKind::Path(path::compile_prefix_pattern(path.as_ref())),
588            negate: false,
589        }
590    }
591
592    /// Add a path matcher to match on top of the existing set of [`HttpMatcher`] matchers.
593    #[must_use]
594    pub fn and_path(self, path: impl AsRef<str>) -> Self {
595        self.and(Self::path(path))
596    }
597
598    /// Create a path matcher to match as an alternative to the existing set of [`HttpMatcher`] matchers.
599    #[must_use]
600    pub fn or_path(self, path: impl AsRef<str>) -> Self {
601        self.or(Self::path(path))
602    }
603
604    /// Create a [`HeaderMatcher`] matcher.
605    #[must_use]
606    pub fn header(
607        name: rama_http_types::header::HeaderName,
608        value: rama_http_types::header::HeaderValue,
609    ) -> Self {
610        Self {
611            kind: HttpMatcherKind::Header(HeaderMatcher::is(name, value)),
612            negate: false,
613        }
614    }
615
616    /// Add a [`HeaderMatcher`] to match on top of the existing set of [`HttpMatcher`] matchers.
617    ///
618    /// See [`HeaderMatcher`] for more information.
619    #[must_use]
620    pub fn and_header(
621        self,
622        name: rama_http_types::header::HeaderName,
623        value: rama_http_types::header::HeaderValue,
624    ) -> Self {
625        self.and(Self::header(name, value))
626    }
627
628    /// Create a [`HeaderMatcher`] matcher to match as an alternative to the existing set of [`HttpMatcher`] matchers.
629    ///
630    /// See [`HeaderMatcher`] for more information.
631    #[must_use]
632    pub fn or_header(
633        self,
634        name: rama_http_types::header::HeaderName,
635        value: rama_http_types::header::HeaderValue,
636    ) -> Self {
637        self.or(Self::header(name, value))
638    }
639
640    /// Create a [`HeaderMatcher`] matcher when the given header exists
641    /// to match on the existence of a header.
642    #[must_use]
643    pub fn header_exists(name: rama_http_types::header::HeaderName) -> Self {
644        Self {
645            kind: HttpMatcherKind::Header(HeaderMatcher::exists(name)),
646            negate: false,
647        }
648    }
649
650    /// Add a [`HeaderMatcher`] to match when the given header exists
651    /// on top of the existing set of [`HttpMatcher`] matchers.
652    ///
653    /// See [`HeaderMatcher`] for more information.
654    #[must_use]
655    pub fn and_header_exists(self, name: rama_http_types::header::HeaderName) -> Self {
656        self.and(Self::header_exists(name))
657    }
658
659    /// Create a [`HeaderMatcher`] matcher to match when the given header exists
660    /// as an alternative to the existing set of [`HttpMatcher`] matchers.
661    ///
662    /// See [`HeaderMatcher`] for more information.
663    #[must_use]
664    pub fn or_header_exists(self, name: rama_http_types::header::HeaderName) -> Self {
665        self.or(Self::header_exists(name))
666    }
667
668    /// Create a [`HeaderMatcher`] matcher to match on it containing the given value.
669    #[must_use]
670    pub fn header_contains(
671        name: rama_http_types::header::HeaderName,
672        value: rama_http_types::header::HeaderValue,
673    ) -> Self {
674        Self {
675            kind: HttpMatcherKind::Header(HeaderMatcher::contains(name, value)),
676            negate: false,
677        }
678    }
679
680    /// Add a [`HeaderMatcher`] to match when it contains the given value
681    /// on top of the existing set of [`HttpMatcher`] matchers.
682    ///
683    /// See [`HeaderMatcher`] for more information.
684    #[must_use]
685    pub fn and_header_contains(
686        self,
687        name: rama_http_types::header::HeaderName,
688        value: rama_http_types::header::HeaderValue,
689    ) -> Self {
690        self.and(Self::header_contains(name, value))
691    }
692
693    /// Create a [`HeaderMatcher`] matcher to match if it contains the given value
694    /// as an alternative to the existing set of [`HttpMatcher`] matchers.
695    ///
696    /// See [`HeaderMatcher`] for more information.
697    #[must_use]
698    pub fn or_header_contains(
699        self,
700        name: rama_http_types::header::HeaderName,
701        value: rama_http_types::header::HeaderValue,
702    ) -> Self {
703        self.or(Self::header_contains(name, value))
704    }
705
706    /// Create a [`SocketMatcher`] matcher.
707    #[must_use]
708    pub fn socket(socket: SocketMatcher<Request<Body>>) -> Self {
709        Self {
710            kind: HttpMatcherKind::Socket(socket),
711            negate: false,
712        }
713    }
714
715    /// Add a [`SocketMatcher`] matcher to match on top of the existing set of [`HttpMatcher`] matchers.
716    ///
717    /// See [`SocketMatcher`] for more information.
718    #[must_use]
719    pub fn and_socket(self, socket: SocketMatcher<Request<Body>>) -> Self {
720        self.and(Self::socket(socket))
721    }
722
723    /// Create a [`SocketMatcher`] matcher to match as an alternative to the existing set of [`HttpMatcher`] matchers.
724    ///
725    /// See [`SocketMatcher`] for more information.
726    #[must_use]
727    pub fn or_socket(self, socket: SocketMatcher<Request<Body>>) -> Self {
728        self.or(Self::socket(socket))
729    }
730
731    /// Create a matcher to match for a GET request.
732    #[must_use]
733    pub fn get(path: impl AsRef<str>) -> Self {
734        Self::method_get().and_path(path)
735    }
736
737    /// Create a matcher that matches according to a custom predicate.
738    ///
739    /// See [`rama_core::matcher::Matcher`] for more information.
740    #[must_use]
741    pub fn custom<M>(matcher: M) -> Self
742    where
743        M: rama_core::matcher::Matcher<Request<Body>>,
744    {
745        Self {
746            kind: HttpMatcherKind::Custom(Arc::new(matcher)),
747            negate: false,
748        }
749    }
750
751    /// Add a custom matcher to match on top of the existing set of [`HttpMatcher`] matchers.
752    ///
753    /// See [`rama_core::matcher::Matcher`] for more information.
754    #[must_use]
755    pub fn and_custom<M>(self, matcher: M) -> Self
756    where
757        M: rama_core::matcher::Matcher<Request<Body>>,
758    {
759        self.and(Self::custom(matcher))
760    }
761
762    /// Create a custom matcher to match as an alternative to the existing set of [`HttpMatcher`] matchers.
763    ///
764    /// See [`rama_core::matcher::Matcher`] for more information.
765    #[must_use]
766    pub fn or_custom<M>(self, matcher: M) -> Self
767    where
768        M: rama_core::matcher::Matcher<Request<Body>>,
769    {
770        self.or(Self::custom(matcher))
771    }
772
773    /// Create a [`SubdomainTrieMatcher`] matcher that matches if the request domain is a subdomain of the provided domains.
774    ///
775    /// See [`SubdomainTrieMatcher`] for more information.
776    #[must_use]
777    pub fn any_subdomain<I, S>(domains: I) -> Self
778    where
779        I: IntoIterator<Item = S>,
780        S: AsDomainRef,
781    {
782        Self {
783            kind: HttpMatcherKind::SubdomainTrie(SubdomainTrieMatcher::new(domains)),
784            negate: false,
785        }
786    }
787
788    /// Add a [`SubdomainTrieMatcher`] matcher that matches if the request domain is a subdomain of the provided domains on top of the existing set of [`HttpMatcher`] matchers.
789    ///
790    /// See [`SubdomainTrieMatcher`] for more information.
791    #[must_use]
792    pub fn and_any_subdomain<I, S>(self, domains: I) -> Self
793    where
794        I: IntoIterator<Item = S>,
795        S: AsDomainRef,
796    {
797        self.and(Self::any_subdomain(domains))
798    }
799
800    /// Create a [`SubdomainTrieMatcher`] matcher that matches if the request domain is a subdomain of the provided domains as an alternative to the existing set of [`HttpMatcher`] matchers.
801    ///
802    /// See [`SubdomainTrieMatcher`] for more information.
803    #[must_use]
804    pub fn or_any_subdomain<I, S>(self, domains: I) -> Self
805    where
806        I: IntoIterator<Item = S>,
807        S: AsDomainRef,
808    {
809        self.or(Self::any_subdomain(domains))
810    }
811
812    /// Create a matcher to match for a POST request.
813    #[must_use]
814    pub fn post(path: impl AsRef<str>) -> Self {
815        Self::method_post().and_path(path)
816    }
817
818    /// Create a matcher to match for a PUT request.
819    #[must_use]
820    pub fn put(path: impl AsRef<str>) -> Self {
821        Self::method_put().and_path(path)
822    }
823
824    /// Create a matcher to match for a DELETE request.
825    #[must_use]
826    pub fn delete(path: impl AsRef<str>) -> Self {
827        Self::method_delete().and_path(path)
828    }
829
830    /// Create a matcher to match for a PATCH request.
831    #[must_use]
832    pub fn patch(path: impl AsRef<str>) -> Self {
833        Self::method_patch().and_path(path)
834    }
835
836    /// Create a matcher to match for a HEAD request.
837    #[must_use]
838    pub fn head(path: impl AsRef<str>) -> Self {
839        Self::method_head().and_path(path)
840    }
841
842    /// Create a matcher to match for a OPTIONS request.
843    #[must_use]
844    pub fn options(path: impl AsRef<str>) -> Self {
845        Self::method_options().and_path(path)
846    }
847
848    /// Create a matcher to match for a TRACE request.
849    #[must_use]
850    pub fn trace(path: impl AsRef<str>) -> Self {
851        Self::method_trace().and_path(path)
852    }
853
854    /// Create a matcher to match for a CONNECT request.
855    #[must_use]
856    pub fn connect(path: impl AsRef<str>) -> Self {
857        Self::method_connect().and_path(path)
858    }
859
860    /// Create a matcher to match for a QUERY request.
861    #[must_use]
862    pub fn query(path: impl AsRef<str>) -> Self {
863        Self::method_query().and_path(path)
864    }
865
866    /// Add a [`HttpMatcher`] to match on top of the existing set of [`HttpMatcher`] matchers.
867    #[must_use]
868    pub fn and(mut self, matcher: Self) -> Self {
869        match (self.negate, &mut self.kind) {
870            (false, HttpMatcherKind::All(v)) => {
871                v.push(matcher);
872                self
873            }
874            _ => Self {
875                kind: HttpMatcherKind::All(vec![self, matcher]),
876                negate: false,
877            },
878        }
879    }
880
881    /// Create a [`HttpMatcher`] matcher to match
882    /// as an alternative to the existing set of [`HttpMatcher`] matchers.
883    #[must_use]
884    pub fn or(mut self, matcher: Self) -> Self {
885        match (self.negate, &mut self.kind) {
886            (false, HttpMatcherKind::Any(v)) => {
887                v.push(matcher);
888                self
889            }
890            _ => Self {
891                kind: HttpMatcherKind::Any(vec![self, matcher]),
892                negate: false,
893            },
894        }
895    }
896
897    /// Negate the current matcher
898    #[must_use]
899    pub fn negate(self) -> Self {
900        Self {
901            kind: self.kind,
902            negate: true,
903        }
904    }
905
906    /// Returns the set of HTTP methods this matcher explicitly allows, as a [`MethodMatcher`].
907    ///
908    /// Returns `None` when the matcher imposes no method restriction
909    /// (e.g. a Path, Domain, or Custom matcher).
910    ///
911    /// Negation is applied uniformly: if `self` is negated the raw method set is
912    /// complemented, so `NOT GET` yields every known method except GET.
913    pub fn allowed_methods(&self) -> Option<MethodMatcher> {
914        let m = self.kind.allowed_methods();
915        if self.negate {
916            m.map(|m| m.complement())
917        } else {
918            m
919        }
920    }
921}
922
923impl<Body> HttpMatcherKind<Body> {
924    fn allowed_methods(&self) -> Option<MethodMatcher> {
925        match self {
926            Self::Method(m) => Some(*m),
927            Self::All(matchers) => {
928                // Intersect: AND together every child that has a method constraint.
929                // Non-method children (None) are skipped — they don't restrict methods.
930                matchers
931                    .iter()
932                    .filter_map(|m| m.allowed_methods())
933                    .reduce(|acc, next| acc.and_method(next))
934            }
935            Self::Any(matchers) => {
936                // Union: OR together all children.
937                // If any child is unconstrained (None), the union is unbounded → None.
938                matchers.iter().try_fold(MethodMatcher::NONE, |acc, m| {
939                    Some(acc.or_method(m.allowed_methods()?))
940                })
941            }
942            // All other variants impose no method constraint.
943            _ => None,
944        }
945    }
946}
947
948impl<Body> rama_core::matcher::Matcher<Request<Body>> for HttpMatcher<Body>
949where
950    Body: Send + 'static,
951{
952    fn matches(&self, ext: Option<&Extensions>, req: &Request<Body>) -> bool {
953        let matches = self.kind.matches(ext, req);
954        if self.negate { !matches } else { matches }
955    }
956}
957
958impl<Body> rama_core::matcher::Matcher<Request<Body>> for HttpMatcherKind<Body>
959where
960    Body: Send + 'static,
961{
962    fn matches(&self, ext: Option<&Extensions>, req: &Request<Body>) -> bool {
963        match self {
964            Self::All(all) => all.iter().matches_and(ext, req),
965            Self::Method(method) => method.matches(ext, req),
966            Self::Path(pattern) => path::match_pattern(pattern, ext, req.uri().path_ref_or_root()),
967            Self::Domain(domain) => domain.matches(ext, req),
968            Self::Version(version) => version.matches(ext, req),
969            Self::Uri(uri) => uri.matches(ext, req),
970            Self::Header(header) => header.matches(ext, req),
971            Self::Socket(socket) => socket.matches(ext, req),
972            Self::Any(all) => all.iter().matches_or(ext, req),
973            Self::SubdomainTrie(subdomain_trie) => subdomain_trie.matches(ext, req),
974            Self::Custom(matcher) => matcher.matches(ext, req),
975        }
976    }
977}
978
979#[cfg(test)]
980mod test {
981    use itertools::Itertools;
982
983    use rama_core::matcher::Matcher;
984
985    use super::*;
986
987    struct BooleanMatcher(bool);
988
989    impl Matcher<Request<()>> for BooleanMatcher {
990        fn matches(&self, _ext: Option<&Extensions>, _req: &Request<()>) -> bool {
991            self.0
992        }
993    }
994
995    #[test]
996    fn test_matcher_and_combination() {
997        for v in [true, false].into_iter().permutations(3) {
998            let expected = v[0] && v[1] && v[2];
999            let a = HttpMatcher::custom(BooleanMatcher(v[0]));
1000            let b = HttpMatcher::custom(BooleanMatcher(v[1]));
1001            let c = HttpMatcher::custom(BooleanMatcher(v[2]));
1002
1003            let matcher = a.and(b).and(c);
1004            let req = Request::builder().body(()).unwrap();
1005            assert_eq!(
1006                matcher.matches(None, &req),
1007                expected,
1008                "({matcher:#?}).matches({req:#?})",
1009            );
1010        }
1011    }
1012
1013    #[test]
1014    fn test_matcher_negation_with_and_combination() {
1015        for v in [true, false].into_iter().permutations(3) {
1016            let expected = !v[0] && v[1] && v[2];
1017            let a = HttpMatcher::custom(BooleanMatcher(v[0]));
1018            let b = HttpMatcher::custom(BooleanMatcher(v[1]));
1019            let c = HttpMatcher::custom(BooleanMatcher(v[2]));
1020
1021            let matcher = a.negate().and(b).and(c);
1022            let req = Request::builder().body(()).unwrap();
1023            assert_eq!(
1024                matcher.matches(None, &req),
1025                expected,
1026                "({matcher:#?}).matches({req:#?})",
1027            );
1028        }
1029    }
1030
1031    #[test]
1032    fn test_matcher_and_combination_negated() {
1033        for v in [true, false].into_iter().permutations(3) {
1034            let expected = !(v[0] && v[1] && v[2]);
1035            let a = HttpMatcher::custom(BooleanMatcher(v[0]));
1036            let b = HttpMatcher::custom(BooleanMatcher(v[1]));
1037            let c = HttpMatcher::custom(BooleanMatcher(v[2]));
1038
1039            let matcher = a.and(b).and(c).negate();
1040            let req = Request::builder().body(()).unwrap();
1041            assert_eq!(
1042                matcher.matches(None, &req),
1043                expected,
1044                "({matcher:#?}).matches({req:#?})",
1045            );
1046        }
1047    }
1048
1049    #[test]
1050    fn test_matcher_ors_combination() {
1051        for v in [true, false].into_iter().permutations(3) {
1052            let expected = v[0] || v[1] || v[2];
1053            let a = HttpMatcher::custom(BooleanMatcher(v[0]));
1054            let b = HttpMatcher::custom(BooleanMatcher(v[1]));
1055            let c = HttpMatcher::custom(BooleanMatcher(v[2]));
1056
1057            let matcher = a.or(b).or(c);
1058            let req = Request::builder().body(()).unwrap();
1059            assert_eq!(
1060                matcher.matches(None, &req),
1061                expected,
1062                "({matcher:#?}).matches({req:#?})",
1063            );
1064        }
1065    }
1066
1067    #[test]
1068    fn test_matcher_negation_with_ors_combination() {
1069        for v in [true, false].into_iter().permutations(3) {
1070            let expected = !v[0] || v[1] || v[2];
1071            let a = HttpMatcher::custom(BooleanMatcher(v[0]));
1072            let b = HttpMatcher::custom(BooleanMatcher(v[1]));
1073            let c = HttpMatcher::custom(BooleanMatcher(v[2]));
1074
1075            let matcher = a.negate().or(b).or(c);
1076            let req = Request::builder().body(()).unwrap();
1077            assert_eq!(
1078                matcher.matches(None, &req),
1079                expected,
1080                "({matcher:#?}).matches({req:#?})",
1081            );
1082        }
1083    }
1084
1085    #[test]
1086    fn test_matcher_ors_combination_negated() {
1087        for v in [true, false].into_iter().permutations(3) {
1088            let expected = !(v[0] || v[1] || v[2]);
1089            let a = HttpMatcher::custom(BooleanMatcher(v[0]));
1090            let b = HttpMatcher::custom(BooleanMatcher(v[1]));
1091            let c = HttpMatcher::custom(BooleanMatcher(v[2]));
1092
1093            let matcher = a.or(b).or(c).negate();
1094            let req = Request::builder().body(()).unwrap();
1095            assert_eq!(
1096                matcher.matches(None, &req),
1097                expected,
1098                "({matcher:#?}).matches({req:#?})",
1099            );
1100        }
1101    }
1102
1103    #[test]
1104    fn test_matcher_or_and_or_and_negation() {
1105        for v in [true, false].into_iter().permutations(5) {
1106            let expected = (v[0] || v[1]) && (v[2] || v[3]) && !v[4];
1107            let a = HttpMatcher::custom(BooleanMatcher(v[0]));
1108            let b = HttpMatcher::custom(BooleanMatcher(v[1]));
1109            let c = HttpMatcher::custom(BooleanMatcher(v[2]));
1110            let d = HttpMatcher::custom(BooleanMatcher(v[3]));
1111            let e = HttpMatcher::custom(BooleanMatcher(v[4]));
1112
1113            let matcher = (a.or(b)).and(c.or(d)).and(e.negate());
1114            let req = Request::builder().body(()).unwrap();
1115            assert_eq!(
1116                matcher.matches(None, &req),
1117                expected,
1118                "({matcher:#?}).matches({req:#?})",
1119            );
1120        }
1121    }
1122}