1use actix_http::{header, uri::Uri, RequestHead};
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 .or_else(|| req.uri.host())
70 .and_then(|host| host.parse().ok())
71}
72
73#[doc(hidden)]
74pub struct HostGuard {
75 host: String,
76 scheme: Option<String>,
77}
78
79impl HostGuard {
80 pub fn scheme<H: AsRef<str>>(mut self, scheme: H) -> HostGuard {
82 self.scheme = Some(scheme.as_ref().to_string());
83 self
84 }
85}
86
87impl Guard for HostGuard {
88 fn check(&self, ctx: &GuardContext<'_>) -> bool {
89 let req_host_uri = match get_host_uri(ctx.head()) {
91 Some(uri) => uri,
92
93 None => return false,
95 };
96
97 match req_host_uri.host() {
98 Some(uri_host) if self.host == uri_host => {}
100
101 _ => return false,
105 }
106
107 if let Some(ref scheme) = self.scheme {
108 if let Some(ref req_host_uri_scheme) = req_host_uri.scheme_str() {
109 return scheme == req_host_uri_scheme;
110 }
111
112 }
115
116 true
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use crate::test::TestRequest;
125
126 #[test]
127 fn host_from_header() {
128 let req = TestRequest::default()
129 .insert_header((
130 header::HOST,
131 header::HeaderValue::from_static("www.rust-lang.org"),
132 ))
133 .to_srv_request();
134
135 let host = Host("www.rust-lang.org");
136 assert!(host.check(&req.guard_ctx()));
137
138 let host = Host("www.rust-lang.org").scheme("https");
139 assert!(host.check(&req.guard_ctx()));
140
141 let host = Host("blog.rust-lang.org");
142 assert!(!host.check(&req.guard_ctx()));
143
144 let host = Host("blog.rust-lang.org").scheme("https");
145 assert!(!host.check(&req.guard_ctx()));
146
147 let host = Host("crates.io");
148 assert!(!host.check(&req.guard_ctx()));
149
150 let host = Host("localhost");
151 assert!(!host.check(&req.guard_ctx()));
152 }
153
154 #[test]
155 fn host_without_header() {
156 let req = TestRequest::default()
157 .uri("www.rust-lang.org")
158 .to_srv_request();
159
160 let host = Host("www.rust-lang.org");
161 assert!(host.check(&req.guard_ctx()));
162
163 let host = Host("www.rust-lang.org").scheme("https");
164 assert!(host.check(&req.guard_ctx()));
165
166 let host = Host("blog.rust-lang.org");
167 assert!(!host.check(&req.guard_ctx()));
168
169 let host = Host("blog.rust-lang.org").scheme("https");
170 assert!(!host.check(&req.guard_ctx()));
171
172 let host = Host("crates.io");
173 assert!(!host.check(&req.guard_ctx()));
174
175 let host = Host("localhost");
176 assert!(!host.check(&req.guard_ctx()));
177 }
178
179 #[test]
180 fn host_scheme() {
181 let req = TestRequest::default()
182 .insert_header((
183 header::HOST,
184 header::HeaderValue::from_static("https://www.rust-lang.org"),
185 ))
186 .to_srv_request();
187
188 let host = Host("www.rust-lang.org").scheme("https");
189 assert!(host.check(&req.guard_ctx()));
190
191 let host = Host("www.rust-lang.org");
192 assert!(host.check(&req.guard_ctx()));
193
194 let host = Host("www.rust-lang.org").scheme("http");
195 assert!(!host.check(&req.guard_ctx()));
196
197 let host = Host("blog.rust-lang.org");
198 assert!(!host.check(&req.guard_ctx()));
199
200 let host = Host("blog.rust-lang.org").scheme("https");
201 assert!(!host.check(&req.guard_ctx()));
202
203 let host = Host("crates.io").scheme("https");
204 assert!(!host.check(&req.guard_ctx()));
205
206 let host = Host("localhost");
207 assert!(!host.check(&req.guard_ctx()));
208 }
209}