1use actix_http::{header, uri::Uri, RequestHead, Version};
2
3use super::{Guard, GuardContext};
4
5#[allow(non_snake_case)]
58pub fn Host(host: impl AsRef<str>) -> HostGuard {
59    HostGuard {
60        host: host.as_ref().to_string(),
61        scheme: None,
62    }
63}
64
65fn get_host_uri(req: &RequestHead) -> Option<Uri> {
66    req.headers
67        .get(header::HOST)
68        .and_then(|host_value| host_value.to_str().ok())
69        .filter(|_| req.version < Version::HTTP_2)
70        .or_else(|| req.uri.host())
71        .and_then(|host| host.parse().ok())
72}
73
74#[doc(hidden)]
75pub struct HostGuard {
76    host: String,
77    scheme: Option<String>,
78}
79
80impl HostGuard {
81    pub fn scheme<H: AsRef<str>>(mut self, scheme: H) -> HostGuard {
83        self.scheme = Some(scheme.as_ref().to_string());
84        self
85    }
86}
87
88impl Guard for HostGuard {
89    fn check(&self, ctx: &GuardContext<'_>) -> bool {
90        let req_host_uri = match get_host_uri(ctx.head()) {
92            Some(uri) => uri,
93
94            None => return false,
96        };
97
98        match req_host_uri.host() {
99            Some(uri_host) if self.host == uri_host => {}
101
102            _ => return false,
106        }
107
108        if let Some(ref scheme) = self.scheme {
109            if let Some(ref req_host_uri_scheme) = req_host_uri.scheme_str() {
110                return scheme == req_host_uri_scheme;
111            }
112
113            }
116
117        true
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125    use crate::test::TestRequest;
126
127    #[test]
128    fn host_not_from_header_if_http2() {
129        let req = TestRequest::default()
130            .uri("www.rust-lang.org")
131            .insert_header((
132                header::HOST,
133                header::HeaderValue::from_static("www.example.com"),
134            ))
135            .to_srv_request();
136
137        let host = Host("www.example.com");
138        assert!(host.check(&req.guard_ctx()));
139
140        let host = Host("www.rust-lang.org");
141        assert!(!host.check(&req.guard_ctx()));
142
143        let req = TestRequest::default()
144            .version(actix_http::Version::HTTP_2)
145            .uri("www.rust-lang.org")
146            .insert_header((
147                header::HOST,
148                header::HeaderValue::from_static("www.example.com"),
149            ))
150            .to_srv_request();
151
152        let host = Host("www.example.com");
153        assert!(!host.check(&req.guard_ctx()));
154
155        let host = Host("www.rust-lang.org");
156        assert!(host.check(&req.guard_ctx()));
157    }
158
159    #[test]
160    fn host_from_header() {
161        let req = TestRequest::default()
162            .insert_header((
163                header::HOST,
164                header::HeaderValue::from_static("www.rust-lang.org"),
165            ))
166            .to_srv_request();
167
168        let host = Host("www.rust-lang.org");
169        assert!(host.check(&req.guard_ctx()));
170
171        let host = Host("www.rust-lang.org").scheme("https");
172        assert!(host.check(&req.guard_ctx()));
173
174        let host = Host("blog.rust-lang.org");
175        assert!(!host.check(&req.guard_ctx()));
176
177        let host = Host("blog.rust-lang.org").scheme("https");
178        assert!(!host.check(&req.guard_ctx()));
179
180        let host = Host("crates.io");
181        assert!(!host.check(&req.guard_ctx()));
182
183        let host = Host("localhost");
184        assert!(!host.check(&req.guard_ctx()));
185    }
186
187    #[test]
188    fn host_without_header() {
189        let req = TestRequest::default()
190            .uri("www.rust-lang.org")
191            .to_srv_request();
192
193        let host = Host("www.rust-lang.org");
194        assert!(host.check(&req.guard_ctx()));
195
196        let host = Host("www.rust-lang.org").scheme("https");
197        assert!(host.check(&req.guard_ctx()));
198
199        let host = Host("blog.rust-lang.org");
200        assert!(!host.check(&req.guard_ctx()));
201
202        let host = Host("blog.rust-lang.org").scheme("https");
203        assert!(!host.check(&req.guard_ctx()));
204
205        let host = Host("crates.io");
206        assert!(!host.check(&req.guard_ctx()));
207
208        let host = Host("localhost");
209        assert!(!host.check(&req.guard_ctx()));
210    }
211
212    #[test]
213    fn host_scheme() {
214        let req = TestRequest::default()
215            .insert_header((
216                header::HOST,
217                header::HeaderValue::from_static("https://www.rust-lang.org"),
218            ))
219            .to_srv_request();
220
221        let host = Host("www.rust-lang.org").scheme("https");
222        assert!(host.check(&req.guard_ctx()));
223
224        let host = Host("www.rust-lang.org");
225        assert!(host.check(&req.guard_ctx()));
226
227        let host = Host("www.rust-lang.org").scheme("http");
228        assert!(!host.check(&req.guard_ctx()));
229
230        let host = Host("blog.rust-lang.org");
231        assert!(!host.check(&req.guard_ctx()));
232
233        let host = Host("blog.rust-lang.org").scheme("https");
234        assert!(!host.check(&req.guard_ctx()));
235
236        let host = Host("crates.io").scheme("https");
237        assert!(!host.check(&req.guard_ctx()));
238
239        let host = Host("localhost");
240        assert!(!host.check(&req.guard_ctx()));
241    }
242}