plane_dynamic_proxy/
https_redirect.rs

1use crate::body::{simple_empty_body, BoxedError, SimpleBody};
2use http::{
3    header,
4    uri::{Authority, Scheme},
5    Request, Response, StatusCode, Uri,
6};
7use hyper::{body::Incoming, service::Service};
8use std::{future::ready, pin::Pin, str::FromStr};
9
10/// A hyper service that redirects HTTP requests to HTTPS.
11#[derive(Debug, Clone)]
12pub struct HttpsRedirectService;
13
14impl HttpsRedirectService {
15    fn call_inner(request: Request<Incoming>) -> Result<Response<SimpleBody>, StatusCode> {
16        // Get the host header.
17        let hostname = request
18            .headers()
19            .get(header::HOST)
20            .ok_or(StatusCode::BAD_REQUEST)?;
21        // Parse the host header into an authority.
22        let authority =
23            Authority::from_str(hostname.to_str().map_err(|_| StatusCode::BAD_REQUEST)?)
24                .map_err(|_| StatusCode::BAD_REQUEST)?;
25        // Strip the port.
26        let authority =
27            Authority::from_str(authority.host()).expect("Valid host is always valid authority.");
28
29        let request_uri = request.uri().clone();
30
31        // Set the scheme to HTTPS
32        let mut parts = request_uri.into_parts();
33        parts.scheme = Some(Scheme::HTTPS);
34
35        parts.authority = Some(authority);
36
37        // Build the new URI
38        let new_uri = Uri::from_parts(parts).expect("URI is always valid");
39
40        let response = Response::builder()
41            .status(StatusCode::MOVED_PERMANENTLY)
42            .header(header::LOCATION, new_uri.to_string())
43            .body(simple_empty_body());
44
45        response.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
46    }
47}
48
49impl Service<Request<Incoming>> for HttpsRedirectService {
50    type Response = Response<SimpleBody>;
51    type Error = BoxedError;
52    type Future = Pin<Box<std::future::Ready<Result<Response<SimpleBody>, BoxedError>>>>;
53
54    fn call(&self, request: Request<Incoming>) -> Self::Future {
55        let result = Self::call_inner(request);
56
57        let result = match result {
58            Ok(response) => response,
59            Err(status) => {
60                tracing::error!("Error redirecting to HTTPS: {}", status);
61                Response::builder()
62                    .status(status)
63                    .body(simple_empty_body())
64                    .expect("Response is always valid")
65            }
66        };
67
68        Box::pin(ready(Ok(result)))
69    }
70}