0.7.0: heavily clean up CGI implementation and make it way more standards compliant and seamless with the rest of the code. fixes #5

This commit is contained in:
abbie 2024-01-15 14:47:25 +00:00
parent 87b79b5bd2
commit ce9600a56f
Signed by: threeoh6000
GPG key ID: 801FE4AD456E922C
4 changed files with 56 additions and 20 deletions

View file

@ -1 +1 @@
6 7

2
Cargo.lock generated
View file

@ -4,7 +4,7 @@ version = 3
[[package]] [[package]]
name = "herb" name = "herb"
version = "0.6.2" version = "0.7.0"
dependencies = [ dependencies = [
"packeteer", "packeteer",
] ]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "herb" name = "herb"
version = "0.6.2" version = "0.7.0"
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -12,14 +12,17 @@ struct Resource {
contents: Vec<u8>, contents: Vec<u8>,
status_code: i32, status_code: i32,
mime: String, mime: String,
iscgi: bool,
} }
struct GetPageResult { struct GetPageResult {
is500: bool, is500: bool,
is502: bool,
contents: Vec<u8>, contents: Vec<u8>,
iscgi: bool,
} }
fn process_cgi(filename: String) -> String { fn process_cgi(filename: String) -> Option<Vec<u8>> {
// This is gonna be the boggiest implementation of CGI that anyone // This is gonna be the boggiest implementation of CGI that anyone
// has ever seen in the history of the fucking world // has ever seen in the history of the fucking world
@ -44,19 +47,20 @@ fn process_cgi(filename: String) -> String {
.env("REQUEST_METHOD", "GET") .env("REQUEST_METHOD", "GET")
.env("QUERY_STRING", query) .env("QUERY_STRING", query)
.env("SCRIPT_NAME", script) .env("SCRIPT_NAME", script)
.output() .output();
.expect("failed to execute process");
} else { } else {
output = Command::new(format!("./{}", script)) output = Command::new(format!("./{}", script))
.env("GATEWAY_INTERFACE", "CGI/1.1") .env("GATEWAY_INTERFACE", "CGI/1.1")
.env("SERVER_SOFTWARE", format!("herb/{}", env!("CARGO_PKG_VERSION"))) .env("SERVER_SOFTWARE", format!("herb/{}", env!("CARGO_PKG_VERSION")))
.env("REQUEST_METHOD", "GET") .env("REQUEST_METHOD", "GET")
.env("SCRIPT_NAME", script) .env("SCRIPT_NAME", script)
.output() .output();
.expect("failed to execute process"); }
if output.is_ok() {
return Some(output.unwrap().stdout);
} else {
return None;
} }
let outputd = String::from_utf8_lossy(&output.stdout).to_string();
return outputd;
} }
fn detect_media_type(filename: String) -> String { fn detect_media_type(filename: String) -> String {
@ -149,7 +153,7 @@ fn get_page(filename: String) -> GetPageResult {
let checks = check_if_path_exists(path.clone()); let checks = check_if_path_exists(path.clone());
let index = check_if_dir(path.clone()); let index = check_if_dir(path.clone());
let mut resultstruct = GetPageResult { is500: false, contents: vec![] }; let mut resultstruct = GetPageResult { is500: false, is502: false, contents: vec![], iscgi: false };
println!("{} {} {}", path, checks, index); println!("{} {} {}", path, checks, index);
@ -160,15 +164,29 @@ fn get_page(filename: String) -> GetPageResult {
return resultstruct; return resultstruct;
} else if path.contains(".cgi?") { } else if path.contains(".cgi?") {
let result = process_cgi(filename); let result = process_cgi(filename);
resultstruct.contents = result.to_string().into_bytes(); if result.is_some() {
return resultstruct; resultstruct.contents = result.unwrap();
resultstruct.iscgi = true;
return resultstruct;
} else {
resultstruct.contents = "<!DOCTYPE HTML><html><body><h1>500 Internal Server Error</h1><p>The resource you are trying to access cannot be read by the server.</p></body></html>".to_string().into_bytes();
resultstruct.is500 = true;
return resultstruct;
}
} }
} }
if filename.contains(".cgi") { if filename.contains(".cgi") {
let result = process_cgi(filename); let result = process_cgi(filename);
resultstruct.contents = result.to_string().into_bytes(); if result.is_some() {
return resultstruct; resultstruct.contents = result.unwrap();
resultstruct.iscgi = true;
return resultstruct;
} else {
resultstruct.contents = "<!DOCTYPE HTML><html><body><h1>502 Bad Gateway</h1><p>The server is unable to complete your request due to an error.</p></body></html>".to_string().into_bytes();
resultstruct.is502 = true;
return resultstruct;
}
} else { } else {
let f = File::open(filename.clone()); let f = File::open(filename.clone());
if f.is_ok() { if f.is_ok() {
@ -178,8 +196,8 @@ fn get_page(filename: String) -> GetPageResult {
resultstruct.contents = buf; resultstruct.contents = buf;
return resultstruct; return resultstruct;
} else { } else {
resultstruct.contents = "<!DOCTYPE HTML><html><body><h1>500 Internal Server Error</h1><p>The resource you are trying to access cannot be read by the server.</p></body></html>".to_string().into_bytes(); resultstruct.contents = "<!DOCTYPE HTML><html><body><h1>502 Bad Gateway</h1><p>The server is unable to complete your request due to an error.</p></body></html>".to_string().into_bytes();
resultstruct.is500 = true; resultstruct.is502 = true;
return resultstruct; return resultstruct;
} }
} else { } else {
@ -222,7 +240,7 @@ fn process_request(request: Vec<u8>) -> Resource {
let exists = check_if_path_exists(path.clone()); let exists = check_if_path_exists(path.clone());
if exists == false && path.contains(".cgi?") == false { if exists == false && path.contains(".cgi?") == false {
let resource = Resource { contents: "<!DOCTYPE HTML><html><body><h1>404 Not Found</h1><p>The resource you are trying to locate cannot be accessed!</p></body></html>".to_string().into_bytes(), status_code: 404, mime: "text/html".to_string() }; let resource = Resource { contents: "<!DOCTYPE HTML><html><body><h1>404 Not Found</h1><p>The resource you are trying to locate cannot be accessed!</p></body></html>".to_string().into_bytes(), status_code: 404, mime: "text/html".to_string(), iscgi: false };
return resource; return resource;
} else { } else {
let dir = check_if_dir(path.clone()); let dir = check_if_dir(path.clone());
@ -238,18 +256,28 @@ fn process_request(request: Vec<u8>) -> Resource {
output = "index.html"; output = "index.html";
} }
} else { } else {
let resource = Resource { contents: "<!DOCTYPE HTML><html><body><h1>501 Not Implemented</h1><p>The request sent by your web browser cannot be handled by this server software.</p></body></html>".to_string().into_bytes(), status_code: 501, mime: "text/html".to_string() }; let resource = Resource { contents: "<!DOCTYPE HTML><html><body><h1>501 Not Implemented</h1><p>The request sent by your web browser cannot be handled by this server software.</p></body></html>".to_string().into_bytes(), status_code: 501, mime: "text/html".to_string(), iscgi: false };
println!("Stream sent unhandlable request."); println!("Stream sent unhandlable request.");
return resource; return resource;
} }
// Did you want to see chars.as_str().to_string()? // Did you want to see chars.as_str().to_string()?
let rescontents = get_page(output.to_string()); let rescontents = get_page(output.to_string());
let mut resource = Resource { contents: rescontents.contents, status_code: 200, mime: detect_media_type(output.split("?").collect::<Vec<&str>>()[0].to_string()) }; let mut resource = Resource { contents: rescontents.contents, status_code: 200, mime: detect_media_type(output.split("?").collect::<Vec<&str>>()[0].to_string()), iscgi: false };
if rescontents.is500 { if rescontents.is500 {
resource.status_code = 500; resource.status_code = 500;
resource.mime = "text/html".to_string(); resource.mime = "text/html".to_string();
} }
if rescontents.is502 {
resource.status_code = 502;
resource.mime = "text/html".to_string();
}
if rescontents.iscgi {
resource.iscgi = true;
}
return resource; return resource;
} }
@ -273,6 +301,14 @@ fn serve(mut stream: TcpStream) {
response_constructed.headers.push(generate_kvheader("Date", &grab_time())) response_constructed.headers.push(generate_kvheader("Date", &grab_time()))
} }
if resource.iscgi {
let mut header = "HTTP/1.1 200 OK\r\n".to_string().into_bytes();
stream.write(header.as_slice()).unwrap();
stream.write(&resource.contents).unwrap();
stream.flush().unwrap();
return;
}
let mut contents_clone = resource.contents.clone(); let mut contents_clone = resource.contents.clone();
let mut unpacked_response = unpack_response(response_constructed).into_bytes(); let mut unpacked_response = unpack_response(response_constructed).into_bytes();
unpacked_response.append(&mut contents_clone); unpacked_response.append(&mut contents_clone);