warp/
redirect.rs

1//! Redirect requests to a new location.
2//!
3//! The types in this module are helpers that implement [`Reply`], and easy
4//! to use in order to setup redirects.
5
6use http::{header, StatusCode};
7
8pub use self::sealed::AsLocation;
9use crate::reply::{self, Reply};
10
11/// HTTP 301 Moved Permanently
12/// Description: The requested resource has been permanently moved to a new URL.
13/// Usage: It is used when a URL has permanently moved to a new location. Search engines will update their index to the new URL. Browsers and clients will automatically cache this redirect, so subsequent requests for the old URL will automatically go to the new URL without making a request to the old URL.
14/// Common Use Case: Changing domain names, restructuring website URLs.
15///
16/// # Example
17///
18/// ```
19/// use warp::{http::Uri, Filter};
20///
21/// let route = warp::path("v1")
22///     .map(|| {
23///         warp::redirect(Uri::from_static("/v2"))
24///     });
25/// ```
26pub fn redirect(uri: impl AsLocation) -> impl Reply {
27    reply::with_header(
28        StatusCode::MOVED_PERMANENTLY,
29        header::LOCATION,
30        uri.header_value(),
31    )
32}
33
34/// HTTP 302 Found (or Temporary Redirect)
35/// Description: The requested resource can be found at a different URL temporarily.
36/// Usage: Historically, this status code was used for temporary redirects. However, its meaning was often misunderstood, and different clients treated it differently. As a result, it is recommended to use 307 (or 303) for temporary redirects instead.
37/// Common Use Case: Rarely used directly due to ambiguity; replaced by 307 or 303.
38///
39/// # Example
40///
41/// ```
42/// use warp::{http::Uri, Filter};
43///
44/// let route = warp::path("v1")
45///     .map(|| {
46///         warp::redirect::found(Uri::from_static("/v2"))
47///     });
48/// ```
49pub fn found(uri: impl AsLocation) -> impl Reply {
50    reply::with_header(StatusCode::FOUND, header::LOCATION, uri.header_value())
51}
52
53/// HTTP 303 See Other
54/// Description: The response to the request can be found at a different URL, and the client should retrieve it using the GET method.
55/// Usage: It is typically used to redirect the client to another URL using a GET request after processing a POST request. It ensures that the client doesn't repeat the POST request if they refresh the page.
56/// Common Use Case: After form submissions or any non-idempotent request.
57///
58/// The HTTP method of the request to the new location will always be `GET`.
59///
60/// # Example
61///
62/// ```
63/// use warp::{http::Uri, Filter};
64///
65/// let route = warp::path("v1")
66///     .map(|| {
67///         warp::redirect::see_other(Uri::from_static("/v2"))
68///     });
69/// ```
70pub fn see_other(uri: impl AsLocation) -> impl Reply {
71    reply::with_header(StatusCode::SEE_OTHER, header::LOCATION, uri.header_value())
72}
73
74/// HTTP 307 Temporary Redirect:
75/// Description: The requested resource can be found at a different URL temporarily.
76/// Usage: Similar to 302, but explicitly defined as a temporary redirect. The main difference between 307 and 302 is that 307 preserves the method of the original request when redirecting. If the original request was a POST, the subsequent request to the new URL will also be a POST.
77/// Common Use Case: Temporary redirects that should preserve the original request method.
78///
79/// This is similar to [`see_other`](fn@see_other) but the HTTP method and the body of the request
80/// to the new location will be the same as the method and body of the current request.
81///
82/// # Example
83///
84/// ```
85/// use warp::{http::Uri, Filter};
86///
87/// let route = warp::path("v1")
88///     .map(|| {
89///         warp::redirect::temporary(Uri::from_static("/v2"))
90///     });
91/// ```
92pub fn temporary(uri: impl AsLocation) -> impl Reply {
93    reply::with_header(
94        StatusCode::TEMPORARY_REDIRECT,
95        header::LOCATION,
96        uri.header_value(),
97    )
98}
99
100/// HTTP 308 Permanent Redirect
101/// Description: The requested resource has been permanently moved to a new URL, and future requests should use the new URL.
102/// Usage: Similar to 301, but like 307, it preserves the original request method when redirecting. It indicates that the redirection is permanent, and browsers and clients will cache this redirect like they do for 301.
103// Common Use Case: Permanently moving resources to a new URL while maintaining the original request method.
104///
105/// This is similar to [`redirect`](fn@redirect) but the HTTP method of the request to the new
106/// location will be the same as the method of the current request.
107///
108/// # Example
109///
110/// ```
111/// use warp::{http::Uri, Filter};
112///
113/// let route = warp::path("v1")
114///     .map(|| {
115///         warp::redirect::permanent(Uri::from_static("/v2"))
116///     });
117/// ```
118pub fn permanent(uri: impl AsLocation) -> impl Reply {
119    reply::with_header(
120        StatusCode::PERMANENT_REDIRECT,
121        header::LOCATION,
122        uri.header_value(),
123    )
124}
125
126mod sealed {
127    use bytes::Bytes;
128    use http::{header::HeaderValue, Uri};
129
130    /// Trait for redirect locations. Currently only a `Uri` can be used in
131    /// redirect.
132    /// This sealed trait exists to allow adding possibly new impls so other
133    /// arguments could be accepted, like maybe just `warp::redirect("/v2")`.
134    pub trait AsLocation: Sealed {}
135    pub trait Sealed {
136        fn header_value(self) -> HeaderValue;
137    }
138
139    impl AsLocation for Uri {}
140
141    impl Sealed for Uri {
142        fn header_value(self) -> HeaderValue {
143            let bytes = Bytes::from(self.to_string());
144            HeaderValue::from_maybe_shared(bytes).expect("Uri is a valid HeaderValue")
145        }
146    }
147}