use std::ffi::{CStr, CString};
use std::fs::File;
use std::io::Read;
use std::mem::size_of;
pub use async_std::main as rohanasan;
use async_std::task;
use libc::{
accept, AF_INET, bind, c_char, c_int, c_void, close, in_addr, INADDR_ANY, listen, puts,
read, sa_family_t, setsockopt, SO_REUSEADDR, SOCK_STREAM, sockaddr, sockaddr_in, socket, socklen_t,
SOL_SOCKET, write,
};
use urldecode;
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: text/html\n\n";
pub const ERROR_404_HEADER: &str = "HTTP/1.1 404 Not Found\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: 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],
sin_len: 1,
};
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)
}
#[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) -> &'static str + 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;
}
task::spawn(async move {
let mut buf: [c_char; 1024] = [0; 1024]; unsafe {
read(new_socket, buf.as_mut_ptr() as *mut c_void, 1024);
puts(buf.as_ptr());
}
let x: String = String::from_utf8(buf.iter().map(|i| *i as u8).collect::<Vec<_>>())
.unwrap()
.clone();
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 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..]);
println!("{}", file_path);
let file_path_cstr = CString::new(file_path).expect("Invalid file path");
serve_static_file(new_socket, file_path_cstr.as_ptr())
} else {
let response = func(the_thing_we_need_to_give_to_func);
unsafe {
write(
new_socket,
response.as_ptr() as *const c_void,
response.len(),
); 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) -> &'static str {
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) -> &'static str {
let thing: String = header.to_string().clone() + body;
thing.leak() }
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 {
write(
client_socket,
not_found_response.as_ptr() as *const c_void,
not_found_response.len(),
);
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 {
write(
client_socket,
response_header_cstr.as_ptr() as *const c_void,
response_header.len(),
)
};
let mut buffer = [0; BUFFER_SIZE];
loop {
match file.read(&mut buffer) {
Ok(0) => break,
Ok(bytes_read) => {
unsafe { write(client_socket, buffer.as_ptr() as *const c_void, bytes_read) };
}
Err(_) => {
eprintln!("Error reading file");
break;
}
}
}
unsafe { close(client_socket) };
}
fn determine_content_type(file_path: &str) -> &str {
match file_path.rsplit('.').next() {
Some("css") => "text/css",
Some("txt") => "text/plain",
Some("js") => "application/javascript",
Some("png") => "image/png",
Some("jpg") | Some("jpeg") => "image/jpeg",
Some("gif") => "image/gif",
Some("pdf") => "application/pdf",
Some("htm") | Some("html") => "text/html",
_ => "application/octet-stream",
}
}
pub fn decode(x: &str) -> &'static str {
urldecode::decode(x.to_string()).leak() }