diff --git a/Cargo.lock b/Cargo.lock index bdee6b2..dc5fa53 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,6 +38,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures-channel" version = "0.3.28" @@ -222,6 +231,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -311,6 +330,7 @@ dependencies = [ "serde_yaml", "tokio", "tower", + "url", ] [[package]] @@ -342,6 +362,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + [[package]] name = "pin-project" version = "1.0.12" @@ -538,6 +564,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.27.0" @@ -642,18 +683,44 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + [[package]] name = "unicode-ident" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + [[package]] name = "unsafe-libyaml" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "want" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index 7848e97..eb94070 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ tower = { version = "0.4", features = ["full"] } serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9" lazy_static = "1.4.0" +url = "2.3.0" [[bin]] name = "odproxy" diff --git a/conf.rs b/conf.rs index 216e898..b264612 100755 --- a/conf.rs +++ b/conf.rs @@ -21,7 +21,8 @@ pub struct RootConf { pub struct ProxyConf { pub hosts: Vec, pub target: String, - pub socket: Option, + #[serde(default)] + pub socket: bool, pub spawn: Option, pub timeout: Option } @@ -29,8 +30,10 @@ pub struct ProxyConf { #[derive(Debug, Deserialize, Clone)] pub struct SpawnConf { pub command: String, - pub args: Option>, - pub envs: Option> + #[serde(default)] + pub args: Vec, + #[serde(default)] + pub envs: Vec<(String, String)> } fn load() -> RootConf { diff --git a/main.rs b/main.rs index ced89f3..6171eb1 100755 --- a/main.rs +++ b/main.rs @@ -26,9 +26,7 @@ async fn run(req: Request) -> Result, hyper::Error> { let mut request_builder = Request::builder().method(req.method()); let path = req.uri().path_and_query().unwrap().as_str(); - let is_socket = p.socket.unwrap_or(false); - - if is_socket { + if p.socket { request_builder = request_builder.uri(hyperlocal::Uri::new(&p.target, path)); } else { let url = p.target.clone() + path; @@ -44,7 +42,7 @@ async fn run(req: Request) -> Result, hyper::Error> { let body = req.into_body(); let nreq = request_builder.body(body).unwrap(); - if is_socket { + if p.socket { Client::unix().request(nreq).await } else { Client::new().request(nreq).await diff --git a/services.rs b/services.rs index 7c7b966..6fae356 100755 --- a/services.rs +++ b/services.rs @@ -1,8 +1,21 @@ -use std::{process::Stdio, time::{Duration, SystemTime, UNIX_EPOCH}, path::Path, io::Error, thread}; +use std::{process::Stdio, time::{Duration, SystemTime, UNIX_EPOCH}, path::Path, io::Error, thread, net::SocketAddr}; use tokio::{process::{Command, Child}, time::sleep, fs}; +use url::Url; +use std::net::TcpStream; +use std::net::{ToSocketAddrs}; use crate::{data::{SERVICES, ServiceData}, conf::{ProxyConf, self}}; +fn target_to_address(target: &str) -> Option { + Url::parse(target) + .ok() + .and_then(|url| { + let host = url.host()?; + let port = url.port()?; + (host.to_string(), port).to_socket_addrs().ok().and_then(|addr| addr.last()) + }) +} + fn modify_service_data(name: &String, modify_fn: F) where F: FnOnce(&mut ServiceData) { @@ -12,41 +25,31 @@ fn modify_service_data(name: &String, modify_fn: F) } } -fn set_service_running(name: &String) { - modify_service_data(name, |s| { - s.running = true; - }); -} - -fn set_service_last_active(name: &String) { - modify_service_data(name, |s| { - s.last_active = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); - }); -} - -fn is_service_running(name: &String) -> bool { - if let Some(service_data) = SERVICES.lock().unwrap().get(name) { - service_data.running - } else { - false - } -} - pub async fn check_service(name: &String, proxy: &ProxyConf) { if proxy.spawn.is_some() { - if proxy.socket.unwrap_or(false) && SERVICES.lock().unwrap().get(name).unwrap().child.is_none() { + + let mut ready = false; + let mut running = false; + modify_service_data(name, |s| { + ready = s.child.is_some(); + running = s.running; + s.last_active = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); + }); + + if !ready && proxy.socket { let path = Path::new(&proxy.target); if path.exists() { fs::remove_file(path).await.unwrap(); } } - start_service(name, &proxy); - if !is_service_running(name) { - wait_for_service(&proxy).await; - set_service_running(name); + + if !running { + start_service(name, proxy); + wait_for_service(proxy).await; + modify_service_data(name, |s| s.running = true); } - set_service_last_active(name); + } } @@ -59,8 +62,8 @@ fn start_service(name: &String, proxy: &ProxyConf) -> bool { return; } let command = spawn.command.clone(); - let args = spawn.args.clone().unwrap_or(vec![]); - let envs = spawn.envs.clone().unwrap_or(vec![]); + let args = spawn.args.clone(); + let envs = spawn.envs.clone(); let spawned_child = create_child(command, args, envs); match spawned_child { Ok(child) => { @@ -74,7 +77,6 @@ fn start_service(name: &String, proxy: &ProxyConf) -> bool { } fn stop_service(name: &String) { - println!("Stopped"); modify_service_data(name, |s| { match s.child.as_mut() { Some(c) => { @@ -88,9 +90,25 @@ fn stop_service(name: &String) { } async fn wait_for_service(proxy: &ProxyConf) { - let path = Path::new(&proxy.target); - while !path.exists() { - sleep(Duration::from_millis(100)).await; + if proxy.socket { + + let path = Path::new(&proxy.target); + while !path.exists() { + sleep(Duration::from_millis(100)).await; + } + + } else { + + if let Some(address) = target_to_address(&proxy.target) { + loop { + sleep(Duration::from_millis(100)).await; + match TcpStream::connect(address) { + Ok(_) => break, + Err(_) => {} + } + } + } + } } @@ -99,7 +117,7 @@ pub async fn prepare_services() { for proxy in conf::get().proxy.into_iter() { hashmap.insert(proxy.0, ServiceData::new()); } - + let interval_duration = Duration::from_secs(10); thread::spawn(move || { loop {