use std::{
cmp::min,
io::{Read, Write},
net::{TcpListener, TcpStream},
sync::{Arc, RwLock},
thread,
};
use args::Args;
use http::{HTTPRequest, HTTPResponse};
use router::Router;
pub mod args;
pub mod http;
pub mod router;
pub type HandlerFunction = fn(HTTPRequest, Arc<RwLock<Args>>) -> anyhow::Result<HTTPResponse>;
pub struct Server {
listener: TcpListener,
router: Arc<RwLock<Router>>,
args: Arc<RwLock<Args>>,
}
impl Server {
pub fn new(addr: &str, router: Router, args: Args) -> anyhow::Result<Self> {
let listener = TcpListener::bind(addr)?;
Ok(Server {
listener,
router: Arc::new(RwLock::new(router)),
args: Arc::new(RwLock::new(args)),
})
}
fn read_request(mut stream: &TcpStream) -> anyhow::Result<HTTPRequest> {
let mut buffer = [0; 4096];
let mut dim = 0;
loop {
let len = stream.read(&mut buffer)?;
dim += len;
if len < 4096 {
break;
}
}
let mut request: HTTPRequest = String::from_utf8_lossy(&buffer)[..dim]
.to_string()
.parse()?;
request.addr = stream.peer_addr()?.ip();
Ok(request)
}
fn write_response(mut stream: &TcpStream, mut response: HTTPResponse) -> anyhow::Result<()> {
response
.headers
.insert("Transfer-Encoding".to_string(), "chunked".to_string());
let body = response.body.clone();
stream.write_all(format!("{}\n\n", response.to_string()).as_bytes())?;
if let Some(bytes) = body {
let mut start = 0;
while start < bytes.len() {
let len = min(4096, bytes.len() - start);
stream.write_all(format!("{:X}\r\n", len).as_bytes())?;
stream.write_all(&bytes[start..start + len])?;
stream.write_all(b"\r\n")?;
start += len;
}
}
stream.write_all(b"0\r\n\r\n")?;
Ok(())
}
pub fn start(&mut self) -> anyhow::Result<()> {
log::info!("Server started on {}", self.listener.local_addr()?);
for stream in self.listener.incoming() {
let router = self.router.clone();
let args = self.args.clone();
match stream {
Ok(stream) => {
thread::spawn(move || -> anyhow::Result<()> {
let request = Server::read_request(&stream)?;
let response = match router.read() {
Ok(router) => match router.route(&request) {
Some(function) => function(request, args)?,
None => {
return Err(anyhow::anyhow!(
"Error: No associated functions to request -> {:#?}",
request
))
}
},
Err(err) => return Err(anyhow::anyhow!("Error: {}", err)),
};
Server::write_response(&stream, response)
});
}
Err(err) => return Err(anyhow::anyhow!("Error: {}", err)),
}
}
Ok(())
}
}