Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
edition = "2024"
edition = "2021"
name = "hteapot"
version = "0.5.0"
exclude = ["config.toml", "demo/", "readme.md"]
Expand All @@ -18,3 +18,7 @@ path = "src/hteapot/mod.rs"

[[bin]]
name = "hteapot"

[features]
default = ["cgi"]
cgi = []
22 changes: 21 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl Schema for TOMLSchema {
r
}
}

// Avoid removing the unused error, they're being used in different part of same file
pub fn toml_parser(content: &str) -> HashMap<String, TOMLSchema> {
let mut map = HashMap::new();
let mut submap = HashMap::new();
Expand Down Expand Up @@ -108,6 +108,7 @@ pub struct Config {
pub index: String, // Index file to serve by default
//pub error: String, // Error file to serve when a file is not found
pub proxy_rules: HashMap<String, String>,
pub cgi_rules: HashMap<String, String>,
}

impl Config {
Expand All @@ -134,6 +135,7 @@ impl Config {
cache: false,
cache_ttl: 0,
proxy_rules: HashMap::new(),
cgi_rules: HashMap::new(),
}
}

Expand All @@ -159,6 +161,23 @@ impl Config {
}
}

let mut cgi_rules: HashMap<String, String> = HashMap::new();
#[cfg(feature = "cgi")]
{
let cgi_map = map.get("cgi");
if cgi_map.is_some() {
let cgi_map = cgi_map.unwrap();
for k in cgi_map.keys() {
let command = cgi_map.get2(k);
if command.is_none() {
continue;
}
let command = command.unwrap();
cgi_rules.insert(k.clone(), command);
}
}
}

let map = map.get("HTEAPOT").unwrap();
Config {
port: map.get2("port").unwrap_or(8080),
Expand All @@ -171,6 +190,7 @@ impl Config {
log_file: map.get2("log_file"),
//error: map.get2("error").unwrap_or("error.html".to_string()),
proxy_rules,
cgi_rules,
}
}
}
79 changes: 79 additions & 0 deletions src/hteapot/cgi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use std::collections::HashMap;
use std::process::{Command, Output};
use std::io;
use std::io::Write;

/// Executes a CGI script with the given environment variables, arguments, and optional input.
/// - `script_path`: Path to the CGI script to execute.
/// - `env_vars`: A `HashMap` of environment variables to set for the script.
/// - `args`: A vector of arguments to pass to the script.
/// - `input`: Optional input data to provide to the script's standard input.
/// - `io::Result<Output>`: The output of the executed script, including stdout, stderr, and exit status.
pub fn execute_cgi(script_path: &str, env_vars: HashMap<String, String>, args: Vec<String>, input: Option<&[u8]>) -> io::Result<Output> {
let mut command = Command::new(script_path);

for (key, value) in env_vars {
command.env(key, value);
}

if !args.is_empty() {
command.args(&args);
}

if let Some(input_data) = input {
command.stdin(std::process::Stdio::piped());
command.stdout(std::process::Stdio::piped());
command.stderr(std::process::Stdio::piped());

let mut child = command.spawn()?;
if let Some(stdin) = child.stdin.as_mut() {
stdin.write_all(input_data)?;
}

child.wait_with_output()
} else {
command.output()
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;

/// Test case to verify successful execution of a CGI script.
/// This test uses a platform-specific command (`cmd.exe` on Windows, `sh` on Unix)
/// to simulate a CGI script that outputs "Hello, World!".
#[test]
fn test_execute_cgi_success() {
// Determine the script path and arguments based on the operating system
let script_path = if cfg!(windows) { "cmd.exe" } else { "sh" };
let mut env_vars = HashMap::new(); // Environment variables (empty in this test)
let args = if cfg!(windows) {
vec!["/C".to_string(), "echo".to_string(), "Hello, World!".to_string()]
} else {
vec!["-c".to_string(), "echo Hello, World!".to_string()]
};

let input = None;

let result = execute_cgi(script_path, env_vars, args, input);
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("Hello"), "Expected 'Hello' in output, got: {}", stdout);
}

/// Test case to verify failure when attempting to execute a non-existent CGI script.
#[test]
fn test_execute_cgi_failure() {
let script_path = "non_existent_script.cgi";
let env_vars = HashMap::new();
let args = vec![]; // No arguments
let input = None; // No input data

let result = execute_cgi(script_path, env_vars, args, input);
assert!(result.is_err()); // Ensure the execution failed
}
}
1 change: 1 addition & 0 deletions src/hteapot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod methods;
mod request;
mod response;
mod status;
pub mod cgi;

use self::response::EmptyHttpResponse;
use self::response::HttpResponseCommon;
Expand Down
Loading