Skip to main content

pwr_server/
listener.rs

1//! TLS listener and connection accept loop for pwr-server.
2//!
3//! Uses synchronous I/O wrapped in tokio::task::spawn_blocking to
4//! keep the handler code simple (Read + Write traits) while still
5//! running in the tokio async runtime.
6
7use std::fs;
8use std::io::BufReader;
9use std::net::TcpListener;
10use std::sync::Arc;
11
12use rustls::ServerConfig as TlsServerConfig;
13use rustls::pki_types::CertificateDer;
14
15use crate::auth::RateLimiter;
16use crate::config::ServerConfig;
17use crate::handler::{self, HandlerContext};
18use crate::storage::ProjectStorage;
19
20/// Build a rustls TLS server configuration from the cert and key files.
21fn build_tls_config(config: &ServerConfig) -> Result<TlsServerConfig, String> {
22    let cert_file = fs::File::open(&config.tls_cert_path)
23        .map_err(|e| format!("Cannot open TLS cert {}: {}", config.tls_cert_path.display(), e))?;
24    let mut cert_reader = BufReader::new(cert_file);
25    let certs: Vec<CertificateDer> = rustls_pemfile::certs(&mut cert_reader)
26        .collect::<Result<Vec<_>, _>>()
27        .map_err(|e| format!("Cannot parse TLS cert: {}", e))?;
28
29    if certs.is_empty() {
30        return Err("No certificates found in cert file".into());
31    }
32
33    let key_file = fs::File::open(&config.tls_key_path)
34        .map_err(|e| format!("Cannot open TLS key {}: {}", config.tls_key_path.display(), e))?;
35    let mut key_reader = BufReader::new(key_file);
36    let key = rustls_pemfile::private_key(&mut key_reader)
37        .map_err(|e| format!("Cannot parse TLS key: {}", e))?
38        .ok_or_else(|| "No private key found in key file".to_string())?;
39
40    let tls_config = TlsServerConfig::builder()
41        .with_no_client_auth()
42        .with_single_cert(certs, key)
43        .map_err(|e| format!("Cannot build TLS config: {}", e))?;
44
45    Ok(tls_config)
46}
47
48/// Run the server main loop. Blocks until shutdown.
49///
50/// Spawns one OS thread per connection (via spawn_blocking) to keep
51/// handler code synchronous while the tokio runtime drives I/O.
52pub fn run(config: ServerConfig) -> Result<(), String> {
53    let psk = pwr_core::crypto::psk_from_hex(&config.auth_token)
54        .map_err(|e| format!("Invalid auth token: {}", e))?;
55
56    let storage = ProjectStorage::new(config.clone())?;
57    let storage = Arc::new(std::sync::RwLock::new(storage));
58    let rate_limiter = Arc::new(std::sync::Mutex::new(RateLimiter::new()));
59
60    let tls_config = Arc::new(build_tls_config(&config)?);
61
62    let bind_addr = config.bind_addr();
63    let listener = TcpListener::bind(&bind_addr)
64        .map_err(|e| format!("Cannot bind to {}: {}", bind_addr, e))?;
65
66    log::info!("pwr-server listening on {} (TLS 1.3)", bind_addr);
67    log::info!("Storage: {}", config.storage_base_path.display());
68
69    // Accept connections in a loop
70    for stream_result in listener.incoming() {
71        let stream = stream_result.map_err(|e| format!("Accept error: {}", e))?;
72        let peer_addr = stream
73            .peer_addr()
74            .unwrap_or_else(|_| "unknown".parse().unwrap());
75
76        let psk = psk;
77        let storage = storage.clone();
78        let rate_limiter = rate_limiter.clone();
79        let tls_config = tls_config.clone();
80
81        // Handle each connection on a dedicated thread
82        std::thread::spawn(move || {
83            log::debug!("Connection from {}", peer_addr);
84
85            // Perform TLS handshake
86            let conn = match rustls::ServerConnection::new(tls_config.clone()) {
87                Ok(c) => c,
88                Err(e) => {
89                    log::error!("Cannot create TLS connection: {}", e);
90                    return;
91                }
92            };
93            let mut tls_stream = rustls::StreamOwned::new(conn, stream);
94
95            let ctx = HandlerContext {
96                storage: storage.clone(),
97                rate_limiter: rate_limiter.clone(),
98                psk,
99                peer_addr,
100                connected_at: std::time::Instant::now(),
101            };
102
103            if let Err(e) = handler::handle_connection(&mut tls_stream, ctx) {
104                log::error!("Handler error for {}: {}", peer_addr, e);
105            }
106        });
107    }
108
109    Ok(())
110}