use core::panic; use std::{net::SocketAddr, pin::Pin}; use http_body_util::{BodyExt, Full}; use hyper::{ Request, Response, StatusCode, body::{Body, Bytes, Incoming}, server::conn::http1, service::Service, }; use hyper_util::rt::TokioIo; use tokio::{net::TcpListener, process::Command, runtime::Runtime}; fn main() { let rt = Runtime::new().unwrap(); rt.block_on(async { run().await }); } async fn run() { let addr = SocketAddr::from(([0, 0, 0, 0], 2562)); let listen = TcpListener::bind(addr).await.unwrap(); let svc = Svc {}; loop { let (stream, _caddr) = listen.accept().await.unwrap(); let io = TokioIo::new(stream); let svc_clone = svc.clone(); tokio::task::spawn( async move { http1::Builder::new().serve_connection(io, svc_clone).await }, ); } } #[derive(Clone, Debug)] struct Svc {} impl Service> for Svc { type Response = Response>; type Error = hyper::Error; type Future = Pin> + Send>>; fn call(&self, req: Request) -> Self::Future { fn make>(b: B) -> Result>, hyper::Error> { Ok(Response::builder().body(Full::new(b.into())).unwrap()) } Box::pin(async { Ok(Self::handle(req).await) }) } } impl Svc { async fn handle(req: Request) -> Response> { let method = req.method().as_str().to_ascii_uppercase(); let path = req.uri().path().to_owned(); let headers = req.headers().clone(); let body = req.into_body().collect().await.unwrap().to_bytes(); let content_length = body.len(); let mut cmd = Command::new("/usr/lib/cgit/cgit.cgi"); cmd.env("PATH_INFO", path).env("REQUEST_METHOD", method); for (header, value) in headers { if let Some(header) = header { let hname = header.as_str(); let hvalue = value.to_str().unwrap(); cmd.env(hname.to_ascii_uppercase(), hvalue); if hname.to_ascii_lowercase() == "user-agent" { println!("USER_AGENT: {hvalue}"); } } } /*if content_length > 0 { cmd.env("CONTENT_LENGTH", content_length.to_string()); }*/ let output = cmd.output().await.unwrap(); let response_raw = output.stdout; let mut response = Response::builder(); println!("{}", String::from_utf8_lossy(&response_raw)); let mut curr = response_raw.as_slice(); let mut status = None; let mut headers = vec![]; let body = loop { let nl = curr.iter().position(|b| *b == b'\n').expect("no nl in header"); let line = &curr[..nl]; let colon = line.iter().position(|b| *b == b':').expect("no colon in header"); let key = &line[..colon]; let value = &line[colon + 1..]; headers.push((key, value)); let key_string = String::from_utf8_lossy(key); if key_string == "Status" { let value_string = String::from_utf8_lossy(value); if let Some((raw_code, raw_msg)) = value_string.trim().split_once(' ') { let code: usize = raw_code.parse().unwrap(); status = Some((code, raw_msg.to_owned())); } } // Body next if curr[nl + 1] == b'\n' || (curr[nl + 1] == b'\r' && curr[nl + 2] == b'\n') { break &curr[nl + 2..]; } else { curr = &curr[nl + 1..]; } }; match status { None => response = response.status(StatusCode::OK), Some((code, _status)) => { response = response.status(StatusCode::from_u16(code as u16).unwrap()); } } for (key, value) in headers { response = response.header(key.to_vec(), value.to_vec()); } response.body(Full::new(Bytes::from(body.to_vec()))).unwrap() } fn make>(b: B) -> Response> { Response::builder().body(Full::new(b.into())).unwrap() } }