cgi-response 0.0.1

Parse a CGI child's stdout into an http::Response with a streaming body.
Documentation

CGI Response

Parse a CGI child's stdout into an http::Response, then stream the body through http_body::Body. The other half of a CGI gateway — building the RFC 3875 environment for the child — lives in the cgi-request crate; pair them when you need both directions.

This crate is a building block, not a complete CGI runner — the fork-exec / pre_exec / privilege-drop / rlimits bits stay in the host, where the unsafe boundary can be audited in context. What lives here is everything around the response that's mechanically reusable regardless of how the child was spawned.

Features

  • [read_until_header_end] — read an AsyncRead until the RFC 3875 header / body separator (\r\n\r\n), with a deadline. Returns the header block, the leftover post-separator bytes, and the still-open reader for body streaming.
  • [parse_response_headers] — turn a header block into an http::response::Builder. Handles the Status: CGI-specific header, the Location:-without-Status: ⇒ 302 fallback, and the no-Status: ⇒ 200 default.
  • [CgiResponseBody] — http_body::Body impl that yields the leftover bytes first, then poll-reads from the open reader to EOF. Generic over a drop-guard parameter so the host can hold a permit / Arc / cancellation guard alive for the body's lifetime.

Example

use std::time::Duration;
use cgi_response::{parse_response_headers, read_until_header_end, CgiResponseBody};
use tokio::time::Instant;

# async fn drive<R, G>(stdout: R, guard: G) -> Result<http::Response<CgiResponseBody<R, G>>, Box<dyn std::error::Error>>
# where
#     R: tokio::io::AsyncRead + Unpin + Send,
#     G: Send + Unpin + 'static,
# {
let connect_deadline = Instant::now() + Duration::from_secs(5);
let total_deadline = connect_deadline + Duration::from_secs(60);

let (header_block, leftover, stdout) = read_until_header_end(stdout, connect_deadline).await?;
let resp = parse_response_headers(&header_block)?
    .body(CgiResponseBody::new(leftover, stdout, total_deadline, guard))?;
# Ok(resp)
# }

License

Released under the MIT License © 2026 Canmi