Added port target spawning support, improved config & session data

This commit is contained in:
Filip Znachor 2023-06-22 22:16:45 +02:00
parent cf94b742e4
commit b694c11d78
5 changed files with 128 additions and 41 deletions

67
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -21,7 +21,8 @@ pub struct RootConf {
pub struct ProxyConf {
pub hosts: Vec<String>,
pub target: String,
pub socket: Option<bool>,
#[serde(default)]
pub socket: bool,
pub spawn: Option<SpawnConf>,
pub timeout: Option<u64>
}
@ -29,8 +30,10 @@ pub struct ProxyConf {
#[derive(Debug, Deserialize, Clone)]
pub struct SpawnConf {
pub command: String,
pub args: Option<Vec<String>>,
pub envs: Option<Vec<(String, String)>>
#[serde(default)]
pub args: Vec<String>,
#[serde(default)]
pub envs: Vec<(String, String)>
}
fn load() -> RootConf {

View file

@ -26,9 +26,7 @@ async fn run(req: Request<Body>) -> Result<Response<Body>, 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<Body>) -> Result<Response<Body>, 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

View file

@ -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<SocketAddr> {
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<F>(name: &String, modify_fn: F)
where F: FnOnce(&mut ServiceData)
{
@ -12,41 +25,31 @@ fn modify_service_data<F>(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 {