From 33f9cbac8908e9c15e6ef5c5878681a425411cf1 Mon Sep 17 00:00:00 2001 From: Thomas Epperson Date: Thu, 11 Dec 2025 09:59:05 -0600 Subject: [PATCH] Add async capabilities to the crate. --- Cargo.toml | 3 +++ src/lib.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 453f55e..98ea58a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,6 @@ exclude = [".gitignore", ".github/**"] [dependencies] http = "1.0.0" + +[features] +async = [] \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 7578f59..0c11c16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,6 +50,8 @@ //! //! Several shortcut functions are provided (such as [`html_response`](fn.html_response.html)/[`binary_response`](fn.binary_response.html)) +#![feature(async_trait_bounds)] + use std::collections::HashMap; use std::convert::TryFrom; use std::fmt::Debug; @@ -63,6 +65,34 @@ pub type Request = http::Request>; /// A `Vec` Response from http pub type Response = http::Response>; +#[cfg(feature = "async")] +async fn handle_with_io_async(func: F, mut stdin: R, mut stdout: W) +where + F: async FnOnce(Request) -> Response, + R: Read, + W: Write, +{ + let env_vars: HashMap = std::env::vars().collect(); + + // How many bytes do we have to read for request body + // A general stdin().read_to_end() can block if the webserver doesn't close things + let content_length: usize = env_vars + .get("CONTENT_LENGTH") + .and_then(|cl| cl.parse::().ok()) + .unwrap_or(0); + + let mut stdin_contents = vec![0; content_length]; + stdin.read_exact(&mut stdin_contents).unwrap(); + + let request = parse_request(env_vars, stdin_contents); + + let response = func(request).await; + + let output = serialize_response(response); + + stdout.write_all(&output).unwrap(); +} + fn handle_with_io(func: F, mut stdin: R, mut stdout: W) where F: FnOnce(Request) -> Response, @@ -111,6 +141,21 @@ where ) } +/// Asynchronously call a function as a CGI programme. +/// +/// This should be called from a `main` function. +/// Parse & extract the CGI environmental variables, and HTTP request body, +/// to create `Request`, and convert your `Response` into the correct format and +/// print to stdout. +#[cfg(feature = "async")] +pub async fn handle_async(func: F) +where + F: async FnOnce(Request) -> Response, +{ + handle_with_io_async(func, std::io::stdin(), std::io::stdout()).await +} + + /// Call a function as a CGI programme. /// /// This should be called from a `main` function.