use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use std::thread;
use json::{array, JsonValue, object};
use crate::cryptos;
use crate::datetime::datetime;
use crate::datetime::timestamp::timestamp;
use crate::files::file::{file_content_get_stream, file_content_put, is_file, remove};
use crate::tools::string;
pub struct Cors {
allow_origin: String,
allow_methods: String,
allow_headers: String,
allow_credentials: bool,
max_age: i32,
}
impl Cors {
pub fn default() -> Self {
Self {
allow_origin: "*".to_string(),
allow_methods: "*".to_string(),
allow_headers: "*".to_string(),
allow_credentials: true,
max_age: 0,
}
}
pub fn allow_origin(mut self, origin: &str) -> Self {
self.allow_origin = origin.to_string();
self
}
pub fn allow_methods(mut self, methods: &str) -> Self {
self.allow_methods = methods.to_string();
self
}
pub fn allow_headers(mut self, headers: &str) -> Self {
self.allow_headers = headers.to_string();
self
}
pub fn allow_credentials(mut self, open: bool) -> Self {
self.allow_credentials = open;
self
}
pub fn max_age(mut self, int: i32) -> Self {
self.max_age = int;
self
}
}
struct Response {
stream: TcpStream,
public: String,
header: JsonValue,
get: JsonValue,
post: JsonValue,
files: JsonValue,
cors: Cors,
}
impl Response {
pub fn default(stream: TcpStream, cors: JsonValue, public: String) -> Self {
let cors = Cors::default()
.allow_origin(cors["allow_origin"].as_str().unwrap())
.allow_methods(cors["allow_methods"].as_str().unwrap())
.allow_headers(cors["allow_headers"].as_str().unwrap())
.allow_credentials(cors["allow_credentials"].as_bool().unwrap())
.max_age(cors["max_age"].as_i32().unwrap());
Self {
stream,
public,
header: object! {},
get: object! {},
post: object! {},
files: object! {},
cors,
}
}
pub fn handle(&mut self, fun: fn(response: JsonValue) -> (JsonValue, i32, String)) {
let mut buf: Vec<u8> = vec![];
let mut buffer = [0; 1024];
let count = self.stream.read(&mut buffer).unwrap();
buf = [buf, buffer[0..count].to_vec()].concat();
let data_length = buf.len();
self._header(buf.clone());
if self.header["method"].as_str().unwrap() == "OPTIONS" {
self._options();
return;
}
let file = format!("{}{}", self.public, self.header["uri"].as_str().unwrap());
if is_file(file.as_str()) {
self._file_html(file.as_str());
return;
}
let body_length = self.header["content-length"].as_usize().unwrap();
let header_length = self.header["header_length"].as_usize().unwrap();
let mut mun = data_length - header_length;
while mun < body_length {
let mut buffer = [0; 1024];
let count = self.stream.read(&mut buffer).unwrap();
mun += count;
buf = [buf, buffer[0..count].to_vec()].concat();
};
let body = buf[header_length..].to_owned();
self._body(body.clone());
let response = object! {
header:self.header.clone(),
post:self.post.clone(),
get:self.get.clone(),
files:self.files.clone()
};
let (contents, code, content_type) = fun(response);
self.write(contents, code, content_type);
for (_, files) in self.files.entries_mut() {
for item in files.members() {
if is_file(item["temp"].as_str().unwrap()) {
remove(item["temp"].as_str().unwrap());
}
}
}
}
fn _header(&mut self, data: Vec<u8>) {
let data = String::from_utf8(data.to_vec()).unwrap();
let list = string::split(data, "\r\n\r\n");
let data = list[0].to_string();
let headers = string::split(data.clone(), "\r\n");
let list = string::split(headers[0].to_string(), " ");
self.header["header_length"] = (data.len() + 4).into();
self.header["method"] = list[0].clone();
self.header["uri"] = list[1].clone();
self.header["version"] = list[2].clone();
self._query(self.header["uri"].to_string());
for header in headers.members() {
let list = string::split(header.to_string(), ": ");
if list.len() == 2 {
self.header[list[0].to_string().to_lowercase().as_str()] = list[1].clone();
continue;
}
}
self.header["content-type"] = self._content_type(self.header["content-type"].to_string(), self.header["accept"].to_string()).into();
self.header["content-length"] = self.header["content-length"].to_string().parse::<i32>().unwrap_or(0).into();
}
fn _body(&mut self, data: Vec<u8>) {
match self.header["content-type"].as_str().unwrap() {
"multipart/form-data" => {
self._multipart(data.clone());
}
"application/x-www-form-urlencoded" => {
let text = String::from_utf8(data.clone()).unwrap();
let text = string::split(text.to_string(), "&");
for item in text.members() {
let row = string::split(item.to_string(), "=");
self.post[row[0].clone().as_str().unwrap()] = row[1].clone();
}
}
"application/json" => {
let text = String::from_utf8(data).unwrap();
let length = self.header["content-length"].to_string().parse::<usize>().unwrap();
if length == 0 {
return;
}
self.post = json::parse(&*text.clone()).unwrap();
}
_ => {
}
}
}
fn _multipart(&mut self, data: Vec<u8>) {
let boundary = self.header["boundary"].as_str().unwrap();
let text = unsafe { String::from_utf8_unchecked(data.clone()) };
let text = text.trim_start_matches("\r\n");
let text = text.trim_end_matches(format!("\r\n--{}--\r\n", boundary.clone()).as_str()).to_string();
let fg = format!("--{}\r\n", boundary.clone());
let list = string::split(text.clone(), fg.as_str().clone());
let mut index = 0;
let mut body = data[2 + fg.len()..].to_vec();
while index < list.len() {
let item = list[index].to_string();
let item = item.trim_start_matches(boundary.clone());
let len = item.len();
if len == 0 {
index += 1;
continue;
}
let mode = string::search(item, "filename=");
match mode {
false => {
let row = string::split(item.to_string(), "\r\n\r\n");
let field = string::split(row[0].to_string(), "\"");
let name = field[1].as_str().unwrap();
let row = string::split(row[1].to_string(), "\r\n");
let value = string::split(row[0].to_string(), "\"");
let value = value[0].clone();
self.post[name] = value.clone();
}
true => {
let text = string::split(item.to_string(), "\r\n\r\n");
let body = text[1].to_string();
let file = body.as_str().trim_start_matches("\u{0}");
let text = text[0].clone();
let text = string::split(text.to_string(), "\r\n");
let name = string::split(text[0].to_string(), "\"");
let types = string::split(text[1].to_string(), ": ");
let types = types[1].as_str().unwrap();
let field = name[1].as_str().unwrap();
let filename = name[3].as_str().unwrap();
fn _content_type_mode(data: &str) -> String {
match data {
"image/png" => "png",
"image/jpeg" => "jpeg",
"image/jpg" => "jpg",
_ => "txt"
}.to_string().clone()
}
let mut file_data = object! {};
file_data["type"] = types.into();
file_data["name"] = filename.into();
file_data["size"] = file.len().into();
file_data["sha"] = cryptos::sha::to_sha1(file.clone()).into();
file_data["mode"] = json::JsonValue::String(_content_type_mode(types.clone()).clone());
let temp_file = format!("{}.{}", timestamp("ms").to_string(), file_data["mode"]);
let filename = format!("{}/../temp/{}", self.public, temp_file);
file_data["temp"] = filename.clone().into();
file_content_put(&*filename, file.clone());
if self.files[field].is_empty() {
self.files[field] = array![file_data];
} else {
self.files[field].push(file_data).unwrap();
}
}
}
index += 1;
if index == list.len() {} else {
body = body[len + fg.len()..].to_vec();
}
}
}
fn _content_type(&mut self, data: String, accept: String) -> String {
if string::search(data.to_lowercase().as_str(), "text/html") {
return "text/html".to_string();
}
if string::search(data.to_lowercase().as_str(), "text/xml") {
return "text/xml".to_string();
}
if string::search(data.to_lowercase().as_str(), "application/json") {
return "application/json".to_string();
}
if string::search(data.to_lowercase().as_str(), "multipart/form-data") {
let ttt = string::split(data, "boundary=");
self.header["boundary"] = ttt[1].clone();
return "multipart/form-data".to_string();
}
if string::search(data.to_lowercase().as_str(), "application/x-www-form-urlencoded") {
return "application/x-www-form-urlencoded".to_string();
}
if string::search(data.to_lowercase().as_str(), "text/plain") {
return "text/plain".to_string();
}
if data.is_empty() {
if string::search(accept.to_lowercase().as_str(), "image/*") {
return "image/*".to_string();
}
if string::search(accept.to_lowercase().as_str(), "image/webp") {
return "image/*".to_string();
}
if string::search(accept.to_lowercase().as_str(), "image/gif") {
return "image/gif".to_string();
}
}
return "text/plain".to_string();
}
fn _query(&mut self, data: String) {
let query = string::split(data, "?");
let query = query[1].clone();
if query.is_empty() {
return;
}
let query = string::split(query.to_string(), "&");
let mut list = object! {};
for item in query.members() {
let row = string::split(item.to_string(), "=");
let key = row[0].clone();
let value = row[1].clone();
list[key.as_str().unwrap()] = value.into();
}
self.get = list;
}
fn _file_html(&mut self, filename: &str) {
let name = string::split(filename.to_string(), "/");
let name = name[name.len() - 1].as_str().unwrap();
let sufxx = string::split(filename.to_string(), ".");
let sufxx = sufxx[sufxx.len() - 1].as_str().unwrap();
let contents = file_content_get_stream(filename);
let crlf = "\r\n";
let status = format!("HTTP/1.1 200 OK{}", crlf);
let date = format!("Date: {}{}", datetime::to_gmt(), crlf);
let server = format!("Server: Rust{}", crlf);
let content_length = format!("Content-Length: {}{}", contents.len(), crlf);
let content_disposition = match sufxx {
"html" => {
format!("")
}
_ => {
format!("Content-Disposition: attachment; filename={}{}", name, crlf)
}
};
let content_type = match sufxx {
"jpg" => {
format!("content-type:image/jpg{}", crlf)
}
"png" => {
format!("content-type:image/png{}", crlf)
}
"bmp" => {
format!("content-type:image/bmp{}", crlf)
}
"jpeg" => {
format!("content-type:image/jpeg{}", crlf)
}
"svg" => {
format!("content-type:image/svg{}", crlf)
}
"webp" => {
format!("content-type:image/webp{}", crlf)
}
"ico" => {
format!("content-type:image/ico{}", crlf)
}
"gif" => {
format!("content-type:image/gif{}", crlf)
}
_ => {
format!("content-type: text/plain;charset=utf-8{}", crlf)
}
};
let response = format!("{}{}{}{}{}{}{}{}", status, date, server, content_disposition, content_type, content_length, crlf, contents);
self.stream.write_all(response.as_bytes()).unwrap();
self.stream.flush().unwrap();
}
fn _options(&mut self) {
let crlf = "\r\n";
let status = format!("HTTP/1.1 200 OK{}", crlf);
let date = format!("Date: {}{}", datetime::to_gmt(), crlf);
let server = format!("Server: Rust{}", crlf);
let allow_origin = format!("Access-Control-Allow-Origin: {}{}", self.cors.allow_origin, crlf);
let allow_methods = format!("Access-Control-Allow-Methods: {}{}", self.cors.allow_methods, crlf);
let allow_headers = format!("Access-Control-Allow-Headers: {}{}", self.cors.allow_headers, crlf);
let max_age = format!("Access-Control-Max-Age: {}{}", self.cors.max_age, crlf);
let allow_credentials = format!("Access-Control-Allow-Credentials: {}{}", self.cors.allow_credentials, crlf);
let response = format!("{}{}{}{}{}{}{}{}{}",
status, date, server,
allow_origin,
allow_methods,
allow_credentials,
max_age,
allow_headers,
crlf);
self.stream.write(response.as_bytes()).unwrap();
self.stream.flush().unwrap();
}
fn write(&mut self, contents: JsonValue, code: i32, content_type: String) {
let crlf = "\r\n";
let status = format!("HTTP/1.1 {} {}{}", code, "OK", crlf);
let content_type = {
match content_type.as_str() {
"json" => {
format!("content-type: application/json;charset=utf-8{}", crlf)
}
_ => {
format!("content-type: text/html;charset=utf-8{}", crlf)
}
}
};
let access_control_allow_origin = format!("Access-Control-Allow-Origin: *{}", crlf);
let content_length = format!("Content-Length: {}{}", contents.to_string().as_bytes().len(), crlf);
let response = format!("{}{}{}{}{}{}", status, content_type, access_control_allow_origin, content_length, crlf, contents);
self.stream.write(response.as_bytes()).unwrap();
self.stream.flush().unwrap();
}
}
pub struct Web {
ip: String,
public: String,
}
impl Web {
pub fn bind(ip: &str, public: &str) -> Self {
Self {
ip: ip.to_string(),
public: public.to_string(),
}
}
pub fn run(&mut self, cors: JsonValue, handle: fn(response: JsonValue) -> (JsonValue, i32, String)) {
let listener = TcpListener::bind(self.ip.as_str()).unwrap();
for stream in listener.incoming() {
let cors = cors.clone();
let public = self.public.clone();
let stream = stream.unwrap();
thread::spawn(move || Response::default(stream, cors, public).handle(handle));
}
}
}