use std::{net::SocketAddr, path::PathBuf, sync::Arc, time::Duration}; use lanspread_db::db::GameDB; use lanspread_proto::{Message as _, Request}; use lanspread_utils::maybe_addr; use s2n_quic::{Connection, Server, provider::limits::Limits, stream::BidirectionalStream}; use crate::req::{RequestHandler, send_game_file_data}; static KEY_PEM: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../key.pem")); static CERT_PEM: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../cert.pem")); #[derive(Clone, Debug)] struct ServerCtx { handler: RequestHandler, games_folder: PathBuf, } async fn handle_bidi_stream(stream: BidirectionalStream, ctx: Arc) -> eyre::Result<()> { let (mut rx, mut tx) = stream.split(); let remote_addr = maybe_addr!(rx.connection().remote_addr()); tracing::trace!("{remote_addr} stream opened"); // handle streams loop { match rx.receive().await { Ok(Some(data)) => { tracing::trace!( "{remote_addr} msg: (raw): {}", String::from_utf8_lossy(&data) ); let request = Request::decode(data); tracing::debug!("{remote_addr} msg: {request:?}"); // special case for now (send game file data to client) if let Request::GetGameFileData(game_file_desc) = &request { send_game_file_data(game_file_desc, &mut tx, &ctx.games_folder).await; continue; } // normal case (handle request) if let Err(e) = ctx .handler .handle_request(request, &ctx.games_folder, &mut tx) .await { tracing::error!(?e, "{remote_addr} error handling request"); } } Ok(None) => { tracing::trace!("{remote_addr} stream closed"); break; } Err(e) => { tracing::error!("{remote_addr} stream error: {e}"); break; } } } Ok(()) } async fn handle_connection(mut connection: Connection, ctx: Arc) -> eyre::Result<()> { let remote_addr = maybe_addr!(connection.remote_addr()); tracing::info!("{remote_addr} connected"); // handle streams while let Ok(Some(stream)) = connection.accept_bidirectional_stream().await { let ctx = ctx.clone(); let remote_addr = remote_addr.clone(); // spawn a new task for the stream tokio::spawn(async move { if let Err(e) = handle_bidi_stream(stream, ctx).await { tracing::error!("{remote_addr} stream error: {e}"); } }); } Ok(()) } pub(crate) async fn run_server( addr: SocketAddr, db: GameDB, games_folder: PathBuf, ) -> eyre::Result<()> { let limits = Limits::default() .with_max_handshake_duration(Duration::from_secs(3))? .with_max_idle_timeout(Duration::from_secs(3))?; let mut server = Server::builder() .with_tls((CERT_PEM, KEY_PEM))? .with_io(addr)? .with_limits(limits)? .start()?; let ctx = Arc::new(ServerCtx { handler: RequestHandler::new(db), games_folder, }); while let Some(connection) = server.accept().await { let ctx = ctx.clone(); // spawn a new task for the connection tokio::spawn(async move { if let Err(e) = handle_connection(connection, ctx).await { tracing::error!("Connection error: {}", e); } }); } tracing::info!("Server shutting down"); Ok(()) }