Skip to main content

rama_http/layer/rewrite_uri/
service.rs

1use std::fmt;
2
3use crate::Request;
4use rama_core::{Service, telemetry::tracing};
5use rama_net::http::uri::{UriMatchError, UriMatchReplace};
6use rama_utils::macros::define_inner_service_accessors;
7use std::borrow::Cow;
8
9// TODO: in future we can move this outside of rama-http
10// and make it work on any request that is identified
11// by a `Uri`.
12
13/// Service which allows you to replace a [`Uri`]
14/// using a [`UriMatchReplace`] to match and replace the incoming request [`Uri`].
15///
16/// [`Uri`]: rama_net::uri::Uri
17pub struct RewriteUriService<R, S> {
18    match_replace: R,
19    inner: S,
20}
21
22impl<R, S> fmt::Debug for RewriteUriService<R, S>
23where
24    R: fmt::Debug,
25    S: fmt::Debug,
26{
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        f.debug_struct("RewriteUriService")
29            .field("match_replace", &self.match_replace)
30            .field("inner", &self.inner)
31            .finish()
32    }
33}
34
35impl<R, S> Clone for RewriteUriService<R, S>
36where
37    R: Clone,
38    S: Clone,
39{
40    fn clone(&self) -> Self {
41        Self {
42            match_replace: self.match_replace.clone(),
43            inner: self.inner.clone(),
44        }
45    }
46}
47
48impl<R, S> RewriteUriService<R, S> {
49    /// Creates a new `RewriteUriService` wrapping the `service`.
50    pub fn new(match_replace: R, service: S) -> Self {
51        Self {
52            match_replace,
53            inner: service,
54        }
55    }
56}
57
58impl<R, S> RewriteUriService<R, S> {
59    define_inner_service_accessors!();
60
61    /// Shared reference to the used [`UriMatchReplace`]
62    #[must_use]
63    pub fn match_replace_ref(&self) -> &R {
64        &self.match_replace
65    }
66
67    /// Exclusive reference to the used [`UriMatchReplace`]
68    #[must_use]
69    pub fn match_replace_mut(&mut self) -> &mut R {
70        &mut self.match_replace
71    }
72}
73
74impl<ReqBody, R, S> Service<Request<ReqBody>> for RewriteUriService<R, S>
75where
76    S: Service<Request<ReqBody>>,
77    R: UriMatchReplace + Send + Sync + 'static,
78    ReqBody: Send + 'static,
79{
80    type Output = S::Output;
81    type Error = S::Error;
82
83    async fn serve(&self, mut req: Request<ReqBody>) -> Result<Self::Output, Self::Error> {
84        let full_uri = req.request_uri();
85        if let Ok(uri) = self
86            .match_replace
87            .match_replace_uri(Cow::Owned(full_uri))
88            .inspect_err(|err| match err {
89                UriMatchError::NoMatch(uri) => {
90                    tracing::trace!("no match found for uri: {uri}; ignore")
91                }
92                UriMatchError::Unexpected(err) => {
93                    tracing::trace!("unexpected error while trying to match uri: {err}; ignore")
94                }
95            })
96        {
97            *req.uri_mut() = uri.into_owned()
98        }
99        self.inner.serve(req).await
100    }
101}