use snafu::OptionExt;
use std::env;
use std::ffi::OsString;
use std::os::unix::ffi::OsStrExt;
pub mod request;
pub mod response;
#[cfg(feature = "tower")]
#[cfg_attr(docsrs, doc(cfg(feature = "tower")))]
pub mod tower;
#[cfg(feature = "tower")]
pub use tower::{CgiServiceError, serve_cgi, serve_cgi_with_output};
pub use request::CGIRequest;
pub use response::CGIResponse;
pub struct MetaVariable {
pub kind: MetaVariableKind,
pub value: OsString,
}
impl MetaVariable {
pub fn as_str(&self) -> Result<&str> {
self.value
.as_os_str()
.to_str()
.context(error::InvalidVariableEncodingSnafu {
kind: self.kind,
value: self.value.clone(),
encoding: "UTF-8",
})
}
pub fn as_bytes(&self) -> &[u8] {
self.value.as_bytes()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MetaVariableKind {
AuthType,
ContentLength,
ContentType,
GatewayInterface,
PathInfo,
PathTranslated,
QueryString,
RemoteAddr,
RemoteHost,
RequestIdent,
RemoteUser,
RequestMethod,
ScriptName,
ServerName,
ServerPort,
ServerProtocol,
ServerSoftware,
UniqueID,
HttpAuthorization,
HttpHost,
HttpUserAgent,
HttpAccept,
HttpCookie,
ServerSignature,
DocumentRoot,
RequestScheme,
ContextDocumentRoot,
ServerAdmin,
ScriptFilename,
RemotePort,
RequestUri,
}
impl MetaVariableKind {
fn as_str(&self) -> &'static str {
match self {
MetaVariableKind::HttpAuthorization => "HTTP_AUTHORIZATION",
MetaVariableKind::AuthType => "AUTH_TYPE",
MetaVariableKind::ContentLength => "CONTENT_LENGTH",
MetaVariableKind::ContentType => "CONTENT_TYPE",
MetaVariableKind::GatewayInterface => "GATEWAY_INTERFACE",
MetaVariableKind::PathInfo => "PATH_INFO",
MetaVariableKind::PathTranslated => "PATH_TRANSLATED",
MetaVariableKind::QueryString => "QUERY_STRING",
MetaVariableKind::RemoteAddr => "REMOTE_ADDR",
MetaVariableKind::RemoteHost => "REMOTE_HOST",
MetaVariableKind::RequestIdent => "REQUEST_IDENT",
MetaVariableKind::RemoteUser => "REMOTE_USER",
MetaVariableKind::RequestMethod => "REQUEST_METHOD",
MetaVariableKind::ScriptName => "SCRIPT_NAME",
MetaVariableKind::ServerName => "SERVER_NAME",
MetaVariableKind::ServerPort => "SERVER_PORT",
MetaVariableKind::ServerProtocol => "SERVER_PROTOCOL",
MetaVariableKind::ServerSoftware => "SERVER_SOFTWARE",
MetaVariableKind::UniqueID => "UNIQUE_ID",
MetaVariableKind::HttpHost => "HTTP_HOST",
MetaVariableKind::HttpUserAgent => "HTTP_USER_AGENT",
MetaVariableKind::HttpAccept => "HTTP_ACCEPT",
MetaVariableKind::ServerSignature => "SERVER_SIGNATURE",
MetaVariableKind::DocumentRoot => "DOCUMENT_ROOT",
MetaVariableKind::RequestScheme => "REQUEST_SCHEME",
MetaVariableKind::ContextDocumentRoot => "CONTEXT_DOCUMENT_ROOT",
MetaVariableKind::ServerAdmin => "SERVER_ADMIN",
MetaVariableKind::ScriptFilename => "SCRIPT_FILENAME",
MetaVariableKind::RemotePort => "REMOTE_PORT",
MetaVariableKind::RequestUri => "REQUEST_URI",
MetaVariableKind::HttpCookie => "HTTP_COOKIE",
}
}
pub fn from_env(&self) -> Option<MetaVariable> {
env::var_os(self.as_str()).map(|value| MetaVariable { kind: *self, value })
}
pub fn try_from_env(&self) -> Result<MetaVariable> {
let kind = *self;
env::var_os(self.as_str())
.map(|value| MetaVariable { kind, value })
.context(error::MetaVariableNotSetSnafu { kind })
}
}
impl std::fmt::Display for MetaVariableKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
pub mod error {
use super::*;
use snafu::Snafu;
#[derive(Debug, Snafu)]
#[snafu(visibility(pub))]
pub enum CGIError {
#[snafu(display("Failed to parse environment variables: {}", source))]
ParseEnv { source: std::env::VarError },
#[snafu(display("Error fetching meta-varaible '{}' from environment: not set", kind))]
MetaVariableNotSet { kind: MetaVariableKind },
#[snafu(display("Failed to parse content-length: {}", source))]
InvalidContentLength { source: std::num::ParseIntError },
#[snafu(display(
"Failed to parse meta-variable '{}' value '{}' as {}",
kind,
value.to_string_lossy(),
encoding
))]
InvalidVariableEncoding {
kind: MetaVariableKind,
value: OsString,
encoding: &'static str,
},
#[snafu(display("Failed to read request body from stdin: {}", source))]
ReadRequestBody { source: std::io::Error },
#[snafu(display("Failed to parse request: {}", source))]
RequestParse { source: hyper::http::Error },
#[snafu(display("Failed to gather response into buffer"))]
BuildResponse,
#[snafu(display("Failed to write response: {}", source))]
WriteResponse { source: std::io::Error },
}
}
pub use error::CGIError;
type Result<T> = std::result::Result<T, CGIError>;