Replace custom logging with tracing crate and RUST_LOG env var
- Remove custom logging module and init_logging function - Update main.rs to use tracing_subscriber with EnvFilter - Remove log_level from global config structure - Update documentation and tests to use RUST_LOG - Format long lines in config.rs and test files for better readability
This commit is contained in:
parent
50a4d9bc75
commit
55fe47b172
15 changed files with 787 additions and 459 deletions
|
|
@ -7,8 +7,22 @@ pub fn generate_test_certificates_for_host(temp_dir: &Path, hostname: &str) {
|
|||
|
||||
// Generate self-signed certificate for testing
|
||||
// This is a simplified version - in production, use proper certificates
|
||||
std::fs::write(&cert_path, format!("-----BEGIN CERTIFICATE-----\nTest cert for {}\n-----END CERTIFICATE-----\n", hostname)).unwrap();
|
||||
std::fs::write(&key_path, format!("-----BEGIN PRIVATE KEY-----\nTest key for {}\n-----END PRIVATE KEY-----\n", hostname)).unwrap();
|
||||
std::fs::write(
|
||||
&cert_path,
|
||||
format!(
|
||||
"-----BEGIN CERTIFICATE-----\nTest cert for {}\n-----END CERTIFICATE-----\n",
|
||||
hostname
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(
|
||||
&key_path,
|
||||
format!(
|
||||
"-----BEGIN PRIVATE KEY-----\nTest key for {}\n-----END PRIVATE KEY-----\n",
|
||||
hostname
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
use tempfile::TempDir;
|
||||
|
||||
|
|
@ -42,12 +56,19 @@ fn generate_test_certificates(temp_dir: &Path) {
|
|||
// Use openssl to generate a test certificate
|
||||
let output = Command::new("openssl")
|
||||
.args(&[
|
||||
"req", "-x509", "-newkey", "rsa:2048",
|
||||
"-keyout", &key_path.to_string_lossy(),
|
||||
"-out", &cert_path.to_string_lossy(),
|
||||
"-days", "1",
|
||||
"req",
|
||||
"-x509",
|
||||
"-newkey",
|
||||
"rsa:2048",
|
||||
"-keyout",
|
||||
&key_path.to_string_lossy(),
|
||||
"-out",
|
||||
&cert_path.to_string_lossy(),
|
||||
"-days",
|
||||
"1",
|
||||
"-nodes",
|
||||
"-subj", "/CN=localhost"
|
||||
"-subj",
|
||||
"/CN=localhost",
|
||||
])
|
||||
.output();
|
||||
|
||||
|
|
@ -60,8 +81,3 @@ fn generate_test_certificates(temp_dir: &Path) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,87 +7,80 @@ fn test_missing_config_file() {
|
|||
let output = Command::new(env!("CARGO_BIN_EXE_pollux"))
|
||||
.arg("--config")
|
||||
.arg("nonexistent.toml")
|
||||
.env("RUST_LOG", "error")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert!(!output.status.success());
|
||||
let stderr = String::from_utf8(output.stderr).unwrap();
|
||||
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||
assert!(stderr.contains("Config file 'nonexistent.toml' not found"));
|
||||
assert!(stderr.contains("Create the config file with") || stderr.contains("Add at least one"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_host_sections() {
|
||||
let temp_dir = common::setup_test_environment();
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
let config_content = r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = 1965
|
||||
# No host sections defined
|
||||
"#;
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_pollux"))
|
||||
.arg("--config")
|
||||
.arg(&config_path)
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert!(!output.status.success());
|
||||
let stderr = String::from_utf8(output.stderr).unwrap();
|
||||
assert!(stderr.contains("No host configurations found"));
|
||||
assert!(stderr.contains("Add at least one [hostname] section"));
|
||||
assert!(stdout.contains("Create the config file with"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonexistent_root_directory() {
|
||||
let temp_dir = common::setup_test_environment();
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
|
||||
["example.com"]
|
||||
root = "/definitely/does/not/exist"
|
||||
cert = "{}"
|
||||
key = "{}"
|
||||
"#, temp_dir.path().join("cert.pem").display(), temp_dir.path().join("key.pem").display());
|
||||
"#,
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_pollux"))
|
||||
.arg("--config")
|
||||
.arg(&config_path)
|
||||
.env("RUST_LOG", "error")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert!(!output.status.success());
|
||||
let stderr = String::from_utf8(output.stderr).unwrap();
|
||||
assert!(stderr.contains("Error for host 'example.com': Root directory '/definitely/does/not/exist' does not exist"));
|
||||
assert!(stderr.contains("Create the directory and add your Gemini files (.gmi, .txt, images)"));
|
||||
assert!(stderr.contains("Failed to parse config file"));
|
||||
assert!(stderr.contains(
|
||||
"Error for host 'example.com': Root directory '/definitely/does/not/exist' does not exist"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_missing_certificate_file() {
|
||||
let temp_dir = common::setup_test_environment();
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
|
||||
["example.com"]
|
||||
root = "{}"
|
||||
cert = "/nonexistent/cert.pem"
|
||||
key = "{}"
|
||||
"#, temp_dir.path().join("content").display(), temp_dir.path().join("key.pem").display());
|
||||
"#,
|
||||
temp_dir.path().join("content").display(),
|
||||
temp_dir.path().join("key.pem").display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_pollux"))
|
||||
.arg("--config")
|
||||
.arg(&config_path)
|
||||
.env("RUST_LOG", "error")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert!(!output.status.success());
|
||||
let stderr = String::from_utf8(output.stderr).unwrap();
|
||||
assert!(stderr.contains("Error for host 'example.com': Certificate file '/nonexistent/cert.pem' does not exist"));
|
||||
assert!(stderr.contains(
|
||||
"Error for host 'example.com': Certificate file '/nonexistent/cert.pem' does not exist"
|
||||
));
|
||||
assert!(stderr.contains("Generate or obtain TLS certificates for your domain"));
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +89,8 @@ fn test_valid_config_startup() {
|
|||
let temp_dir = common::setup_test_environment();
|
||||
let port = 1967 + (std::process::id() % 1000) as u16;
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
|
||||
|
|
@ -104,7 +98,12 @@ port = {}
|
|||
root = "{}"
|
||||
cert = "{}"
|
||||
key = "{}"
|
||||
"#, port, temp_dir.path().join("content").display(), temp_dir.path().join("cert.pem").display(), temp_dir.path().join("key.pem").display());
|
||||
"#,
|
||||
port,
|
||||
temp_dir.path().join("content").display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
let mut server_process = Command::new(env!("CARGO_BIN_EXE_pollux"))
|
||||
|
|
@ -117,7 +116,10 @@ key = "{}"
|
|||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
|
||||
// Check server is still running (didn't exit with error)
|
||||
assert!(server_process.try_wait().unwrap().is_none(), "Server should still be running with valid config");
|
||||
assert!(
|
||||
server_process.try_wait().unwrap().is_none(),
|
||||
"Server should still be running with valid config"
|
||||
);
|
||||
|
||||
// Kill server
|
||||
server_process.kill().unwrap();
|
||||
|
|
@ -141,24 +143,38 @@ fn test_valid_multiple_hosts_startup() {
|
|||
// Generate certificate for host1
|
||||
let cert_result1 = std::process::Command::new("openssl")
|
||||
.args(&[
|
||||
"req", "-x509", "-newkey", "rsa:2048",
|
||||
"-keyout", &key1_path.to_string_lossy(),
|
||||
"-out", &cert1_path.to_string_lossy(),
|
||||
"-days", "1",
|
||||
"req",
|
||||
"-x509",
|
||||
"-newkey",
|
||||
"rsa:2048",
|
||||
"-keyout",
|
||||
&key1_path.to_string_lossy(),
|
||||
"-out",
|
||||
&cert1_path.to_string_lossy(),
|
||||
"-days",
|
||||
"1",
|
||||
"-nodes",
|
||||
"-subj", "/CN=host1.com"
|
||||
"-subj",
|
||||
"/CN=host1.com",
|
||||
])
|
||||
.output();
|
||||
|
||||
// Generate certificate for host2
|
||||
let cert_result2 = std::process::Command::new("openssl")
|
||||
.args(&[
|
||||
"req", "-x509", "-newkey", "rsa:2048",
|
||||
"-keyout", &key2_path.to_string_lossy(),
|
||||
"-out", &cert2_path.to_string_lossy(),
|
||||
"-days", "1",
|
||||
"req",
|
||||
"-x509",
|
||||
"-newkey",
|
||||
"rsa:2048",
|
||||
"-keyout",
|
||||
&key2_path.to_string_lossy(),
|
||||
"-out",
|
||||
&cert2_path.to_string_lossy(),
|
||||
"-days",
|
||||
"1",
|
||||
"-nodes",
|
||||
"-subj", "/CN=host2.com"
|
||||
"-subj",
|
||||
"/CN=host2.com",
|
||||
])
|
||||
.output();
|
||||
|
||||
|
|
@ -167,7 +183,8 @@ fn test_valid_multiple_hosts_startup() {
|
|||
}
|
||||
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
|
||||
|
|
@ -181,13 +198,14 @@ root = "{}"
|
|||
cert = "{}"
|
||||
key = "{}"
|
||||
"#,
|
||||
port,
|
||||
temp_dir.path().join("host1").display(),
|
||||
cert1_path.display(),
|
||||
key1_path.display(),
|
||||
temp_dir.path().join("host2").display(),
|
||||
cert2_path.display(),
|
||||
key2_path.display());
|
||||
port,
|
||||
temp_dir.path().join("host1").display(),
|
||||
cert1_path.display(),
|
||||
key1_path.display(),
|
||||
temp_dir.path().join("host2").display(),
|
||||
cert2_path.display(),
|
||||
key2_path.display()
|
||||
);
|
||||
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
|
|
@ -201,7 +219,10 @@ key = "{}"
|
|||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
|
||||
// Check server is still running (didn't exit with error)
|
||||
assert!(server_process.try_wait().unwrap().is_none(), "Server should start with valid multiple host config");
|
||||
assert!(
|
||||
server_process.try_wait().unwrap().is_none(),
|
||||
"Server should start with valid multiple host config"
|
||||
);
|
||||
|
||||
// Kill server
|
||||
server_process.kill().unwrap();
|
||||
|
|
@ -222,12 +243,19 @@ fn test_multiple_hosts_missing_certificate() {
|
|||
|
||||
let cert_result = std::process::Command::new("openssl")
|
||||
.args(&[
|
||||
"req", "-x509", "-newkey", "rsa:2048",
|
||||
"-keyout", &key1_path.to_string_lossy(),
|
||||
"-out", &cert1_path.to_string_lossy(),
|
||||
"-days", "1",
|
||||
"req",
|
||||
"-x509",
|
||||
"-newkey",
|
||||
"rsa:2048",
|
||||
"-keyout",
|
||||
&key1_path.to_string_lossy(),
|
||||
"-out",
|
||||
&cert1_path.to_string_lossy(),
|
||||
"-days",
|
||||
"1",
|
||||
"-nodes",
|
||||
"-subj", "/CN=host1.com"
|
||||
"-subj",
|
||||
"/CN=host1.com",
|
||||
])
|
||||
.output();
|
||||
|
||||
|
|
@ -235,7 +263,8 @@ fn test_multiple_hosts_missing_certificate() {
|
|||
panic!("Failed to generate test certificate");
|
||||
}
|
||||
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
|
||||
["host1.com"]
|
||||
|
|
@ -248,22 +277,26 @@ root = "{}"
|
|||
cert = "/nonexistent/cert.pem"
|
||||
key = "/nonexistent/key.pem"
|
||||
"#,
|
||||
temp_dir.path().join("host1").display(),
|
||||
cert1_path.display(),
|
||||
key1_path.display(),
|
||||
temp_dir.path().join("host2").display());
|
||||
temp_dir.path().join("host1").display(),
|
||||
cert1_path.display(),
|
||||
key1_path.display(),
|
||||
temp_dir.path().join("host2").display()
|
||||
);
|
||||
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_pollux"))
|
||||
.arg("--config")
|
||||
.arg(&config_path)
|
||||
.env("RUST_LOG", "error")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert!(!output.status.success());
|
||||
let stderr = String::from_utf8(output.stderr).unwrap();
|
||||
assert!(stderr.contains("Error for host 'host2.com': Certificate file '/nonexistent/cert.pem' does not exist"));
|
||||
assert!(stderr.contains(
|
||||
"Error for host 'host2.com': Certificate file '/nonexistent/cert.pem' does not exist"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -284,24 +317,38 @@ fn test_multiple_hosts_invalid_hostname() {
|
|||
// Generate certificate for valid host
|
||||
let cert_result1 = std::process::Command::new("openssl")
|
||||
.args(&[
|
||||
"req", "-x509", "-newkey", "rsa:2048",
|
||||
"-keyout", &key1_path.to_string_lossy(),
|
||||
"-out", &cert1_path.to_string_lossy(),
|
||||
"-days", "1",
|
||||
"req",
|
||||
"-x509",
|
||||
"-newkey",
|
||||
"rsa:2048",
|
||||
"-keyout",
|
||||
&key1_path.to_string_lossy(),
|
||||
"-out",
|
||||
&cert1_path.to_string_lossy(),
|
||||
"-days",
|
||||
"1",
|
||||
"-nodes",
|
||||
"-subj", "/CN=valid.com"
|
||||
"-subj",
|
||||
"/CN=valid.com",
|
||||
])
|
||||
.output();
|
||||
|
||||
// Generate certificate for invalid host (hostname validation happens before cert validation)
|
||||
let cert_result2 = std::process::Command::new("openssl")
|
||||
.args(&[
|
||||
"req", "-x509", "-newkey", "rsa:2048",
|
||||
"-keyout", &key2_path.to_string_lossy(),
|
||||
"-out", &cert2_path.to_string_lossy(),
|
||||
"-days", "1",
|
||||
"req",
|
||||
"-x509",
|
||||
"-newkey",
|
||||
"rsa:2048",
|
||||
"-keyout",
|
||||
&key2_path.to_string_lossy(),
|
||||
"-out",
|
||||
&cert2_path.to_string_lossy(),
|
||||
"-days",
|
||||
"1",
|
||||
"-nodes",
|
||||
"-subj", "/CN=invalid.com"
|
||||
"-subj",
|
||||
"/CN=invalid.com",
|
||||
])
|
||||
.output();
|
||||
|
||||
|
|
@ -309,7 +356,8 @@ fn test_multiple_hosts_invalid_hostname() {
|
|||
panic!("Failed to generate test certificates");
|
||||
}
|
||||
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
|
||||
["valid.com"]
|
||||
|
|
@ -322,22 +370,24 @@ root = "{}"
|
|||
cert = "{}"
|
||||
key = "{}"
|
||||
"#,
|
||||
temp_dir.path().join("validhost").display(),
|
||||
cert1_path.display(),
|
||||
key1_path.display(),
|
||||
temp_dir.path().join("invalidhost").display(),
|
||||
cert2_path.display(),
|
||||
key2_path.display());
|
||||
temp_dir.path().join("validhost").display(),
|
||||
cert1_path.display(),
|
||||
key1_path.display(),
|
||||
temp_dir.path().join("invalidhost").display(),
|
||||
cert2_path.display(),
|
||||
key2_path.display()
|
||||
);
|
||||
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_pollux"))
|
||||
.arg("--config")
|
||||
.arg(&config_path)
|
||||
.env("RUST_LOG", "error")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert!(!output.status.success());
|
||||
let stderr = String::from_utf8(output.stderr).unwrap();
|
||||
assert!(stderr.contains("Invalid hostname 'bad..host.com'. Hostnames must be valid DNS names."));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ fn test_rate_limiting_with_concurrent_requests() {
|
|||
let cert_path = temp_dir.path().join("cert.pem");
|
||||
let key_path = temp_dir.path().join("key.pem");
|
||||
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
max_concurrent_requests = 1
|
||||
|
|
@ -22,7 +23,12 @@ max_concurrent_requests = 1
|
|||
root = "{}"
|
||||
cert = "{}"
|
||||
key = "{}"
|
||||
"#, port, root_dir.display(), cert_path.display(), key_path.display());
|
||||
"#,
|
||||
port,
|
||||
root_dir.display(),
|
||||
cert_path.display(),
|
||||
key_path.display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
// Start server binary with test delay to simulate processing time
|
||||
|
|
@ -30,7 +36,7 @@ key = "{}"
|
|||
.arg("--config")
|
||||
.arg(&config_path)
|
||||
.arg("--test-processing-delay")
|
||||
.arg("3") // 3 second delay per request
|
||||
.arg("3") // 3 second delay per request
|
||||
.spawn()
|
||||
.expect("Failed to start server");
|
||||
|
||||
|
|
@ -68,13 +74,30 @@ key = "{}"
|
|||
let rate_limited_count = results.iter().filter(|r| r.starts_with("41")).count();
|
||||
|
||||
// Debug output
|
||||
println!("Results: {:?}", results);
|
||||
println!("Success: {}, Rate limited: {}", success_count, rate_limited_count);
|
||||
tracing::debug!("Test results: {:?}", results);
|
||||
tracing::debug!(
|
||||
"Success: {}, Rate limited: {}",
|
||||
success_count,
|
||||
rate_limited_count
|
||||
);
|
||||
|
||||
// Strict validation - rate limiting must work deterministically with delay
|
||||
assert_eq!(success_count, 1, "Expected exactly 1 successful request with limit=1, got {}. Results: {:?}", success_count, results);
|
||||
assert_eq!(rate_limited_count, 4, "Expected exactly 4 rate limited requests with limit=1, got {}. Results: {:?}", rate_limited_count, results);
|
||||
assert_eq!(
|
||||
success_count, 1,
|
||||
"Expected exactly 1 successful request with limit=1, got {}. Results: {:?}",
|
||||
success_count, results
|
||||
);
|
||||
assert_eq!(
|
||||
rate_limited_count, 4,
|
||||
"Expected exactly 4 rate limited requests with limit=1, got {}. Results: {:?}",
|
||||
rate_limited_count, results
|
||||
);
|
||||
|
||||
// Verify all requests received valid responses
|
||||
assert_eq!(success_count + rate_limited_count, 5, "All 5 requests should receive responses. Results: {:?}", results);
|
||||
}
|
||||
assert_eq!(
|
||||
success_count + rate_limited_count,
|
||||
5,
|
||||
"All 5 requests should receive responses. Results: {:?}",
|
||||
results
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,19 @@ fn test_single_host_config() {
|
|||
|
||||
let cert_result = Command::new("openssl")
|
||||
.args(&[
|
||||
"req", "-x509", "-newkey", "rsa:2048",
|
||||
"-keyout", &key_path.to_string_lossy(),
|
||||
"-out", &cert_path.to_string_lossy(),
|
||||
"-days", "1",
|
||||
"req",
|
||||
"-x509",
|
||||
"-newkey",
|
||||
"rsa:2048",
|
||||
"-keyout",
|
||||
&key_path.to_string_lossy(),
|
||||
"-out",
|
||||
&cert_path.to_string_lossy(),
|
||||
"-days",
|
||||
"1",
|
||||
"-nodes",
|
||||
"-subj", "/CN=example.com"
|
||||
"-subj",
|
||||
"/CN=example.com",
|
||||
])
|
||||
.output();
|
||||
|
||||
|
|
@ -30,7 +37,8 @@ fn test_single_host_config() {
|
|||
panic!("Failed to generate test certificates for config test");
|
||||
}
|
||||
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
|
||||
|
|
@ -38,7 +46,12 @@ port = {}
|
|||
root = "{}"
|
||||
cert = "{}"
|
||||
key = "{}"
|
||||
"#, port, content_dir.display(), cert_path.display(), key_path.display());
|
||||
"#,
|
||||
port,
|
||||
content_dir.display(),
|
||||
cert_path.display(),
|
||||
key_path.display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
let mut server_process = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
||||
|
|
@ -48,7 +61,10 @@ key = "{}"
|
|||
.unwrap();
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
assert!(server_process.try_wait().unwrap().is_none(), "Server should start with valid single host config");
|
||||
assert!(
|
||||
server_process.try_wait().unwrap().is_none(),
|
||||
"Server should start with valid single host config"
|
||||
);
|
||||
server_process.kill().unwrap();
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +72,8 @@ key = "{}"
|
|||
fn test_multiple_hosts_config() {
|
||||
let temp_dir = common::setup_test_environment();
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
[site1.com]
|
||||
root = "{}"
|
||||
cert = "{}"
|
||||
|
|
@ -69,12 +86,14 @@ key = "{}"
|
|||
|
||||
bind_host = "127.0.0.1"
|
||||
port = 1965
|
||||
"#, temp_dir.path().join("site1").display(),
|
||||
temp_dir.path().join("site1_cert.pem").display(),
|
||||
temp_dir.path().join("site1_key.pem").display(),
|
||||
temp_dir.path().join("site2").display(),
|
||||
temp_dir.path().join("site2_cert.pem").display(),
|
||||
temp_dir.path().join("site2_key.pem").display());
|
||||
"#,
|
||||
temp_dir.path().join("site1").display(),
|
||||
temp_dir.path().join("site1_cert.pem").display(),
|
||||
temp_dir.path().join("site1_key.pem").display(),
|
||||
temp_dir.path().join("site2").display(),
|
||||
temp_dir.path().join("site2_cert.pem").display(),
|
||||
temp_dir.path().join("site2_key.pem").display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
// Create additional directories and generate certificates
|
||||
|
|
@ -87,24 +106,38 @@ port = 1965
|
|||
// Site 1 certificate
|
||||
let cert_result1 = Command::new("openssl")
|
||||
.args(&[
|
||||
"req", "-x509", "-newkey", "rsa:2048",
|
||||
"-keyout", &temp_dir.path().join("site1_key.pem").to_string_lossy(),
|
||||
"-out", &temp_dir.path().join("site1_cert.pem").to_string_lossy(),
|
||||
"-days", "1",
|
||||
"req",
|
||||
"-x509",
|
||||
"-newkey",
|
||||
"rsa:2048",
|
||||
"-keyout",
|
||||
&temp_dir.path().join("site1_key.pem").to_string_lossy(),
|
||||
"-out",
|
||||
&temp_dir.path().join("site1_cert.pem").to_string_lossy(),
|
||||
"-days",
|
||||
"1",
|
||||
"-nodes",
|
||||
"-subj", "/CN=site1.com"
|
||||
"-subj",
|
||||
"/CN=site1.com",
|
||||
])
|
||||
.output();
|
||||
|
||||
// Site 2 certificate
|
||||
let cert_result2 = Command::new("openssl")
|
||||
.args(&[
|
||||
"req", "-x509", "-newkey", "rsa:2048",
|
||||
"-keyout", &temp_dir.path().join("site2_key.pem").to_string_lossy(),
|
||||
"-out", &temp_dir.path().join("site2_cert.pem").to_string_lossy(),
|
||||
"-days", "1",
|
||||
"req",
|
||||
"-x509",
|
||||
"-newkey",
|
||||
"rsa:2048",
|
||||
"-keyout",
|
||||
&temp_dir.path().join("site2_key.pem").to_string_lossy(),
|
||||
"-out",
|
||||
&temp_dir.path().join("site2_cert.pem").to_string_lossy(),
|
||||
"-days",
|
||||
"1",
|
||||
"-nodes",
|
||||
"-subj", "/CN=site2.org"
|
||||
"-subj",
|
||||
"/CN=site2.org",
|
||||
])
|
||||
.output();
|
||||
|
||||
|
|
@ -114,7 +147,8 @@ port = 1965
|
|||
|
||||
// Test server starts successfully with multiple host config
|
||||
let port = 1968 + (std::process::id() % 1000) as u16;
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
|
||||
|
|
@ -128,13 +162,14 @@ root = "{}"
|
|||
cert = "{}"
|
||||
key = "{}"
|
||||
"#,
|
||||
port,
|
||||
temp_dir.path().join("site1").display(),
|
||||
temp_dir.path().join("site1_cert.pem").display(),
|
||||
temp_dir.path().join("site1_key.pem").display(),
|
||||
temp_dir.path().join("site2").display(),
|
||||
temp_dir.path().join("site2_cert.pem").display(),
|
||||
temp_dir.path().join("site2_key.pem").display());
|
||||
port,
|
||||
temp_dir.path().join("site1").display(),
|
||||
temp_dir.path().join("site1_cert.pem").display(),
|
||||
temp_dir.path().join("site1_key.pem").display(),
|
||||
temp_dir.path().join("site2").display(),
|
||||
temp_dir.path().join("site2_cert.pem").display(),
|
||||
temp_dir.path().join("site2_key.pem").display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
let mut server_process = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
||||
|
|
@ -144,7 +179,10 @@ key = "{}"
|
|||
.unwrap();
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
assert!(server_process.try_wait().unwrap().is_none(), "Server should start with valid multiple host config");
|
||||
assert!(
|
||||
server_process.try_wait().unwrap().is_none(),
|
||||
"Server should start with valid multiple host config"
|
||||
);
|
||||
server_process.kill().unwrap();
|
||||
}
|
||||
|
||||
|
|
@ -177,14 +215,17 @@ root = "/tmp/content"
|
|||
fn test_invalid_hostname_config() {
|
||||
let temp_dir = common::setup_test_environment();
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
["invalid"]
|
||||
root = "{}"
|
||||
cert = "{}"
|
||||
key = "{}"
|
||||
"#, temp_dir.path().join("content").display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display());
|
||||
"#,
|
||||
temp_dir.path().join("content").display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
let output = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
||||
|
|
@ -224,7 +265,8 @@ port = 1965
|
|||
fn test_duplicate_hostname_config() {
|
||||
let temp_dir = common::setup_test_environment();
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
[example.com]
|
||||
root = "{}"
|
||||
cert = "{}"
|
||||
|
|
@ -234,12 +276,14 @@ key = "{}"
|
|||
root = "{}"
|
||||
cert = "{}"
|
||||
key = "{}"
|
||||
"#, temp_dir.path().join("path1").display(),
|
||||
temp_dir.path().join("cert1.pem").display(),
|
||||
temp_dir.path().join("key1.pem").display(),
|
||||
temp_dir.path().join("path2").display(),
|
||||
temp_dir.path().join("cert2.pem").display(),
|
||||
temp_dir.path().join("key2.pem").display());
|
||||
"#,
|
||||
temp_dir.path().join("path1").display(),
|
||||
temp_dir.path().join("cert1.pem").display(),
|
||||
temp_dir.path().join("key1.pem").display(),
|
||||
temp_dir.path().join("path2").display(),
|
||||
temp_dir.path().join("cert2.pem").display(),
|
||||
temp_dir.path().join("key2.pem").display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
// Create the directories and certs
|
||||
|
|
@ -266,7 +310,8 @@ fn test_host_with_port_override() {
|
|||
let config_path = temp_dir.path().join("config.toml");
|
||||
// Test server starts successfully
|
||||
let port = 1969 + (std::process::id() % 1000) as u16;
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
|
||||
|
|
@ -275,10 +320,12 @@ root = "{}"
|
|||
cert = "{}"
|
||||
key = "{}"
|
||||
port = 1970 # Override global port
|
||||
"#, port,
|
||||
temp_dir.path().join("content").display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display());
|
||||
"#,
|
||||
port,
|
||||
temp_dir.path().join("content").display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
let mut server_process = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
||||
|
|
@ -288,7 +335,10 @@ port = 1970 # Override global port
|
|||
.unwrap();
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
assert!(server_process.try_wait().unwrap().is_none(), "Server should start with host port override");
|
||||
assert!(
|
||||
server_process.try_wait().unwrap().is_none(),
|
||||
"Server should start with host port override"
|
||||
);
|
||||
server_process.kill().unwrap();
|
||||
}
|
||||
|
||||
|
|
@ -303,4 +353,4 @@ fn test_config_file_not_found() {
|
|||
assert!(!output.status.success());
|
||||
let stderr = String::from_utf8(output.stderr).unwrap();
|
||||
assert!(stderr.contains("Config file 'nonexistent.toml' not found"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,7 @@ fn test_concurrent_requests_multiple_hosts() {
|
|||
for host in &hosts {
|
||||
let root_dir = temp_dir.path().join(host.replace(".", "_"));
|
||||
std::fs::create_dir(&root_dir).unwrap();
|
||||
std::fs::write(
|
||||
root_dir.join("index.gmi"),
|
||||
format!("Welcome to {}", host),
|
||||
).unwrap();
|
||||
std::fs::write(root_dir.join("index.gmi"), format!("Welcome to {}", host)).unwrap();
|
||||
host_roots.push(root_dir);
|
||||
}
|
||||
|
||||
|
|
@ -26,23 +23,28 @@ fn test_concurrent_requests_multiple_hosts() {
|
|||
let config_path = temp_dir.path().join("config.toml");
|
||||
let port = 1969 + (std::process::id() % 1000) as u16;
|
||||
|
||||
let mut config_content = format!(r#"
|
||||
let mut config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
|
||||
"#, port);
|
||||
"#,
|
||||
port
|
||||
);
|
||||
|
||||
for (i, host) in hosts.iter().enumerate() {
|
||||
config_content.push_str(&format!(r#"
|
||||
config_content.push_str(&format!(
|
||||
r#"
|
||||
["{}"]
|
||||
root = "{}"
|
||||
cert = "{}"
|
||||
key = "{}"
|
||||
"#,
|
||||
host,
|
||||
host_roots[i].display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display()));
|
||||
host,
|
||||
host_roots[i].display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display()
|
||||
));
|
||||
}
|
||||
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
|
@ -65,9 +67,20 @@ key = "{}"
|
|||
let port_clone = Arc::clone(&port_arc);
|
||||
|
||||
let handle = thread::spawn(move || {
|
||||
let response = make_gemini_request("127.0.0.1", *port_clone, &format!("gemini://{}/", host));
|
||||
assert!(response.starts_with("20"), "Request {} failed: {}", i, response);
|
||||
assert!(response.contains(&format!("Welcome to {}", host)), "Wrong content for request {}: {}", i, response);
|
||||
let response =
|
||||
make_gemini_request("127.0.0.1", *port_clone, &format!("gemini://{}/", host));
|
||||
assert!(
|
||||
response.starts_with("20"),
|
||||
"Request {} failed: {}",
|
||||
i,
|
||||
response
|
||||
);
|
||||
assert!(
|
||||
response.contains(&format!("Welcome to {}", host)),
|
||||
"Wrong content for request {}: {}",
|
||||
i,
|
||||
response
|
||||
);
|
||||
response
|
||||
});
|
||||
|
||||
|
|
@ -97,7 +110,8 @@ fn test_mixed_valid_invalid_hostnames() {
|
|||
// Create config
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
let port = 1970 + (std::process::id() % 1000) as u16;
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
|
||||
|
|
@ -106,10 +120,11 @@ root = "{}"
|
|||
cert = "{}"
|
||||
key = "{}"
|
||||
"#,
|
||||
port,
|
||||
root_dir.display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display());
|
||||
port,
|
||||
root_dir.display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
// Start server with TLS
|
||||
|
|
@ -123,8 +138,16 @@ key = "{}"
|
|||
|
||||
// Test valid hostname
|
||||
let valid_response = make_gemini_request("127.0.0.1", port, "gemini://valid.com/");
|
||||
assert!(valid_response.starts_with("20"), "Valid host should work: {}", valid_response);
|
||||
assert!(valid_response.contains("Valid site content"), "Should serve correct content: {}", valid_response);
|
||||
assert!(
|
||||
valid_response.starts_with("20"),
|
||||
"Valid host should work: {}",
|
||||
valid_response
|
||||
);
|
||||
assert!(
|
||||
valid_response.contains("Valid site content"),
|
||||
"Should serve correct content: {}",
|
||||
valid_response
|
||||
);
|
||||
|
||||
// Test various invalid hostnames
|
||||
let invalid_hosts = vec![
|
||||
|
|
@ -135,8 +158,14 @@ key = "{}"
|
|||
];
|
||||
|
||||
for invalid_host in invalid_hosts {
|
||||
let response = make_gemini_request("127.0.0.1", port, &format!("gemini://{}/", invalid_host));
|
||||
assert!(response.starts_with("53"), "Invalid host '{}' should return 53, got: {}", invalid_host, response);
|
||||
let response =
|
||||
make_gemini_request("127.0.0.1", port, &format!("gemini://{}/", invalid_host));
|
||||
assert!(
|
||||
response.starts_with("53"),
|
||||
"Invalid host '{}' should return 53, got: {}",
|
||||
invalid_host,
|
||||
response
|
||||
);
|
||||
}
|
||||
|
||||
server_process.kill().unwrap();
|
||||
|
|
@ -153,7 +182,8 @@ fn test_load_performance_basic() {
|
|||
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
let port = 1971 + (std::process::id() % 1000) as u16;
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
|
||||
|
|
@ -162,10 +192,11 @@ root = "{}"
|
|||
cert = "{}"
|
||||
key = "{}"
|
||||
"#,
|
||||
port,
|
||||
root_dir.display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display());
|
||||
port,
|
||||
root_dir.display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
// Start server with TLS
|
||||
|
|
@ -183,17 +214,30 @@ key = "{}"
|
|||
|
||||
for i in 0..NUM_REQUESTS {
|
||||
let response = make_gemini_request("127.0.0.1", port, "gemini://perf.com/");
|
||||
assert!(response.starts_with("20"), "Request {} failed: {}", i, response);
|
||||
assert!(
|
||||
response.starts_with("20"),
|
||||
"Request {} failed: {}",
|
||||
i,
|
||||
response
|
||||
);
|
||||
}
|
||||
|
||||
let elapsed = start.elapsed();
|
||||
let avg_time = elapsed.as_millis() as f64 / NUM_REQUESTS as f64;
|
||||
|
||||
println!("Processed {} requests in {:?} (avg: {:.2}ms per request)",
|
||||
NUM_REQUESTS, elapsed, avg_time);
|
||||
tracing::debug!(
|
||||
"Processed {} requests in {:?} (avg: {:.2}ms per request)",
|
||||
NUM_REQUESTS,
|
||||
elapsed,
|
||||
avg_time
|
||||
);
|
||||
|
||||
// Basic performance check - should be reasonably fast
|
||||
assert!(avg_time < 100.0, "Average request time too slow: {:.2}ms", avg_time);
|
||||
assert!(
|
||||
avg_time < 100.0,
|
||||
"Average request time too slow: {:.2}ms",
|
||||
avg_time
|
||||
);
|
||||
|
||||
server_process.kill().unwrap();
|
||||
}
|
||||
|
|
@ -222,7 +266,8 @@ fn test_full_request_lifecycle() {
|
|||
let cert_path = temp_dir.path().join("cert.pem");
|
||||
let key_path = temp_dir.path().join("key.pem");
|
||||
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
|
||||
|
|
@ -231,10 +276,11 @@ root = "{}"
|
|||
cert = "{}"
|
||||
key = "{}"
|
||||
"#,
|
||||
port,
|
||||
root_dir.display(),
|
||||
cert_path.display(),
|
||||
key_path.display());
|
||||
port,
|
||||
root_dir.display(),
|
||||
cert_path.display(),
|
||||
key_path.display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
// Start server with TLS
|
||||
|
|
@ -248,27 +294,64 @@ key = "{}"
|
|||
|
||||
// Test root index
|
||||
let root_response = make_gemini_request("127.0.0.1", port, "gemini://lifecycle.com/");
|
||||
assert!(root_response.starts_with("20"), "Root request failed: {}", root_response);
|
||||
assert!(root_response.contains("Main site content"), "Wrong root content: {}", root_response);
|
||||
assert!(
|
||||
root_response.starts_with("20"),
|
||||
"Root request failed: {}",
|
||||
root_response
|
||||
);
|
||||
assert!(
|
||||
root_response.contains("Main site content"),
|
||||
"Wrong root content: {}",
|
||||
root_response
|
||||
);
|
||||
|
||||
// Test explicit index
|
||||
let index_response = make_gemini_request("127.0.0.1", port, "gemini://lifecycle.com/index.gmi");
|
||||
assert!(index_response.starts_with("20"), "Index request failed: {}", index_response);
|
||||
assert!(index_response.contains("Main site content"), "Wrong index content: {}", index_response);
|
||||
assert!(
|
||||
index_response.starts_with("20"),
|
||||
"Index request failed: {}",
|
||||
index_response
|
||||
);
|
||||
assert!(
|
||||
index_response.contains("Main site content"),
|
||||
"Wrong index content: {}",
|
||||
index_response
|
||||
);
|
||||
|
||||
// Test subdirectory index
|
||||
let blog_response = make_gemini_request("127.0.0.1", port, "gemini://lifecycle.com/blog/");
|
||||
assert!(blog_response.starts_with("20"), "Blog request failed: {}", blog_response);
|
||||
assert!(blog_response.contains("Blog index content"), "Wrong blog content: {}", blog_response);
|
||||
assert!(
|
||||
blog_response.starts_with("20"),
|
||||
"Blog request failed: {}",
|
||||
blog_response
|
||||
);
|
||||
assert!(
|
||||
blog_response.contains("Blog index content"),
|
||||
"Wrong blog content: {}",
|
||||
blog_response
|
||||
);
|
||||
|
||||
// Test individual file
|
||||
let about_response = make_gemini_request("127.0.0.1", port, "gemini://lifecycle.com/about.gmi");
|
||||
assert!(about_response.starts_with("20"), "About request failed: {}", about_response);
|
||||
assert!(about_response.contains("About page content"), "Wrong about content: {}", about_response);
|
||||
assert!(
|
||||
about_response.starts_with("20"),
|
||||
"About request failed: {}",
|
||||
about_response
|
||||
);
|
||||
assert!(
|
||||
about_response.contains("About page content"),
|
||||
"Wrong about content: {}",
|
||||
about_response
|
||||
);
|
||||
|
||||
// Test not found
|
||||
let notfound_response = make_gemini_request("127.0.0.1", port, "gemini://lifecycle.com/nonexistent.gmi");
|
||||
assert!(notfound_response.starts_with("51"), "Not found should return 51: {}", notfound_response);
|
||||
let notfound_response =
|
||||
make_gemini_request("127.0.0.1", port, "gemini://lifecycle.com/nonexistent.gmi");
|
||||
assert!(
|
||||
notfound_response.starts_with("51"),
|
||||
"Not found should return 51: {}",
|
||||
notfound_response
|
||||
);
|
||||
|
||||
server_process.kill().unwrap();
|
||||
}
|
||||
|
|
@ -295,4 +378,3 @@ fn make_gemini_request(host: &str, port: u16, url: &str) -> String {
|
|||
Err(e) => format!("Error: Failed to run Python client: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
mod common;
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_per_host_content_isolation() {
|
||||
let temp_dir = common::setup_test_environment();
|
||||
|
|
@ -19,7 +17,8 @@ fn test_per_host_content_isolation() {
|
|||
// Create config with two hosts
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
let port = 1965 + (std::process::id() % 1000) as u16; // Use dynamic port
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
|
||||
|
|
@ -33,13 +32,14 @@ root = "{}"
|
|||
cert = "{}"
|
||||
key = "{}"
|
||||
"#,
|
||||
port,
|
||||
site1_root.display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display(),
|
||||
site2_root.display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display());
|
||||
port,
|
||||
site1_root.display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display(),
|
||||
site2_root.display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
// Start server with TLS
|
||||
|
|
@ -54,13 +54,29 @@ key = "{}"
|
|||
|
||||
// Test site1.com serves its content
|
||||
let response1 = make_gemini_request("127.0.0.1", port, "gemini://site1.com/");
|
||||
assert!(response1.starts_with("20"), "Expected success for site1.com, got: {}", response1);
|
||||
assert!(response1.contains("Welcome to Site 1"), "Should serve site1 content, got: {}", response1);
|
||||
assert!(
|
||||
response1.starts_with("20"),
|
||||
"Expected success for site1.com, got: {}",
|
||||
response1
|
||||
);
|
||||
assert!(
|
||||
response1.contains("Welcome to Site 1"),
|
||||
"Should serve site1 content, got: {}",
|
||||
response1
|
||||
);
|
||||
|
||||
// Test site2.org serves its content
|
||||
let response2 = make_gemini_request("127.0.0.1", port, "gemini://site2.org/");
|
||||
assert!(response2.starts_with("20"), "Expected success for site2.org, got: {}", response2);
|
||||
assert!(response2.contains("Welcome to Site 2"), "Should serve site2 content, got: {}", response2);
|
||||
assert!(
|
||||
response2.starts_with("20"),
|
||||
"Expected success for site2.org, got: {}",
|
||||
response2
|
||||
);
|
||||
assert!(
|
||||
response2.contains("Welcome to Site 2"),
|
||||
"Should serve site2 content, got: {}",
|
||||
response2
|
||||
);
|
||||
|
||||
server_process.kill().unwrap();
|
||||
}
|
||||
|
|
@ -73,7 +89,11 @@ fn test_per_host_path_security() {
|
|||
let site1_root = temp_dir.path().join("site1");
|
||||
std::fs::create_dir(&site1_root).unwrap();
|
||||
std::fs::create_dir(site1_root.join("subdir")).unwrap();
|
||||
std::fs::write(site1_root.join("subdir").join("secret.gmi"), "Secret content").unwrap();
|
||||
std::fs::write(
|
||||
site1_root.join("subdir").join("secret.gmi"),
|
||||
"Secret content",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Create config
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
|
|
@ -81,7 +101,8 @@ fn test_per_host_path_security() {
|
|||
let cert_path = temp_dir.path().join("cert.pem");
|
||||
let key_path = temp_dir.path().join("key.pem");
|
||||
|
||||
let config_content = format!(r#"
|
||||
let config_content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
|
||||
|
|
@ -90,10 +111,11 @@ root = "{}"
|
|||
cert = "{}"
|
||||
key = "{}"
|
||||
"#,
|
||||
port,
|
||||
site1_root.display(),
|
||||
cert_path.display(),
|
||||
key_path.display());
|
||||
port,
|
||||
site1_root.display(),
|
||||
cert_path.display(),
|
||||
key_path.display()
|
||||
);
|
||||
std::fs::write(&config_path, config_content).unwrap();
|
||||
|
||||
let mut server_process = std::process::Command::new(env!("CARGO_BIN_EXE_pollux"))
|
||||
|
|
@ -106,12 +128,24 @@ key = "{}"
|
|||
|
||||
// Test path traversal attempt should be blocked
|
||||
let response = make_gemini_request("127.0.0.1", port, "gemini://site1.com/../../../etc/passwd");
|
||||
assert!(response.starts_with("51"), "Path traversal should be blocked, got: {}", response);
|
||||
assert!(
|
||||
response.starts_with("51"),
|
||||
"Path traversal should be blocked, got: {}",
|
||||
response
|
||||
);
|
||||
|
||||
// Test valid subdirectory access should work
|
||||
let response = make_gemini_request("127.0.0.1", port, "gemini://site1.com/subdir/secret.gmi");
|
||||
assert!(response.starts_with("20"), "Valid subdirectory access should work, got: {}", response);
|
||||
assert!(response.contains("Secret content"), "Should serve content from subdirectory, got: {}", response);
|
||||
assert!(
|
||||
response.starts_with("20"),
|
||||
"Valid subdirectory access should work, got: {}",
|
||||
response
|
||||
);
|
||||
assert!(
|
||||
response.contains("Secret content"),
|
||||
"Should serve content from subdirectory, got: {}",
|
||||
response
|
||||
);
|
||||
|
||||
server_process.kill().unwrap();
|
||||
}
|
||||
|
|
@ -128,4 +162,4 @@ fn make_gemini_request(host: &str, port: u16, url: &str) -> String {
|
|||
.output()
|
||||
.unwrap();
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,8 @@ fn test_virtual_host_routing_multiple_hosts() {
|
|||
|
||||
// Create config with two hosts
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
let content = format!(r#"
|
||||
let content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
|
||||
|
|
@ -85,20 +86,29 @@ root = "{}"
|
|||
cert = "{}"
|
||||
key = "{}"
|
||||
"#,
|
||||
port,
|
||||
temp_dir.path().join("site1").display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display(),
|
||||
temp_dir.path().join("site2").display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display());
|
||||
port,
|
||||
temp_dir.path().join("site1").display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display(),
|
||||
temp_dir.path().join("site2").display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display()
|
||||
);
|
||||
std::fs::write(&config_path, content).unwrap();
|
||||
|
||||
// Create host-specific content
|
||||
std::fs::create_dir_all(temp_dir.path().join("site1")).unwrap();
|
||||
std::fs::create_dir_all(temp_dir.path().join("site2")).unwrap();
|
||||
std::fs::write(temp_dir.path().join("site1").join("index.gmi"), "# Site 1 Content\n").unwrap();
|
||||
std::fs::write(temp_dir.path().join("site2").join("index.gmi"), "# Site 2 Content\n").unwrap();
|
||||
std::fs::write(
|
||||
temp_dir.path().join("site1").join("index.gmi"),
|
||||
"# Site 1 Content\n",
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(
|
||||
temp_dir.path().join("site2").join("index.gmi"),
|
||||
"# Site 2 Content\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Use the same certs for both hosts (server uses first cert anyway)
|
||||
|
||||
|
|
@ -114,11 +124,19 @@ key = "{}"
|
|||
|
||||
// Test request to site1.com with TLS
|
||||
let response1 = make_gemini_request("127.0.0.1", port, "gemini://site1.com/index.gmi");
|
||||
assert!(response1.starts_with("20"), "Expected success response for site1.com, got: {}", response1);
|
||||
assert!(
|
||||
response1.starts_with("20"),
|
||||
"Expected success response for site1.com, got: {}",
|
||||
response1
|
||||
);
|
||||
|
||||
// Test request to site2.org
|
||||
let response2 = make_gemini_request("127.0.0.1", port, "gemini://site2.org/index.gmi");
|
||||
assert!(response2.starts_with("20"), "Expected success response for site2.org, got: {}", response2);
|
||||
assert!(
|
||||
response2.starts_with("20"),
|
||||
"Expected success response for site2.org, got: {}",
|
||||
response2
|
||||
);
|
||||
|
||||
server_process.kill().unwrap();
|
||||
}
|
||||
|
|
@ -132,7 +150,8 @@ fn test_virtual_host_routing_known_hostname() {
|
|||
|
||||
// Config with only one host
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
let content = format!(r#"
|
||||
let content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
|
||||
|
|
@ -141,10 +160,11 @@ root = "{}"
|
|||
cert = "{}"
|
||||
key = "{}"
|
||||
"#,
|
||||
port,
|
||||
temp_dir.path().join("content").display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display());
|
||||
port,
|
||||
temp_dir.path().join("content").display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display()
|
||||
);
|
||||
std::fs::write(&config_path, content).unwrap();
|
||||
|
||||
// Start server with TLS
|
||||
|
|
@ -159,7 +179,11 @@ key = "{}"
|
|||
|
||||
// Test request to unknown hostname
|
||||
let response = make_gemini_request("127.0.0.1", port, "gemini://unknown.com/index.gmi");
|
||||
assert!(response.starts_with("53"), "Should return status 53 for unknown hostname, got: {}", response);
|
||||
assert!(
|
||||
response.starts_with("53"),
|
||||
"Should return status 53 for unknown hostname, got: {}",
|
||||
response
|
||||
);
|
||||
|
||||
server_process.kill().unwrap();
|
||||
}
|
||||
|
|
@ -173,7 +197,8 @@ fn test_virtual_host_routing_malformed_url() {
|
|||
|
||||
// Config with one host
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
let content = format!(r#"
|
||||
let content = format!(
|
||||
r#"
|
||||
bind_host = "127.0.0.1"
|
||||
port = {}
|
||||
|
||||
|
|
@ -182,10 +207,11 @@ root = "{}"
|
|||
cert = "{}"
|
||||
key = "{}"
|
||||
"#,
|
||||
port,
|
||||
temp_dir.path().join("content").display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display());
|
||||
port,
|
||||
temp_dir.path().join("content").display(),
|
||||
temp_dir.path().join("cert.pem").display(),
|
||||
temp_dir.path().join("key.pem").display()
|
||||
);
|
||||
std::fs::write(&config_path, content).unwrap();
|
||||
|
||||
// Start server with TLS
|
||||
|
|
@ -200,7 +226,11 @@ key = "{}"
|
|||
|
||||
// Test malformed URL (wrong protocol)
|
||||
let response = make_gemini_request("127.0.0.1", port, "http://example.com/index.gmi");
|
||||
assert!(response.starts_with("59"), "Should return status 59 for malformed URL, got: {}", response);
|
||||
assert!(
|
||||
response.starts_with("59"),
|
||||
"Should return status 59 for malformed URL, got: {}",
|
||||
response
|
||||
);
|
||||
|
||||
server_process.kill().unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue