0.7.2: Finish off CGI hopefully and add path resolving for PATH_STRING env

This commit is contained in:
abbie 2024-01-15 16:03:28 +00:00
parent 1656697a7c
commit 3fe3587236
Signed by: threeoh6000
GPG key ID: 801FE4AD456E922C
5 changed files with 91 additions and 3 deletions

2
Cargo.lock generated
View file

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

View file

@ -1,6 +1,6 @@
[package]
name = "herb"
version = "0.7.1"
version = "0.7.2"
edition = "2018"
[dependencies]

View file

@ -11,5 +11,8 @@ herb is able to send all types of data to clients, including text, video, audio,
* Supports text and raw data, including PNGs and MP3s
* Installable with curze
## Can herb run established CGI applications?
As of 0.7.2, herb's CGI implementation is fairly compliant and works with query strings _and_ paths. [Here's a screenshot of Cgit running under herb 0.7.2 displaying its own source code!](https://git.colean.cc/threeoh6000/herb/src/branch/master/cgit.png)
## Licensing
herb is licensed under [GNU General Public License version 3.](https://git.colean.cc/threeoh6000/herb/src/branch/master/LICENSE)

BIN
cgit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

View file

@ -6,7 +6,7 @@ use std::fs::File;
use std::string::{String};
use std::process::Command;
use packeteer::http1::*;
use packeteer::{generate_kvheader, unwrap_url_into_segments};
use packeteer::{generate_kvheader, unwrap_url_into_segments, Url};
struct Resource {
contents: Vec<u8>,
@ -63,6 +63,58 @@ fn process_cgi(filename: String) -> Option<Vec<u8>> {
}
}
fn process_cgi_with_path(filename: String, segments: Vec<String>, location: i32) -> Option<Vec<u8>> {
let query: String;
let script = filename;
if segments[segments.len()-1].contains("?") {
let vars = segments[segments.len()-1].to_string().find("?").unwrap();
query = segments[segments.len()-1][vars..].to_string();
} else {
query = "".to_string();
}
let mut i = 0;
let mut path = "".to_string();
for seg in segments {
if !(i <= location) {
if path == "".to_string() {
path = format!("/{}", seg);
} else {
path = format!("{}/{}", path, seg);
}
}
i = i + 1;
}
path = format!("{}/", path);
let output;
if query != "".to_string() {
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("PATH_INFO", path)
.env("SCRIPT_NAME", script)
.output();
} 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("PATH_INFO", path)
.env("SCRIPT_NAME", script)
.output();
}
if output.is_ok() {
return Some(output.unwrap().stdout);
} else {
return None;
}
}
fn detect_media_type(filename: String) -> String {
// The Lynx terminal browser made me do this.
@ -242,7 +294,40 @@ fn process_request(request: Vec<u8>) -> Resource {
println!("Stream sent GET request.");
if request.location.segments.len() != 0 {
let segclone = request.location.segments.clone();
path = unwrap_url_into_segments(request.location);
if path.contains(".cgi/") {
let mut cgiscript = 0;
let mut i = 0;
let mut cgipath: Vec<String> = vec![];
for segment in &segclone {
if segment.contains(".cgi") {
cgipath.push(segment.to_string());
cgiscript = i;
break;
} else {
cgipath.push(segment.to_string());
i = i + 1;
}
}
let mut cgipathraw = "".to_string();
for seg in cgipath {
if cgipathraw == "".to_string() {
cgipathraw = seg;
} else {
cgipathraw = format!("{}/{}", cgipathraw, seg);
}
}
let contents = process_cgi_with_path(cgipathraw, segclone, cgiscript);
if contents.is_some() {
let mut resource = Resource { contents: contents.unwrap(), status_code: 200, mime: "text/html".to_string(), iscgi: true };
return resource;
} else {
let newcontents = "<!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();
let mut resource = Resource { contents: newcontents, status_code: 502, mime: "text/html".to_string(), iscgi: false };
return resource;
}
}
path.remove(0);
let exists = check_if_path_exists(path.clone());