trusted_proxies/
extract.rs

1/// A trait to extract required information from a request in order to fetch trusted information
2pub trait RequestInformation {
3    /// Check if the host header is allowed
4    ///
5    /// Most implementations should return `true` if the HTTP version is less than HTTP/2
6    fn is_host_header_allowed(&self) -> bool;
7
8    /// Get the host header of the request
9    fn host_header(&self) -> Option<&str>;
10
11    /// Get the authority of the request
12    fn authority(&self) -> Option<&str>;
13
14    /// Get the `Forwarded` header values
15    ///
16    /// A double-ended iterator is returned to allow the implementation to optimize the iteration in
17    /// case of multiple values
18    fn forwarded(&self) -> impl DoubleEndedIterator<Item = &str>;
19
20    /// Get the `X-Forwarded-For` header values
21    fn x_forwarded_for(&self) -> impl DoubleEndedIterator<Item = &str>;
22
23    /// Get the `X-Forwarded-Host` header values
24    fn x_forwarded_host(&self) -> impl DoubleEndedIterator<Item = &str>;
25
26    /// Get the `X-Forwarded-Proto` header values
27    fn x_forwarded_proto(&self) -> impl DoubleEndedIterator<Item = &str>;
28
29    /// Get the `X-Forwarded-By` header values
30    fn x_forwarded_by(&self) -> impl DoubleEndedIterator<Item = &str>;
31
32    /// Return the default host of the request when no trusted headers are found
33    ///
34    /// Default to host header if allowed or authority
35    fn default_host(&self) -> Option<&str> {
36        self.host_header()
37            // skip host header if HTTP/2, we should use :authority instead
38            .filter(|_| self.is_host_header_allowed())
39            .or_else(|| self.authority())
40    }
41
42    /// Return the default scheme of the request when no trusted headers are found
43    fn default_scheme(&self) -> Option<&str>;
44}
45
46#[cfg(feature = "http")]
47mod http {
48    use super::RequestInformation;
49
50    impl<T> RequestInformation for http::Request<T> {
51        fn is_host_header_allowed(&self) -> bool {
52            self.version() < http::Version::HTTP_2
53        }
54
55        fn host_header(&self) -> Option<&str> {
56            self.headers()
57                .get("host")
58                .and_then(|value| value.to_str().ok())
59        }
60
61        fn authority(&self) -> Option<&str> {
62            self.uri().authority().map(|auth| auth.as_str())
63        }
64
65        fn forwarded(&self) -> impl DoubleEndedIterator<Item = &str> {
66            self.headers()
67                .get_all("forwarded")
68                .iter()
69                .filter_map(|value| value.to_str().ok())
70        }
71
72        fn x_forwarded_for(&self) -> impl DoubleEndedIterator<Item = &str> {
73            self.headers()
74                .get_all("x-forwarded-for")
75                .iter()
76                .filter_map(|value| value.to_str().ok())
77        }
78
79        fn x_forwarded_host(&self) -> impl DoubleEndedIterator<Item = &str> {
80            self.headers()
81                .get_all("x-forwarded-host")
82                .iter()
83                .filter_map(|value| value.to_str().ok())
84        }
85
86        fn x_forwarded_proto(&self) -> impl DoubleEndedIterator<Item = &str> {
87            self.headers()
88                .get_all("x-forwarded-proto")
89                .iter()
90                .filter_map(|value| value.to_str().ok())
91        }
92
93        fn x_forwarded_by(&self) -> impl DoubleEndedIterator<Item = &str> {
94            self.headers()
95                .get_all("x-forwarded-by")
96                .iter()
97                .filter_map(|value| value.to_str().ok())
98        }
99
100        fn default_scheme(&self) -> Option<&str> {
101            self.uri().scheme_str()
102        }
103    }
104
105    impl RequestInformation for http::request::Parts {
106        fn is_host_header_allowed(&self) -> bool {
107            self.version < http::Version::HTTP_2
108        }
109
110        fn host_header(&self) -> Option<&str> {
111            self.headers
112                .get("host")
113                .and_then(|value| value.to_str().ok())
114        }
115
116        fn authority(&self) -> Option<&str> {
117            self.uri.authority().map(|auth| auth.as_str())
118        }
119
120        fn forwarded(&self) -> impl DoubleEndedIterator<Item = &str> {
121            self.headers
122                .get_all("forwarded")
123                .iter()
124                .filter_map(|value| value.to_str().ok())
125        }
126
127        fn x_forwarded_for(&self) -> impl DoubleEndedIterator<Item = &str> {
128            self.headers
129                .get_all("x-forwarded-for")
130                .iter()
131                .filter_map(|value| value.to_str().ok())
132        }
133
134        fn x_forwarded_host(&self) -> impl DoubleEndedIterator<Item = &str> {
135            self.headers
136                .get_all("x-forwarded-host")
137                .iter()
138                .filter_map(|value| value.to_str().ok())
139        }
140
141        fn x_forwarded_proto(&self) -> impl DoubleEndedIterator<Item = &str> {
142            self.headers
143                .get_all("x-forwarded-proto")
144                .iter()
145                .filter_map(|value| value.to_str().ok())
146        }
147
148        fn x_forwarded_by(&self) -> impl DoubleEndedIterator<Item = &str> {
149            self.headers
150                .get_all("x-forwarded-by")
151                .iter()
152                .filter_map(|value| value.to_str().ok())
153        }
154
155        fn default_scheme(&self) -> Option<&str> {
156            self.uri.scheme_str()
157        }
158    }
159}