rush_sync_server/server/
redirect.rs1use crate::core::prelude::*;
3use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};
4
5pub struct HttpRedirectServer {
7 port: u16,
8 target_port: u16,
9}
10
11impl HttpRedirectServer {
12 pub fn new(port: u16, target_port: u16) -> Self {
13 Self { port, target_port }
14 }
15
16 async fn redirect_handler(req: HttpRequest, target_port: web::Data<u16>) -> HttpResponse {
17 let host = req
18 .headers()
19 .get("host")
20 .and_then(|v| v.to_str().ok())
21 .unwrap_or("localhost");
22
23 let host_clean = host.split(':').next().unwrap_or(host);
25
26 let path = req.uri().path();
27 let query = req.uri().query().unwrap_or("");
28
29 let redirect_url = if *target_port.get_ref() == 443 {
31 format!("https://{}{}", host_clean, path)
32 } else {
33 format!("https://{}:{}{}", host_clean, target_port.get_ref(), path)
34 };
35
36 let final_url = if !query.is_empty() {
37 format!("{}?{}", redirect_url, query)
38 } else {
39 redirect_url
40 };
41
42 log::debug!("HTTP→HTTPS: {} → {}", req.uri(), final_url);
43
44 HttpResponse::MovedPermanently()
45 .insert_header(("Location", final_url))
46 .insert_header(("Strict-Transport-Security", "max-age=31536000"))
47 .finish()
48 }
49
50 pub async fn run(self) -> Result<()> {
51 log::info!("HTTP redirect server starting on port {}", self.port);
52 log::info!("Redirecting to HTTPS port {}", self.target_port);
53
54 let target_port = self.target_port;
55
56 HttpServer::new(move || {
57 App::new()
58 .app_data(web::Data::new(target_port))
59 .default_service(web::route().to(Self::redirect_handler))
60 })
61 .bind(("0.0.0.0", self.port))
62 .map_err(|e| AppError::Validation(format!("Port {} bind failed: {}", self.port, e)))?
63 .run()
64 .await
65 .map_err(|e| AppError::Io(e))
66 }
67}