pub use async_std::task::block_on;
use libc::{accept, bind, c_char, c_int, c_void, close, in_addr, listen, recv, sa_family_t, send, setsockopt, sockaddr, sockaddr_in, socket, socklen_t, AF_INET, INADDR_ANY, SOCK_STREAM, SOL_SOCKET, SO_REUSEPORT, puts};
use std::ffi::{CStr, CString};
use std::fs::File;
use std::io::Read;
use std::mem::size_of;
#[macro_export]
macro_rules! rohanasan {
($($body:tt)*) => {
use $crate::block_on 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(async {
$($body)*
});
};
}
const STATIC_FOLDER: &str = "./static/";
pub const DEFAULT_HTML_HEADER: &str = "HTTP/1.1 200 OK\nContent-Type: text/html\n\n";
pub const DEFAULT_JSON_HEADER: &str = "HTTP/1.1 200 OK\nContent-Type: application/json\n\n";
pub const ERROR_403_HEADER: &str = "HTTP/1.1 403 Forbidden\nContent-Type: text/html\n\n";
pub const DEFAULT_PLAIN_TEXT_HEADER: &str = "HTTP/1.1 200 OK\nContent-Type: text/plain\n\n";
pub const DEFAULT_500_HEADER: &str = "HTTP/1.1 500 Internal Server Error\nContent-Type: text/html\n\n";
pub const ERROR_404_HEADER: &str = "HTTP/1.1 404 Not Found\nContent-Type: text/html\n\n";
pub const DEFAULT_301_HEADER: &str = "HTTP/1.1 301 Moved Permanently\nContent-Type: text/html\n\n";
pub const DEFAULT_400_HEADER: &str = "HTTP/1.1 400 Bad Request\nContent-Type: text/html\n\n";
pub const DEFAULT_401_HEADER: &str = "HTTP/1.1 401 Unauthorized\nContent-Type: text/html\n\n";
pub const DEFAULT_402_HEADER: &str = "HTTP/1.1 402 Payment Required\nContent-Type: text/html\n\n";
pub struct Request {
pub path: &'static str,
pub method: &'static str,
pub get_request: &'static str,
pub protocol: &'static str,
pub post_request: &'static str,
}
#[cfg(not(target_os = "linux"))]
pub fn init(port: u16) -> (i32, sockaddr_in, usize) {
let opt: c_int = 1;
let server_fd: c_int = unsafe { socket(AF_INET, SOCK_STREAM, 0) };
if server_fd == -1 {
panic!("Failed to create socket");
}
let address: sockaddr_in = sockaddr_in {
sin_family: AF_INET as sa_family_t,
sin_port: unsafe { htons(port) },
sin_addr: in_addr { s_addr: INADDR_ANY },
sin_zero: [0; 8],
sin_len: 1,
};
let addrlen: usize = size_of::<sockaddr_in>();
let res: c_int = unsafe {
setsockopt(
server_fd,
SOL_SOCKET,
SO_REUSEPORT,
&opt as *const i32 as *const c_void,
std::mem::size_of_val(&opt) as socklen_t,
)
};
if res == -1 {
panic!("Failed to set socket option");
}
(server_fd, address, addrlen)
}
#[cfg(target_os = "linux")]
pub fn init(port: u16) -> (i32, sockaddr_in, usize) {
let opt: i32 = 1;
let server_fd: i32 = unsafe { socket(AF_INET, SOCK_STREAM, 0) };
if server_fd == -1 {
panic!("Failed to create socket");
}
let address: sockaddr_in = sockaddr_in {
sin_family: AF_INET as sa_family_t,
sin_port: unsafe { htons(port) },
sin_addr: in_addr { s_addr: INADDR_ANY },
sin_zero: [0; 8],
};
let addrlen: usize = size_of::<sockaddr_in>();
let res: i32 = unsafe {
setsockopt(
server_fd,
SOL_SOCKET,
SO_REUSEADDR,
&opt as *const i32 as *const c_void,
size_of::<i32>() as socklen_t,
)
};
if res == -1 {
panic!("Failed to set socket option");
}
(server_fd, address, addrlen)
}
pub async fn serve<F>(args: (i32, sockaddr_in, usize), func: F)
where
F: Fn(Request) -> String + Send + Sync + 'static + Copy,
{
let (server_fd, address, addrlen) = args;
let if_bind: i32 = unsafe {
bind(
server_fd,
&address as *const _ as *const sockaddr,
addrlen as socklen_t,
)
};
if if_bind == -1 {
panic!("Failed to bind");
}
let if_listen = unsafe { listen(server_fd, 3) };
if if_listen == -1 {
panic!("Failed to listen");
}
loop {
let new_socket: i32 = unsafe {
accept(
server_fd,
&address as *const _ as *mut sockaddr,
&addrlen as *const _ as *mut socklen_t,
)
};
if new_socket == -1 {
continue;
}
async_std::task::spawn(async move {
let mut buf: [c_char; BUFFER_SIZE] = [0; BUFFER_SIZE];
unsafe {
recv(
new_socket,
buf.as_mut_ptr() as *mut c_void,
BUFFER_SIZE - 1,
0,
);
puts(buf.as_ptr());
}
let mut x = String::from_utf8(buf.iter().map(|i| *i as u8).collect::<Vec<u8>>());
match x {
Ok(x) => {
let tokens = x.leak().split_whitespace().collect::<Vec<&str>>();
let method = tokens[0];
let mut path: &str = "";
let mut get_request = "";
let mut post_request = "";
let mut protocol = "";
if tokens.len() > 2 {
path = tokens[1].split('?').collect::<Vec<&str>>()[0];
if path.ends_with('/') && path != "/" {
path = &path[0..path.len() - 1];
}
if tokens[1].split('?').collect::<Vec<&str>>().len() > 1 {
get_request = tokens[1].split('?').collect::<Vec<&str>>()[1];
} else {
get_request = "";
}
protocol = tokens[2];
if method == "POST" {
post_request = tokens[tokens.len() - 1];
}
}
let the_thing_we_need_to_give_to_func = Request {
path,
method,
get_request,
protocol,
post_request,
};
if path.starts_with("/static/") && path != "/static/" && path != "/static" {
let mut file_path = String::from(STATIC_FOLDER);
file_path.push_str(&path[8..]);
let file_path_cstr = CString::new(file_path).expect("Invalid file path");
serve_static_file(new_socket, file_path_cstr.as_ptr());
unsafe {
close(new_socket);
}
} else {
let response = func(the_thing_we_need_to_give_to_func);
unsafe {
send(
new_socket,
response.as_ptr() as *const c_void,
response.len(),
0,
); close(new_socket);
}
}
}
Err(_) => {
let response = String::from("URL format not in utf8 format");
unsafe {
send(
new_socket,
response.as_ptr() as *const c_void,
response.len(),
0,
); close(new_socket);
}
}
}
});
}
}
pub const BUFFER_SIZE: usize = 1024;
extern "C" {
fn htons(p0: u16) -> u16;
}
pub fn send_file(header: &str, file_path: &str) -> String {
let mut file = File::open(file_path).expect("Please enter the correct path to your html file");
let mut contents = String::new();
file.read_to_string(&mut contents)
.expect("File can't be read!");
send_http_response(header, &contents)
}
pub fn send_http_response(header: &str, body: &str) -> String {
let thing: String = header.to_string().clone() + body;
thing
}
fn serve_static_file(client_socket: c_int, file_path: *const c_char) {
let file_path_str = unsafe { CStr::from_ptr(file_path).to_string_lossy() };
let file_path = file_path_str.trim();
let mut file = match File::open(file_path) {
Ok(file) => file,
Err(_) => {
let not_found_response =
b"HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\n\r\n<h1>404 Not Found</h1>";
unsafe {
send(
client_socket,
not_found_response.as_ptr() as *const c_void,
not_found_response.len(),
0,
);
close(client_socket);
}
return;
}
};
let content_type = determine_content_type(file_path);
let response_header = format!("HTTP/1.1 200 OK\r\nContent-Type: {}\r\n\r\n", content_type);
let response_header_cstr =
CString::new(response_header.clone()).expect("Failed to create response header CString");
unsafe {
send(
client_socket,
response_header_cstr.as_ptr() as *const c_void,
response_header.len(),
0,
)
};
let mut buffer = [0; BUFFER_SIZE];
loop {
match file.read(&mut buffer) {
Ok(0) => break,
Ok(bytes_read) => {
unsafe {
send(
client_socket,
buffer.as_ptr() as *const c_void,
bytes_read,
0,
)
};
}
Err(_) => {
eprintln!("Error reading file");
break;
}
}
}
unsafe { close(client_socket) };
}
fn determine_content_type(file_path: &str) -> String {
match file_path.rsplit('.').next() {
Some("css") => "text/css".parse().unwrap(),
Some("txt") => "text/plain".parse().unwrap(),
Some("js") => "application/javascript".parse().unwrap(),
Some("png") => "image/png".parse().unwrap(),
Some("jpg") | Some("jpeg") => "image/jpeg".parse().unwrap(),
Some("gif") => "image/gif".parse().unwrap(),
Some("pdf") => "application/pdf".parse().unwrap(),
Some("htm") | Some("html") => "text/html".parse().unwrap(),
_ => "application/octet-stream".parse().unwrap(),
}
}
pub fn decode(x: &str) -> String {
urldecode::decode(x.to_string())
}