diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..72abb0e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,131 @@ +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<Request<Incoming>> for Svc { + type Response = Response<Full<Bytes>>; + type Error = hyper::Error; + type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>; + + fn call(&self, req: Request<Incoming>) -> Self::Future { + fn make<B: Into<Bytes>>(b: B) -> Result<Response<Full<Bytes>>, 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<Incoming>) -> Response<Full<Bytes>> { + 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: Into<Bytes>>(b: B) -> Response<Full<Bytes>> { + Response::builder().body(Full::new(b.into())).unwrap() + } +} |