use std::collections::HashMap;
use std::io::{BufRead, BufReader, Lines, Read};
use std::net::{TcpListener, TcpStream};
use crate::request::Request;
use crate::response::Response;
use crate::router::Router;
pub type Handler = fn(&Request, &mut Response);
pub struct Server {
address: String,
router: Router,
}
impl Server {
pub fn new(port: &str) -> Server {
let localhost: &str = "127.0.0.1";
let address = format!("{}:{}", localhost, port);
Server {
address,
router: Router::new(),
}
}
pub fn get(&mut self, path: &str, handler: Handler) {
self.router.add_route("GET", path, handler);
}
pub fn post(&mut self, path: &str, handler: Handler) {
self.router.add_route("POST", path, handler);
}
pub fn put(&mut self, path: &str, handler: Handler) {
self.router.add_route("PUT", path, handler);
}
pub fn delete(&mut self, path: &str, handler: Handler) {
self.router.add_route("DELETE", path, handler);
}
pub fn patch(&mut self, path: &str, handler: Handler) {
self.router.add_route("PATCH", path, handler);
}
pub fn options(&mut self, path: &str, handler: Handler) {
self.router.add_route("OPTIONS", path, handler);
}
pub fn head(&mut self, path: &str, handler: Handler) {
self.router.add_route("HEAD", path, handler);
}
pub fn run(&self) {
let listener = TcpListener::bind(&self.address).expect("Failed to bind port");
println!("[rxpress] running on http://{} ⚙️", self.address);
for stream in listener.incoming() {
match stream {
Ok(stream) => self.handle_connection(stream),
Err(err) => eprintln!("Connection failed: {}", err),
}
}
}
pub fn address(&self) -> &str {
&self.address
}
fn handle_connection(&self, mut stream: TcpStream) {
let mut buf_reader = BufReader::new(&stream);
let mut lines = buf_reader.by_ref().lines();
let request_line = self.get_request_line(&mut lines);
let headers = self.get_headers(&mut lines);
let body = self.get_body(&headers, &mut buf_reader);
let mut req = Request::new(&request_line, headers, body);
let mut res = Response::new(&mut stream);
self.router.handle(&mut req, &mut res);
}
fn get_request_line(&self, lines: &mut Lines<&mut BufReader<&TcpStream>>) -> String {
lines.next().unwrap().unwrap()
}
fn get_headers(
&self,
lines: &mut Lines<&mut BufReader<&TcpStream>>,
) -> HashMap<String, String> {
let mut map: HashMap<String, String> = HashMap::new();
while let Some(Ok(line)) = lines.next() {
if line.is_empty() {
break;
}
if let Some((key, val)) = line.split_once(":") {
map.insert(
key.trim().to_string().to_ascii_lowercase(), val.trim().to_string(),
);
}
}
map
}
fn get_body(
&self,
headers: &HashMap<String, String>,
buf_reader: &mut BufReader<&TcpStream>,
) -> String {
let mut str = String::new();
if let Some(len) = headers.get("content-length") {
if let Ok(size) = len.parse::<usize>() {
let mut buffer = vec![0; size];
buf_reader.read_exact(&mut buffer).unwrap();
str = String::from_utf8_lossy(&buffer).to_string();
}
}
str
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_server_new() {
let server = Server::new("3000");
assert_eq!(server.address(), "127.0.0.1:3000");
}
}