This repository has been archived on 2025-08-04. You can view files and clone it, but cannot push or open issues or pull requests.
rhaj/components/calendar/examples/server.rs
2025-04-04 08:28:07 +02:00

120 lines
3.5 KiB
Rust

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<Body>) -> Result<Response<Body>, 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<Body>) -> Vec<u8> {
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<Body>
fn parse_raw_http_response(bytes: &[u8]) -> Result<Response<Body>, Box<dyn std::error::Error>> {
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#"<script>
const ws = new WebSocket("ws://localhost:35729");
ws.onmessage = (msg) => {
if (msg.data === "reload") location.reload();
};
</script>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
>
"#;
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);
}
}