use async_shutdown::ShutdownManager;
use std::net::SocketAddr;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
#[tokio::main]
async fn main() {
let shutdown = ShutdownManager::new();
tokio::spawn({
let shutdown = shutdown.clone();
async move {
if let Err(e) = tokio::signal::ctrl_c().await {
eprintln!("Failed to wait for CTRL+C: {}", e);
std::process::exit(1);
} else {
eprintln!("\nReceived interrupt signal. Shutting down server...");
shutdown.trigger_shutdown(0).ok();
}
}
});
match run_server(shutdown.clone(), "[::]:9372").await {
Ok(()) => {
shutdown.trigger_shutdown(0).ok();
},
Err(e) => {
eprintln!("Server task finished with an error: {}", e);
shutdown.trigger_shutdown(1).ok();
},
};
let exit_code = shutdown.wait_shutdown_complete().await;
std::process::exit(exit_code);
}
async fn run_server(shutdown: ShutdownManager<i32>, bind_address: &str) -> std::io::Result<()> {
let server = TcpListener::bind(&bind_address).await?;
eprintln!("Server listening on {}", bind_address);
while let Ok(connection) = shutdown.wrap_cancel(server.accept()).await {
let (stream, address) = connection?;
tokio::spawn(handle_client(shutdown.clone(), stream, address));
}
Ok(())
}
async fn handle_client(shutdown: ShutdownManager<i32>, mut stream: TcpStream, address: SocketAddr) {
eprintln!("Accepted new connection from {}", address);
let _delay_token = match shutdown.delay_shutdown_token() {
Ok(token) => token,
Err(_) => {
eprintln!("Shutdown already started, closing connection with {}", address);
return;
}
};
match shutdown.wrap_cancel(echo_loop(&mut stream)).await {
Ok(Err(e)) => eprintln!("Error in connection {}: {}", address, e),
Ok(Ok(())) => eprintln!("Connection closed by {}", address),
Err(_exit_code) => eprintln!("Shutdown triggered, closing connection with {}", address),
}
}
async fn echo_loop(stream: &mut TcpStream) -> std::io::Result<()> {
let mut buffer = vec![0; 512];
loop {
let read = stream.read(&mut buffer).await?;
if read == 0 {
break;
}
stream.write_all(&buffer[..read]).await?;
}
Ok(())
}