#![doc = include_str!("../README.md")]
mod constants;
mod priv_parse;
mod readers;
mod senders;
mod static_folder;
pub use constants::*;
use priv_parse::parse_headers;
use readers::read_the_request;
use senders::{send_invalid_utf8_error, send_static_folder_and_programmers_response};
use std::{net::SocketAddr, time::Duration};
pub use tokio::runtime::Builder;
use tokio::{
net::{TcpListener, TcpStream},
time::timeout,
};
#[macro_export]
macro_rules! rohanasan {
($($body:tt)*) => {
use $crate::Builder as why_will_someone_use_this_as_a_name_to_import_task_32194ilqrjf8da;
why_will_someone_use_this_as_a_name_to_import_task_32194ilqrjf8da::new_multi_thread().enable_all()
.build()
.unwrap()
.block_on(
$($body)*
)
};
}
pub struct Request {
pub method: &'static str,
pub path: &'static str,
pub get_request: &'static str,
pub keep_alive: bool,
pub protocol: &'static str,
request_was_correct: bool,
}
async fn handle_connection<F>(mut stream: TcpStream, func: F)
where
F: Fn(Request) -> String + Send + Copy,
{
let (buffer, n) = read_the_request(&mut stream).await;
if n == 0 {
return;
}
let request: Request = parse_headers(buffer, n);
if request.request_was_correct {
if request.keep_alive {
send_static_folder_and_programmers_response(request, &mut stream, func).await;
let mut counter = 0;
while counter < 20 {
counter += 1;
let request_result =
timeout(Duration::from_secs(10), read_the_request(&mut stream)).await;
if let Ok((buffer, n)) = request_result {
if n == 0 {
return; }
let request_inside_loop: Request = parse_headers(buffer, n);
let keep_alive = request_inside_loop.keep_alive;
if request_inside_loop.request_was_correct {
send_static_folder_and_programmers_response(
request_inside_loop,
&mut stream,
func,
)
.await;
if !keep_alive {
return;
}
} else {
send_invalid_utf8_error(&mut stream).await;
}
} else {
}
}
} else {
send_static_folder_and_programmers_response(request, &mut stream, func).await;
}
} else {
send_invalid_utf8_error(&mut stream).await;
}
}
pub async fn serve<F>(port: u16, func: F)
where
F: Fn(Request) -> String + Send + 'static + Copy,
{
let addr = SocketAddr::from(([0, 0, 0, 0], port));
let listener = TcpListener::bind(addr).await.expect("");
loop {
let (stream, _) = listener.accept().await.expect("");
tokio::spawn(handle_connection(stream, func));
}
}
pub fn send_http_response(header: &str, body: &str, req: Request) -> String {
if req.keep_alive {
format!(
"{}\r\nContent-Length:{}\r\nConnection:Keep-Alive\r\n\r\n{}",
header,
body.len(),
body
)
} else {
format!(
"{}\r\nContent-Length:{}\r\nConnection:Close\r\n\r\n{}",
header,
body.len(),
body
)
}
}
pub fn send_file(header: &str, file_path: &str, req: Request) -> String {
let contents = std::fs::read_to_string(file_path).expect("msg");
send_http_response(header, &contents, req)
}
pub fn send_file_top_bottom(
header: &str,
file_path: &str,
top: &str,
bottom: &str,
req: Request,
) -> String {
let mut contents: String = std::fs::read_to_string(file_path).expect("msg");
contents = contents.replace("{%INJECT_DATA TOP%}", top);
contents = contents.replace("{%INJECT_DATA BOTTOM%}", bottom);
send_http_response(header, &contents, req)
}
pub fn url_decode(encoded_string: &str) -> String {
urldecode::decode(encoded_string.to_string())
}