use hyper::{Body, Request, Response, Server}; use hyper::service::{make_service_fn, service_fn}; use hyper::{Method, StatusCode}; use std::convert::Infallible; use std::net::SocketAddr; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::process::Command; use httparse; async fn handle_request(req: Request) -> Result, Infallible> { // Serialize request into raw HTTP format let raw_request = build_raw_http_request(req).await; // Spawn the binary let mut child = Command::new("./target/debug/calendar") .stdin(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) .spawn() .expect("failed to spawn binary"); // Feed raw HTTP request to binary's stdin if let Some(mut stdin) = child.stdin.take() { let _ = stdin.write_all(&raw_request).await; } // Read response from binary's stdout let mut stdout = child.stdout.take().expect("no stdout"); let mut output = Vec::new(); let _ = stdout.read_to_end(&mut output).await; let _ = child.wait().await; // Parse raw HTTP response match parse_raw_http_response(&output) { Ok(res) => Ok(res), Err(e) => { let mut resp = Response::new(Body::from(format!("Failed to parse response: {}", e))); *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; Ok(resp) } } } // Build raw HTTP request string async fn build_raw_http_request(req: Request) -> Vec { let mut raw = String::new(); let method = req.method(); let path = req.uri().path(); let version = match req.version() { hyper::Version::HTTP_11 => "HTTP/1.1", _ => "HTTP/1.1", }; raw.push_str(&format!("{method} {path} {version}\r\n")); for (key, value) in req.headers() { if let Ok(val_str) = value.to_str() { raw.push_str(&format!("{}: {}\r\n", key, val_str)); } } raw.push_str("\r\n"); let body_bytes = hyper::body::to_bytes(req.into_body()).await.unwrap_or_default(); let mut full = raw.into_bytes(); full.extend_from_slice(&body_bytes); full } // Parse raw HTTP response into hyper::Response fn parse_raw_http_response(bytes: &[u8]) -> Result, Box> { let mut headers = [httparse::EMPTY_HEADER; 64]; let mut res = httparse::Response::new(&mut headers); let parsed_len = res.parse(bytes)?.unwrap(); // returns offset let status = res.code.unwrap_or(200); let mut builder = Response::builder().status(status); for h in res.headers.iter() { builder = builder.header(h.name, std::str::from_utf8(h.value)?); } // Get body and append LiveReload script let mut body = bytes[parsed_len..].to_vec(); let livereload_snippet = br#" "#; body.extend_from_slice(b"\n"); body.extend_from_slice(livereload_snippet); Ok(builder.body(Body::from(body))?) } #[tokio::main] async fn main() { let addr = SocketAddr::from(([127, 0, 0, 1], 8080)); let make_svc = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle_request)) }); println!("Proxy server running at http://{}", addr); if let Err(e) = Server::bind(&addr).serve(make_svc).await { eprintln!("server error: {}", e); } }