Initial commit

This commit is contained in:
Andrii Babarytskyi 2025-06-05 20:11:02 +02:00
commit 012ef02a8e
9 changed files with 1068 additions and 0 deletions

123
src/server.rs Normal file
View file

@ -0,0 +1,123 @@
use crate::http;
use crate::schemas;
use clap::Parser;
use http::{HttpError, HttpMethod, HttpRequest, parse_method};
use schemas::{Args, State};
use std::error::Error;
use std::sync::Mutex;
use std::thread;
use std::{
collections::HashMap,
io::{BufReader, prelude::*},
net::{TcpListener, TcpStream},
sync::Arc,
};
use std::{path::PathBuf, str::FromStr};
type AppState = Arc<Mutex<State>>;
fn parse_http_request(mut buf: BufReader<&TcpStream>) -> Result<HttpRequest, HttpError> {
let mut request_method: Option<HttpMethod> = None;
let mut uri: Option<PathBuf> = None;
let mut headers: Vec<String> = Vec::new();
let mut header_map: HashMap<String, String> = HashMap::new();
let mut method_line: String = String::new();
for (i, line_result) in buf.by_ref().lines().enumerate() {
let line = line_result.unwrap();
if i == 0 {
method_line.push_str(&line);
let http_args = method_line.split(" ").collect::<Vec<&str>>();
if http_args.len() == 3 {
match parse_method(http_args[0]) {
Ok(m) => request_method = Some(m),
Err(e) => return Err(e),
}
match PathBuf::from_str(http_args[1]) {
Ok(b) => uri = Some(b),
Err(_) => return Err(HttpError::InternalServerError),
}
} else {
return Err(HttpError::InternalServerError);
}
}
if line.is_empty() {
break;
}
headers.push(line.clone());
if let Some((key, value)) = line.split_once(": ") {
header_map.insert(key.to_string().to_lowercase(), value.to_string());
}
}
let conten_length = header_map
.get("content-length")
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(0);
println!("Content length: {conten_length:?}");
let mut body: Vec<u8> = vec![0 as u8; conten_length];
let res = match buf.read_exact(&mut body) {
Ok(_) => body,
Err(_) => return Err(HttpError::InternalServerError),
};
if uri.is_none() || request_method.is_none() {
return Err(HttpError::InternalServerError);
}
Ok(HttpRequest {
method: request_method.unwrap(),
uri: uri.unwrap(),
headers: header_map,
body: res,
})
}
fn dispatch_connection(mut stream: TcpStream, state: &AppState) -> Result<(), HttpError> {
loop {
let buf_reader = BufReader::new(&stream);
match parse_http_request(buf_reader) {
Ok(request) => {
println!("Request: {:?}", &request);
if request.uri == PathBuf::from("/healthcheck") {
} else {
}
let response = "HTTP/1.1 200 OK\r\nContent-length: 0\r\n\r\n";
stream.write_all(response.as_bytes()).unwrap();
}
Err(_) => {
println!("Closing connection due to error or client disconnect.");
break;
}
}
}
Ok(())
}
pub struct HttpServer {
listener: TcpListener,
}
impl HttpServer {
pub fn new(port: &u32) -> Self {
Self {
listener: TcpListener::bind(format!("[::]:{}", port)).unwrap(),
}
}
pub fn listen(&self, state: &AppState) {
println!("Listening on port 7878");
for stream in self.listener.incoming() {
let stream = stream.unwrap();
let _ = dispatch_connection(stream, &state);
}
}
}