static_web_server/
https_redirect.rs1use headers::{HeaderMapExt, Host};
10use hyper::{
11 Body, Request, Response, StatusCode,
12 header::{HeaderValue, LOCATION},
13};
14use std::sync::Arc;
15
16use crate::Result;
17
18pub struct RedirectOpts {
20 pub https_hostname: String,
22 pub https_port: u16,
24 pub allowed_hosts: Vec<String>,
26}
27
28pub fn redirect_to_https<T>(
30 req: &Request<T>,
31 opts: Arc<RedirectOpts>,
32) -> Result<Response<Body>, StatusCode> {
33 if let Some(ref host) = req.headers().typed_get::<Host>() {
34 let from_hostname = host.hostname();
35 if !opts
36 .allowed_hosts
37 .iter()
38 .any(|s| s.as_str() == from_hostname)
39 {
40 tracing::debug!("redirect host is not allowed!");
41 return Err(StatusCode::BAD_REQUEST);
42 }
43
44 let url = format!(
45 "https://{}:{}{}",
46 opts.https_hostname,
47 opts.https_port,
48 req.uri()
49 );
50 tracing::debug!("https redirect to {}", url);
51
52 let location = match HeaderValue::from_str(&url) {
53 Ok(location) => location,
54 Err(err) => {
55 tracing::error!("invalid https redirect location `{url}`: {err:?}");
56 return Err(StatusCode::BAD_REQUEST);
57 }
58 };
59
60 let mut resp = Response::new(Body::empty());
61 *resp.status_mut() = StatusCode::MOVED_PERMANENTLY;
62 resp.headers_mut().insert(LOCATION, location);
63 return Ok(resp);
64 }
65
66 tracing::debug!("redirect host was not determined!");
67 Err(StatusCode::BAD_REQUEST)
68}