Skip to content
Open
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
4 changes: 3 additions & 1 deletion cgi-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ version = "0.1.0"
edition = "2021"

[dependencies]
hyper = "0.14"
http-body-util = "0.1.2"
hyper = "1.6.0"
snafu = "0.8"
tokio = "1"
bytes = "1.10.0"
10 changes: 8 additions & 2 deletions cgi-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@
//! ## Examples
//! ### Parsing an HTTP Request
//! ```rust
//! use hyper::{Request, Body};
//! use hyper::Request;
//! use hyper::body::Bytes;
//! use http_body_util::Full;
//! use cgi_rs::CGIRequest;
//!
//! // In a CGI environment, the CGI server would set these variables, as well as others.
//! std::env::set_var("REQUEST_METHOD", "GET");
//! std::env::set_var("CONTENT_LENGTH", "0");
//! std::env::set_var("REQUEST_URI", "/");
//!
//! let cgi_request: Request<Body> = CGIRequest::from_env()
//! let cgi_request: Request<Full<Bytes>> = CGIRequest::<Full<Bytes>>::from_env()
//! .and_then(Request::try_from).unwrap();
//!
//! assert_eq!(cgi_request.method(), "GET");
Expand Down Expand Up @@ -103,9 +105,11 @@ pub enum MetaVariableKind {

// Not in the RFC, but emitted from httpd's mod_cgi
UniqueID,
HttpAuthorization,
HttpHost,
HttpUserAgent,
HttpAccept,
HttpCookie,
ServerSignature,
DocumentRoot,
RequestScheme,
Expand All @@ -119,6 +123,7 @@ pub enum MetaVariableKind {
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",
Expand Down Expand Up @@ -148,6 +153,7 @@ impl MetaVariableKind {
MetaVariableKind::ScriptFilename => "SCRIPT_FILENAME",
MetaVariableKind::RemotePort => "REMOTE_PORT",
MetaVariableKind::RequestUri => "REQUEST_URI",
MetaVariableKind::HttpCookie => "HTTP_COOKIE",
}
}

Expand Down
47 changes: 35 additions & 12 deletions cgi-rs/src/request.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
use crate::{error, CGIError, MetaVariable, MetaVariableKind, Result};
use hyper::{Body as HttpBody, Request};
use crate::{error, MetaVariable, MetaVariableKind, Result};
use http_body_util::Full;
use hyper::body::{Body, Bytes};
use hyper::Request;
use snafu::ResultExt;
use std::io::{stdin, Read};

pub struct CGIRequest {
pub request_body: HttpBody,
pub struct CGIRequest<B> {
pub request_body: B,
}

impl CGIRequest {
pub fn from_env() -> Result<Self> {
impl<B> CGIRequest<B>
where
B: Body,
{
pub fn from_env() -> Result<CGIRequest<Full<Bytes>>> {
let content_length = MetaVariableKind::ContentLength
.from_env()
.map(|content_length| {
Expand All @@ -19,8 +24,15 @@ impl CGIRequest {
.transpose()?
.unwrap_or_default();

let request_body = HttpBody::from(Self::request_body_from_env(content_length)?);
Ok(Self { request_body })
let read_content = Self::request_body_from_env(content_length)?;

let request_body = Bytes::from(read_content);

let full = Full::from(request_body);

let result = CGIRequest { request_body: full };

Ok(result)
}

pub fn var(&self, kind: MetaVariableKind) -> Option<MetaVariable> {
Expand All @@ -44,11 +56,17 @@ impl CGIRequest {
self.var(MetaVariableKind::RequestUri)
.map(|uri| Ok(uri.as_str()?.to_string()))
.unwrap_or_else(|| {
let path_info_str = match MetaVariableKind::PathInfo.try_from_env() {
Ok(meta_variable) => String::from(meta_variable.as_str().unwrap_or("")),
Err(_) => String::from(""),
};

let script_name = MetaVariableKind::ScriptName.try_from_env()?;
let query_string = MetaVariableKind::QueryString.try_from_env()?;
Ok(format!(
"{}?{}",
"{}{}?{}",
script_name.as_str()?,
path_info_str,
query_string.as_str()?
))
})
Expand All @@ -65,10 +83,13 @@ macro_rules! try_set_headers {
};
}

impl TryFrom<CGIRequest> for Request<HttpBody> {
type Error = CGIError;
impl<B> TryFrom<CGIRequest<B>> for Request<B>
where
B: Body,
{
type Error = crate::CGIError;

fn try_from(cgi_request: CGIRequest) -> Result<Self> {
fn try_from(cgi_request: CGIRequest<B>) -> Result<Self> {
let mut request_builder = Request::builder()
.method(
cgi_request
Expand All @@ -81,9 +102,11 @@ impl TryFrom<CGIRequest> for Request<HttpBody> {
request_builder,
cgi_request,
["Content-Length", MetaVariableKind::ContentLength],
["Authorization", MetaVariableKind::HttpAuthorization],
["Accept", MetaVariableKind::HttpAccept],
["Host", MetaVariableKind::HttpHost],
["User-Agent", MetaVariableKind::HttpUserAgent],
["Cookie", MetaVariableKind::HttpCookie],
);

request_builder
Expand Down
42 changes: 13 additions & 29 deletions cgi-rs/src/response.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
use crate::{error, CGIError, Result};
use hyper::{http::HeaderValue, HeaderMap, Response};
use crate::{error, Result};
use bytes::Bytes;
use hyper::{http::HeaderValue, HeaderMap};
use snafu::ResultExt;
use std::io::Write;

#[derive(Debug)]
pub struct CGIResponse<B: hyper::body::HttpBody> {
headers: HeaderMap<HeaderValue>,
status: String,
reason: Option<String>,
body: B,
pub struct CGIResponse {
pub headers: HeaderMap<HeaderValue>,
pub status: String,
pub reason: Option<String>,
pub body: Bytes,
}

impl<B: hyper::body::HttpBody> CGIResponse<B> {
impl CGIResponse {
pub async fn write_response_to_output(self, mut output: impl Write) -> Result<()> {
self.write_status(&mut output).await?;
self.write_headers(&mut output).await?;
Expand Down Expand Up @@ -50,29 +51,12 @@ impl<B: hyper::body::HttpBody> CGIResponse<B> {
}

async fn write_body(self, output: &mut impl Write) -> Result<()> {
let body = hyper::body::to_bytes(self.body)
.await
.or_else(|_| error::BuildResponseSnafu.fail())?;
let body = self.body;

output.write(&body).context(error::WriteResponseSnafu)?;
output
.write(body.as_ref())
.context(error::WriteResponseSnafu)?;

Ok(())
}
}

impl<B: hyper::body::HttpBody> TryFrom<Response<B>> for CGIResponse<B> {
type Error = CGIError;

fn try_from(response: Response<B>) -> Result<Self> {
let headers = response.headers().clone();
let status = response.status().to_string();
let reason = response.status().canonical_reason().map(|s| s.to_string());
let body = response.into_body();
Ok(CGIResponse {
headers,
status,
reason,
body,
})
}
}
8 changes: 8 additions & 0 deletions http-cgi-server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "http-cgi-server"
version = "0.1.0"
edition = "2021"

[dependencies]

[workspace]
Loading