bws_web_server/server/
dynamic_tls.rs

1use crate::ssl::SslManager;
2use pingora::prelude::*;
3use rustls::ServerConfig as RustlsServerConfig;
4use std::collections::HashMap;
5use std::sync::Arc;
6use tokio::sync::RwLock;
7
8/// A dynamic TLS handler that can upgrade from HTTP to HTTPS
9pub struct DynamicTlsHandler {
10    ssl_managers: Arc<RwLock<HashMap<String, Arc<SslManager>>>>,
11    tls_configs: Arc<RwLock<HashMap<String, Arc<RustlsServerConfig>>>>,
12}
13
14impl DynamicTlsHandler {
15    pub fn new(ssl_managers: Arc<RwLock<HashMap<String, Arc<SslManager>>>>) -> Self {
16        Self {
17            ssl_managers,
18            tls_configs: Arc::new(RwLock::new(HashMap::new())),
19        }
20    }
21
22    /// Check if data looks like a TLS handshake
23    pub fn is_tls_handshake(data: &[u8]) -> bool {
24        if data.len() < 3 {
25            return false;
26        }
27
28        // TLS handshake starts with:
29        // - 0x16 (handshake record type)
30        // - 0x03 0x01, 0x03 0x02, 0x03 0x03, or 0x03 0x04 (TLS versions)
31        data[0] == 0x16 && data[1] == 0x03 && (data[2] >= 0x01 && data[2] <= 0x04)
32    }
33
34    /// Update TLS configuration for a domain
35    pub async fn update_tls_config(
36        &self,
37        domain: &str,
38    ) -> Result<bool, Box<dyn std::error::Error>> {
39        let ssl_managers = self.ssl_managers.read().await;
40
41        if let Some(ssl_manager) = ssl_managers.get(domain) {
42            // Try to get the certificate for this domain
43            if ssl_manager.has_certificate(domain).await {
44                log::info!("Certificate available for {domain}, creating TLS config");
45
46                // Get the certificate and create Rustls config
47                if let Ok(rustls_config) = ssl_manager.get_rustls_config(domain).await {
48                    let mut tls_configs = self.tls_configs.write().await;
49                    tls_configs.insert(domain.to_string(), Arc::new(rustls_config));
50
51                    log::info!("✅ TLS configuration updated for domain: {domain}");
52                    log::info!("🔒 HTTPS is now available for https://{domain}");
53                    return Ok(true);
54                }
55            }
56        }
57
58        Ok(false)
59    }
60
61    /// Check if TLS is available for a domain
62    pub async fn has_tls_config(&self, domain: &str) -> bool {
63        let tls_configs = self.tls_configs.read().await;
64        tls_configs.contains_key(domain)
65    }
66
67    /// Get TLS config for a domain
68    pub async fn get_tls_config(&self, domain: &str) -> Option<Arc<RustlsServerConfig>> {
69        let tls_configs = self.tls_configs.read().await;
70        tls_configs.get(domain).cloned()
71    }
72
73    /// Start background task to monitor for certificate updates
74    pub fn start_certificate_monitor(&self, domains: Vec<String>) {
75        let ssl_managers = self.ssl_managers.clone();
76        let handler = Self {
77            ssl_managers: ssl_managers.clone(),
78            tls_configs: self.tls_configs.clone(),
79        };
80
81        tokio::spawn(async move {
82            let mut interval = tokio::time::interval(std::time::Duration::from_secs(10));
83
84            loop {
85                interval.tick().await;
86
87                for domain in &domains {
88                    if !handler.has_tls_config(domain).await {
89                        match handler.update_tls_config(domain).await {
90                            Ok(true) => {
91                                log::info!("🎉 Dynamic HTTPS upgrade successful for {domain}");
92                                log::warn!("Note: Existing HTTP connections on port 443 will continue as HTTP");
93                                log::warn!("New connections will automatically use HTTPS");
94                            }
95                            Ok(false) => {
96                                // Certificate not ready yet, continue monitoring
97                            }
98                            Err(e) => {
99                                log::error!("Failed to update TLS config for {domain}: {e}");
100                            }
101                        }
102                    }
103                }
104            }
105        });
106    }
107}