use crate::{http::Request, router::Router};
use anyhow::Result;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{TcpListener, TcpStream},
};
pub struct Server {
listener: TcpListener,
pub routes: Router,
}
impl Server {
pub async fn new() -> Result<Self> {
Self::with_port("3000").await
}
pub async fn with_port(port: &str) -> Result<Self> {
let listener = TcpListener::bind(format!("0.0.0.0:{port}")).await?;
println!("Server listening on port {port}");
Ok(Self {
listener,
routes: Router::new(),
})
}
pub async fn run(&self) {
loop {
let incoming = self.listener.accept().await;
match incoming {
Ok((mut stream, _)) => {
let router = self.routes.clone();
tokio::spawn(async move {
Self::handle_connection(&mut stream, router).await.unwrap();
});
}
Err(e) => {
println!("error: {e}");
}
}
}
}
async fn handle_connection(stream: &mut TcpStream, router: Router) -> Result<()> {
loop {
let mut buf = [0; 1024];
_ = stream.read(&mut buf).await?;
let Ok(request) = Request::try_from(&buf) else { break };
let response = router.handle(request);
stream.write_all(response.to_string().as_bytes()).await?;
stream.shutdown().await?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use serial_test::serial;
use super::*;
#[tokio::test]
#[serial]
async fn test_can_create_server() {
let _ = Server::new().await.unwrap();
}
#[tokio::test]
#[serial]
async fn test_can_create_server_with_port() {
let _ = Server::with_port("3001").await.unwrap();
}
#[tokio::test]
#[serial]
async fn test_can_run_server() {
let http = Server::with_port("4012").await.unwrap();
tokio::spawn(async move {
http.run().await;
});
let _ = TcpStream::connect("127.0.0.1:4012").await.unwrap();
}
}