use std::io::{Write, BufReader, BufRead}; use std::thread; use std::net::{TcpListener, TcpStream}; use std::fs; use std::string::{String}; use std::process::Command; use packeteer::http1::*; use packeteer::generate_kvheader; struct Resource { contents: String, status_code: i32, mime: String, } fn process_cgi(filename: String) -> String { // This is gonna be the boggiest implementation of CGI that anyone // has ever seen in the history of the fucking world let query; let script; if filename.contains("?") { let vars = filename.to_string().find("?").unwrap(); let vars1 = vars + 1; query = &filename[vars1..]; script = &filename[..vars]; } else { query = ""; script = &filename; } println!("{}", script); let output; if query != "" { output = Command::new(format!("./{}", script)) .env("GATEWAY_INTERFACE", "CGI/1.1") .env("SERVER_SOFTWARE", format!("herb/{}", env!("CARGO_PKG_VERSION"))) .env("REQUEST_METHOD", "GET") .env("QUERY_STRING", query) .env("SCRIPT_NAME", script) .output() .expect("failed to execute process"); } else { output = Command::new(format!("./{}", script)) .env("GATEWAY_INTERFACE", "CGI/1.1") .env("SERVER_SOFTWARE", format!("herb/{}", env!("CARGO_PKG_VERSION"))) .env("REQUEST_METHOD", "GET") .env("SCRIPT_NAME", script) .output() .expect("failed to execute process"); } let outputd = String::from_utf8_lossy(&output.stdout).to_string(); return outputd; } fn detect_media_type(filename: String) -> String { // The Lynx terminal browser made me do this. let path = filename.to_string(); let paths = check_if_path_exists(path); let path = filename.to_string(); let dir = check_if_dir(path); if paths == true && dir == false && filename.contains(".") == false { return "text/html".to_string(); } else { let ext_index = filename.to_string().find(".").unwrap(); let test = ext_index + 1; let ext = &filename[test..]; if filename[test..].contains("cgi") { return "text/html".to_string(); } match ext { "aac" => "audio/aac".to_string(), "avi" => "video/x-msvideo".to_string(), "bmp" => "image/bmp".to_string(), "bz2" => "application/x-bzip2".to_string(), "css" => "text/css".to_string(), "gz" => "application/gzip".to_string(), "gif" => "image/gif".to_string(), "png" => "image/png".to_string(), "pdf" => "application/pdf".to_string(), "jpeg" => "image/jpeg".to_string(), "jpg" => "image/jpeg".to_string(), "js" => "text/javascript".to_string(), "mid" => "audio/midi".to_string(), "midi" => "audio/midi".to_string(), "mp3" => "audio/mpeg".to_string(), "mp4" => "video/mp4".to_string(), "mpeg" => "video/mpeg".to_string(), "ogg" => "audio/ogg".to_string(), "oga" => "audio/ogg".to_string(), "ogv" => "video/ogg".to_string(), "opus" => "audio/opus".to_string(), "txt" => "text/plain".to_string(), "html" => "text/html".to_string(), "htm" => "text/html".to_string(), "cgi" => "text/html".to_string(), ".error_server_404" => "text/html".to_string(), ".error_server_501" => "text/html".to_string(), _ => "application/octet-stream".to_string(), } } } fn check_if_path_exists(path: String) -> bool { // This is probably not the best way of checking if a path // exists but I don't care :) let file = fs::metadata(path); match file { Ok(_) => true, Err(_) => false, } } fn check_if_dir(directory: String) -> bool { let path = directory + "/index.html"; let result = check_if_path_exists(path); return result; } fn generate_index(directory: String) -> String { let mut index = format!("
The resource you are trying to access cannot be read by the server.
".to_string(), } } } fn grab_time() -> String{ let output = Command::new("date") .arg("+'%a, %d %b %Y %H:%m:%S %Z'") .output() .expect("failed to execute process"); return String::from_utf8_lossy(&output.stdout).to_string().replace("'","").replace("\n",""); } fn process_request(request: VecThe resource you are trying to locate cannot be accessed!
".to_string(), status_code: 404, mime: "text/html".to_string() }; return resource; } else { let path = chars.as_str().to_string(); let dir = check_if_dir(path); if dir == true { let path = chars.as_str().to_string(); index += &path; index += "/index.html"; output = index.as_str().clone(); } else { output = chars.as_str(); } } } else { output = "index.html"; } } else { // It's get_page()'s problem now. let resource = Resource { contents: "The request sent by your web browser cannot be handled by this server software.
".to_string(), status_code: 501, mime: "text/html".to_string() }; println!("Stream sent unhandlable request."); return resource; } // Did you want to see chars.as_str().to_string()? let mut resource = Resource { contents: get_page(output.to_string()), status_code: 200, mime: detect_media_type(output.to_string()) }; if resource.contents.contains("500 Internal Server Error") { resource.status_code = 500; resource.mime = "text/html".to_string(); } return resource; } fn serve(mut stream: TcpStream) { thread::spawn(move || { println!("Stream thread created."); let mut request = Vec::new(); let mut reader = BufReader::new(&mut stream); reader .read_until(b'\n', &mut request) .expect("Failed to read from stream!"); let resource = process_request(request); // let contents = get_page(resource.clone()); // let header = "HTTP/1.1 200 OK\r\n"; // let content_type = format!("Content-Type: {}\r\n", mime); // let server = format!("Server: herb/{}\r\n", env!("CARGO_PKG_VERSION")); // let extra_fields; let mut response_constructed = generate_response(resource.status_code, &resource.contents); response_constructed.headers.push(generate_kvheader("Content-Type", &resource.mime)); response_constructed.headers.push(generate_kvheader("Server", &format!("herb/{}", env!("CARGO_PKG_VERSION")))); if cfg!(unix) { response_constructed.headers.push(generate_kvheader("Date", &grab_time())) } else { // I don't have a Windows or macOS box to test anything on // which means others are gonna have to deal with it :/ } println!("{:#?}", response_constructed); stream.write(unpack_response(response_constructed).as_bytes()).unwrap(); stream.flush().unwrap(); }); } fn main() -> std::io::Result<()> { let listen = TcpListener::bind("0.0.0.0:8080")?; for stream in listen.incoming() { println!("Serving incoming stream."); serve(stream?); } Ok(()) }