use std::error;
use std::fmt;
use std::io;
use std::io::BufRead;
use std::io::Error as IoError;
use std::io::Read;
use std::process::Command;
use std::process::Stdio;
use Request;
use Response;
use ResponseBody;
#[derive(Debug)]
pub enum CgiError {
BodyAlreadyExtracted,
IoError(IoError),
}
impl From<IoError> for CgiError {
fn from(err: IoError) -> CgiError {
CgiError::IoError(err)
}
}
impl error::Error for CgiError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
CgiError::IoError(ref e) => Some(e),
_ => None,
}
}
}
impl fmt::Display for CgiError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let description = match *self {
CgiError::BodyAlreadyExtracted => "the body of the request was already extracted",
CgiError::IoError(_) => {
"could not read the body from the request, or could not execute the CGI program"
}
};
write!(fmt, "{}", description)
}
}
pub trait CgiRun {
fn start_cgi(self, request: &Request) -> Result<Response, CgiError>;
}
impl CgiRun for Command {
fn start_cgi(mut self, request: &Request) -> Result<Response, CgiError> {
self.env("SERVER_SOFTWARE", "rouille")
.env("SERVER_NAME", "localhost") .env("GATEWAY_INTERFACE", "CGI/1.1")
.env("SERVER_PROTOCOL", "HTTP/1.1") .env("SERVER_PORT", "80") .env("REQUEST_METHOD", request.method())
.env("PATH_INFO", &request.url()) .env("SCRIPT_NAME", "") .env("QUERY_STRING", request.raw_query_string())
.env("REMOTE_ADDR", &request.remote_addr().to_string())
.env("AUTH_TYPE", "") .env("REMOTE_USER", "") .env(
"CONTENT_TYPE",
&request.header("Content-Type").unwrap_or(""),
)
.env(
"CONTENT_LENGTH",
&request.header("Content-Length").unwrap_or(""),
)
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.stdin(Stdio::piped());
let mut child = self.spawn()?;
if let Some(mut body) = request.data() {
io::copy(&mut body, child.stdin.as_mut().unwrap())?;
} else {
return Err(CgiError::BodyAlreadyExtracted);
}
let response = {
let mut stdout = io::BufReader::new(child.stdout.take().unwrap());
let mut headers = Vec::new();
let mut status_code = 200;
for header in stdout.by_ref().lines() {
let header = header?;
if header.is_empty() {
break;
}
let (header, val) = header.split_once(':').unwrap();
let val = &val[1..];
if header == "Status" {
status_code = val[0..3]
.parse()
.expect("Status returned by CGI program is invalid");
} else {
headers.push((header.to_owned().into(), val.to_owned().into()));
}
}
Response {
status_code,
headers,
data: ResponseBody::from_reader(stdout),
upgrade: None,
}
};
Ok(response)
}
}