use std::process::{Child as ChildProcess, Command}; use std::rc::Rc; use hyper::http::uri::Scheme; use hyper::{header, Body, Client, Request, Response}; use rhai::{Dynamic, FnPtr, Map}; pub struct KillOnDrop(ChildProcess); impl Drop for KillOnDrop { fn drop(&mut self) { self.0.kill().unwrap(); } } #[derive(Clone)] pub struct ProxyChild { process: Rc, port: u16, } impl ProxyChild { fn new(params: Map) -> Self { let command_line = params["command"].clone().into_immutable_string().unwrap(); let port = params["port"].as_int().unwrap(); let mut command_line = command_line.split(" "); let command = command_line.next().unwrap(); let mut child = Command::new(command); child.args(command_line); if let Some(cwd) = params.get("in_dir") { let cwd = cwd.clone().into_immutable_string().unwrap(); let cwd: &str = cwd.as_ref(); child.current_dir(cwd); } let child = child.spawn().unwrap(); Self { process: Rc::new(KillOnDrop(child)), port: port as u16, } } } pub fn handle_request(child: &mut ProxyChild, request: Rc>) -> Rc> { let ProxyChild { port, .. } = child; let mut request_uri = request.uri().clone().into_parts(); // TODO ipv6 loopback? request_uri.authority = Some(format!("127.0.0.1:{}", port).parse().unwrap()); request_uri.scheme = Some(Scheme::HTTP); let mut proxy_request = Request::builder() .method(request.method()) .uri(request_uri) .header(header::HOST, request.headers()[header::HOST].clone()); proxy_request.headers_mut().unwrap().extend( request .headers() .iter() .map(|(x, y)| (x.clone(), y.clone())), ); // TODO handle nonempty body let proxy_request = proxy_request.body(Body::empty()).unwrap(); let response = async { let client = Client::new(); Rc::new(client.request(proxy_request).await.unwrap()) }; let runtime = tokio::runtime::Handle::current(); runtime.block_on(response) } pub fn proxy_child(params: Map) -> FnPtr { let child = ProxyChild::new(params); let mut result = FnPtr::new("handle_request_proxy_child").unwrap(); result.add_curry(Dynamic::from(child)); result }