rama_core/layer/
hijack.rs

1//! Middleware to hijack request to a [`Service`] which match using a [`Matcher`].
2//!
3//! Common usecases for hijacking requests are:
4//! - Redirecting requests to a different service based on the conditions specified in the [`Matcher`].
5//! - Block requests based on the conditions specified in the [`Matcher`] (and thus act like a Firewall).
6//!
7//! [`Service`]: crate
8//! [`Matcher`]: crate::matcher::Matcher
9
10use crate::{Context, Layer, Service, context::Extensions, matcher::Matcher};
11use rama_utils::macros::define_inner_service_accessors;
12
13/// Middleware to hijack request to a [`Service`] which match using a [`Matcher`].
14///
15/// Common usecases for hijacking requests are:
16/// - Redirecting requests to a different service based on the conditions specified in the [`Matcher`].
17/// - Block requests based on the conditions specified in the [`Matcher`] (and thus act like a Firewall).
18///
19/// [`Service`]: crate
20/// [`Matcher`]: crate::matcher::Matcher
21pub struct HijackService<S, H, M> {
22    inner: S,
23    hijack: H,
24    matcher: M,
25}
26
27impl<S, H, M> std::fmt::Debug for HijackService<S, H, M> {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        f.debug_struct("HijackService").finish()
30    }
31}
32
33impl<S, H, M> Clone for HijackService<S, H, M>
34where
35    S: Clone,
36    H: Clone,
37    M: Clone,
38{
39    fn clone(&self) -> Self {
40        Self {
41            inner: self.inner.clone(),
42            hijack: self.hijack.clone(),
43            matcher: self.matcher.clone(),
44        }
45    }
46}
47
48impl<S, H, M> HijackService<S, H, M> {
49    /// Create a new `HijackService`.
50    pub const fn new(inner: S, hijack: H, matcher: M) -> Self {
51        Self {
52            inner,
53            hijack,
54            matcher,
55        }
56    }
57
58    define_inner_service_accessors!();
59}
60
61impl<S, H, M, State, Request> Service<State, Request> for HijackService<S, H, M>
62where
63    S: Service<State, Request>,
64    H: Service<State, Request, Response: Into<S::Response>, Error: Into<S::Error>>,
65    M: Matcher<State, Request>,
66    State: Clone + Send + Sync + 'static,
67    Request: Send + 'static,
68{
69    type Response = S::Response;
70    type Error = S::Error;
71
72    async fn serve(
73        &self,
74        mut ctx: Context<State>,
75        req: Request,
76    ) -> Result<Self::Response, Self::Error> {
77        let mut ext = Extensions::new();
78        if self.matcher.matches(Some(&mut ext), &ctx, &req) {
79            ctx.extend(ext);
80            match self.hijack.serve(ctx, req).await {
81                Ok(response) => Ok(response.into()),
82                Err(err) => Err(err.into()),
83            }
84        } else {
85            self.inner.serve(ctx, req).await
86        }
87    }
88}
89
90/// Middleware to hijack request to a [`Service`] which match using a [`Matcher`].
91///
92/// Common usecases for hijacking requests are:
93/// - Redirecting requests to a different service based on the conditions specified in the [`Matcher`].
94/// - Block requests based on the conditions specified in the [`Matcher`] (and thus act like an Http Firewall).
95///
96/// [`Service`]: crate
97/// [`Matcher`]: crate::matcher::Matcher
98pub struct HijackLayer<H, M> {
99    hijack: H,
100    matcher: M,
101}
102
103impl<H, M> std::fmt::Debug for HijackLayer<H, M> {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        f.debug_struct("HijackLayer").finish()
106    }
107}
108
109impl<H, M> Clone for HijackLayer<H, M>
110where
111    H: Clone,
112    M: Clone,
113{
114    fn clone(&self) -> Self {
115        Self {
116            hijack: self.hijack.clone(),
117            matcher: self.matcher.clone(),
118        }
119    }
120}
121
122impl<H, M> HijackLayer<H, M> {
123    /// Create a new [`HijackLayer`].
124    pub const fn new(matcher: M, hijack: H) -> Self {
125        Self { hijack, matcher }
126    }
127}
128
129impl<S, H, M> Layer<S> for HijackLayer<H, M>
130where
131    H: Clone,
132    M: Clone,
133{
134    type Service = HijackService<S, H, M>;
135
136    fn layer(&self, inner: S) -> Self::Service {
137        HijackService::new(inner, self.hijack.clone(), self.matcher.clone())
138    }
139
140    fn into_layer(self, inner: S) -> Self::Service {
141        HijackService::new(inner, self.hijack, self.matcher)
142    }
143}