tortuga 0.7.2

A Rust-based CGI-specific HTTP web server.
Documentation
use crate::script::{Process, Wasm};
use crate::{about, server};
use std::ffi::OsStr;
use std::net::SocketAddr;
use std::path::PathBuf;

pub struct ServerContext {
    document_root: PathBuf,
    cgi_bin: PathBuf,
    hostname: String,
    ip_address: String,
    port: String,
    path: &'static str,
    scheme: String,
    software: String,
    cgi_scripts: ScriptMapping,
}

pub struct ScriptMapping {
    process: Process,
    wasm: Wasm,
}

impl ScriptMapping {
    pub fn new(process: Process, wasm: Wasm) -> Self {
        Self { process, wasm }
    }

    pub fn process(&self) -> &Process {
        &self.process
    }

    pub fn wasm(&self) -> &Wasm {
        &self.wasm
    }
}

impl ServerContext {
    pub fn new(address: SocketAddr, options: server::Options, cgi_scripts: ScriptMapping) -> Self {
        let ip_address = address.ip().to_string();
        let port = address.port().to_string();

        let path: &'static str = env!("PATH");
        let scheme = String::from("http");
        let software = format!("{}/{}", about::PROGRAM, about::VERSION);

        Self {
            document_root: options.document_root,
            cgi_bin: options.cgi_bin,
            hostname: options.hostname,
            ip_address,
            port,
            path,
            scheme,
            software,
            cgi_scripts,
        }
    }

    pub fn path(&self) -> &str {
        self.path
    }

    pub fn scheme(&self) -> &str {
        self.scheme.as_str()
    }

    pub fn software(&self) -> &str {
        self.software.as_str()
    }

    pub fn server_name(&self) -> &str {
        self.hostname.as_str()
    }

    pub fn script_filename<'a>(&self, path: &'a str) -> Option<(PathBuf, &'a str)> {
        let script_path = path.strip_prefix("/cgi-bin/")?;

        let index = script_path
            .chars()
            .position(|c| c == '/')
            .unwrap_or_else(|| script_path.len());
        let (filename, extra_path) = script_path.split_at(index);

        let mut file_path = self.cgi_bin.clone();

        file_path.push(filename);

        Some((file_path, extra_path))
    }

    pub fn resolve_path(&self, path: &str) -> PathBuf {
        let mut normalized_path = path.strip_prefix('/').unwrap_or(path);

        if normalized_path.is_empty() {
            normalized_path = "index.html";
        }

        self.document_root.join(normalized_path)
    }

    pub fn translate_path(&self, path: &str) -> PathBuf {
        self.document_root
            .join(path.strip_prefix('/').unwrap_or(path))
    }

    pub fn working_directory(&self) -> &OsStr {
        self.document_root.as_os_str()
    }

    pub fn ip_address(&self) -> &str {
        self.ip_address.as_str()
    }

    pub fn port(&self) -> &str {
        self.port.as_str()
    }

    pub fn script_mappings(&self) -> &ScriptMapping {
        &self.cgi_scripts
    }
}