Skip to main content

rama_http/service/redirect/
static.rs

1use crate::Request;
2use crate::Response;
3use crate::service::web::response;
4use rama_core::Service;
5use rama_http_headers::{HeaderMapExt, Location};
6use rama_http_types::body::OptionalBody;
7use std::{convert::Infallible, fmt, marker::PhantomData};
8
9/// Service that redirects all requests to a static location.
10pub struct RedirectStatic<ResBody> {
11    resp: response::Redirect,
12    // Covariant over ResBody, no dropping of ResBody
13    _marker: PhantomData<fn() -> ResBody>,
14}
15
16impl<ResBody> RedirectStatic<ResBody> {
17    /// Create a new [`RedirectStatic`] that uses a [`303 See Other`][mdn] status code.
18    ///
19    /// This redirect instructs the client to change the method to GET for the subsequent request
20    /// to the given location, which is useful after successful form submission, file upload or when
21    /// you generally don't want the redirected-to page to observe the original request method and
22    /// body (if non-empty). If you want to preserve the request method and body,
23    /// [`RedirectStatic::temporary`] should be used instead.
24    ///
25    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303
26    pub fn to(loc: impl response::redirect::IntoRedirectLoc) -> Self {
27        Self {
28            resp: response::Redirect::to(loc),
29            _marker: PhantomData,
30        }
31    }
32
33    /// Create a new found (301) redirect response.
34    ///
35    /// Used if a resource is permanently moved.
36    ///
37    /// Use [`Self::permanent`] in case you wish to respect the original HTTP Method.
38    pub fn moved(loc: impl response::redirect::IntoRedirectLoc) -> Self {
39        Self {
40            resp: response::Redirect::moved(loc),
41            _marker: PhantomData,
42        }
43    }
44
45    /// Create a new found (302) redirect.
46    ///
47    /// Can be useful in flows where the resource was legit and found,
48    /// but a pre-requirement such as authentication wasn't met.
49    pub fn found(loc: impl response::redirect::IntoRedirectLoc) -> Self {
50        Self {
51            resp: response::Redirect::found(loc),
52            _marker: PhantomData,
53        }
54    }
55
56    /// Create a new temporary (307) redirect.
57    pub fn temporary(loc: impl response::redirect::IntoRedirectLoc) -> Self {
58        Self {
59            resp: response::Redirect::temporary(loc),
60            _marker: PhantomData,
61        }
62    }
63
64    /// Create a new permanent (308) redirect.
65    pub fn permanent(loc: impl response::redirect::IntoRedirectLoc) -> Self {
66        Self {
67            resp: response::Redirect::permanent(loc),
68            _marker: PhantomData,
69        }
70    }
71}
72
73impl<Body, ResBody> Service<Request<Body>> for RedirectStatic<ResBody>
74where
75    Body: Send + 'static,
76    ResBody: Send + 'static,
77{
78    type Output = Response<OptionalBody<ResBody>>;
79    type Error = Infallible;
80
81    async fn serve(&self, _req: Request<Body>) -> Result<Self::Output, Self::Error> {
82        let mut res = Response::default();
83        *res.status_mut() = self.resp.status_code();
84        res.headers_mut()
85            .typed_insert(Location::new(self.resp.location().clone()));
86        Ok(res)
87    }
88}
89
90impl<ResBody> fmt::Debug for RedirectStatic<ResBody> {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        f.debug_struct("RedirectStatic")
93            .field("response", &self.resp)
94            .finish()
95    }
96}
97
98impl<ResBody> Clone for RedirectStatic<ResBody> {
99    fn clone(&self) -> Self {
100        Self {
101            resp: self.resp.clone(),
102            _marker: PhantomData,
103        }
104    }
105}