From 7d292699864d39c5d034ade5aa55d7267ae43cb9 Mon Sep 17 00:00:00 2001 From: Max Carrara Date: Tue, 21 Nov 2023 11:08:45 +0100 Subject: [PATCH] rest-server: Add `Redirector` The `Redirector` is a simple `Service` that redirects HTTP requests to HTTPS and can be served by a `hyper::Server`. Signed-off-by: Max Carrara Tested-by: Lukas Wagner Reviewed-by: Lukas Wagner Signed-off-by: Wolfgang Bumiller --- proxmox-rest-server/src/lib.rs | 2 +- proxmox-rest-server/src/rest.rs | 73 +++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/proxmox-rest-server/src/lib.rs b/proxmox-rest-server/src/lib.rs index bc5be01d..1c64ffb4 100644 --- a/proxmox-rest-server/src/lib.rs +++ b/proxmox-rest-server/src/lib.rs @@ -48,7 +48,7 @@ mod api_config; pub use api_config::{ApiConfig, AuthError, AuthHandler, IndexHandler}; mod rest; -pub use rest::RestServer; +pub use rest::{Redirector, RestServer}; pub mod connection; diff --git a/proxmox-rest-server/src/rest.rs b/proxmox-rest-server/src/rest.rs index 2ccd4d51..3e10a3a7 100644 --- a/proxmox-rest-server/src/rest.rs +++ b/proxmox-rest-server/src/rest.rs @@ -97,6 +97,79 @@ impl Service<&T> for RestServer { } } +pub struct Redirector; + +impl Default for Redirector { + fn default() -> Self { + Redirector::new() + } +} + +impl Redirector { + pub fn new() -> Self { + Self {} + } +} + +impl Service<&T> for Redirector { + type Response = RedirectService; + type Error = Error; + type Future = std::future::Ready>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _ctx: &T) -> Self::Future { + std::future::ready(Ok(RedirectService {})) + } +} + +pub struct RedirectService; + +impl Service> for RedirectService { + type Response = Response; + type Error = anyhow::Error; + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: Request) -> Self::Future { + let future = async move { + let header_host_value = req + .headers() + .get("host") + .and_then(|value| value.to_str().ok()); + + let response = if let Some(value) = header_host_value { + let location_value = String::from_iter(["https://", value]); + + let status_code = if matches!(*req.method(), http::Method::GET | http::Method::HEAD) + { + StatusCode::MOVED_PERMANENTLY + } else { + StatusCode::PERMANENT_REDIRECT + }; + + Response::builder() + .status(status_code) + .header("Location", String::from(location_value)) + .body(Body::empty())? + } else { + Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::empty())? + }; + + Ok(response) + }; + + future.boxed() + } +} + pub trait PeerAddress { fn peer_addr(&self) -> Result; }