mod config; mod jwt; mod message; mod router; mod stores; use clap::Parser; use configparser::ini::Ini; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, net::{TcpListener, TcpStream}, time::{timeout, Duration}, }; use crate::router::ROUTER; use config::Config; #[derive(Parser)] #[clap(author, version, about, long_about = None)] struct Cli { /// config filepath (.ini) config: String, } #[tokio::main] async fn main() { simple_logger::init_with_level(log::Level::Info).unwrap(); let args = Cli::parse(); let mut config = Ini::new(); match config.load(args.config) { Ok(c) => c, Err(e) => { log::error!("error while loading the config file details={}", e); std::process::exit(1); } }; let server_url = config.get("server", "url").unwrap_or("".to_string()); let listener = { match TcpListener::bind(&server_url).await { Ok(t) => { log::info!("server is listening on '{}'", server_url); t } Err(e) => { log::error!("while initializing tcp listener details={}", e); std::process::exit(1); } } }; let router_config: Config = { match Config::try_from(config) { Ok(c) => c, Err(e) => { log::error!("unable to load the configuration details={}", e); std::process::exit(1); } } }; loop { let (stream, addr) = listener.accept().await.unwrap(); let conf = router_config.clone(); tokio::spawn(handle_connection(stream, addr.to_string(), conf.clone())); } } /// handle_connection parses the incoming request and builds an HTTP response async fn handle_connection(mut stream: TcpStream, addr: String, config: Config) { log::info!("client connected: {}", addr); let mut message = vec![]; let mut buffer: [u8; 1024] = [0; 1024]; let duration = Duration::from_millis(5); // loop until the message is read // the stream can be fragmented so, using a timeout (5ms should be far enough) for the future for completion // after the timeout, the message is "considered" as entirely read loop { match timeout(duration, stream.read(&mut buffer)).await { Ok(v) => { let n = v.unwrap(); message.extend_from_slice(&buffer[0..n]); } Err(_e) => break, } } let request_string = std::str::from_utf8(&message).unwrap(); let response = ROUTER.route(request_string, config).await; let response_str: String = response.into(); stream.write(response_str.as_bytes()).await.unwrap(); stream.flush().await.unwrap(); log::info!("connection closed: {}", addr); }