Added service spawning and stopping, yaml config
This commit is contained in:
parent
643c658f4c
commit
a31db191e8
38
Cargo.lock
generated
Normal file → Executable file
38
Cargo.lock
generated
Normal file → Executable file
|
@ -302,14 +302,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "odproxy"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"hyper",
|
||||
"hyperlocal",
|
||||
"lazy_static",
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tower",
|
||||
]
|
||||
|
||||
|
@ -437,6 +437,12 @@ dependencies = [
|
|||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
|
@ -463,6 +469,19 @@ dependencies = [
|
|||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
|
@ -563,15 +582,6 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.4.13"
|
||||
|
@ -638,6 +648,12 @@ version = "1.0.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.0"
|
||||
|
|
4
Cargo.toml
Normal file → Executable file
4
Cargo.toml
Normal file → Executable file
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "odproxy"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
@ -11,8 +11,8 @@ hyper = { version = "0.14", features = ["full"] }
|
|||
tokio = { version = "1", features = ["full"] }
|
||||
tower = { version = "0.4", features = ["full"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_yaml = "0.9"
|
||||
lazy_static = "1.4.0"
|
||||
toml = "0.5.8"
|
||||
|
||||
[[bin]]
|
||||
name = "odproxy"
|
||||
|
|
13
conf.rs
Normal file → Executable file
13
conf.rs
Normal file → Executable file
|
@ -1,9 +1,9 @@
|
|||
use std::net::SocketAddr;
|
||||
use std::{fs::File, process::exit};
|
||||
use std::io::prelude::*;
|
||||
use toml::{de::from_str};
|
||||
use serde::Deserialize;
|
||||
use lazy_static::lazy_static;
|
||||
use serde_yaml::from_str;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref CONFIG: RootConf = load_config();
|
||||
|
@ -20,7 +20,8 @@ pub struct ProxyConf {
|
|||
pub hosts: Vec<String>,
|
||||
pub target: String,
|
||||
pub socket: Option<bool>,
|
||||
pub spawn: Option<SpawnConf>
|
||||
pub spawn: Option<SpawnConf>,
|
||||
pub timeout: Option<u64>
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
@ -31,16 +32,16 @@ pub struct SpawnConf {
|
|||
}
|
||||
|
||||
fn load_config() -> RootConf {
|
||||
let file = File::open("config.toml");
|
||||
let file = File::open("config.yml");
|
||||
if file.is_err() {
|
||||
println!("[!] Unable to read config file"); exit(-1);
|
||||
println!("[!] Config file was not found!"); exit(-1);
|
||||
}
|
||||
let mut contents = String::new();
|
||||
if file.unwrap().read_to_string(&mut contents).is_err() {
|
||||
println!("[!] Unable to read config file"); exit(-1);
|
||||
println!("[!] Unable to read config file!"); exit(-1);
|
||||
}
|
||||
match from_str(&contents) {
|
||||
Ok(conf) => conf,
|
||||
Err(_) => {println!("[!] Unable to parse config"); exit(0);}
|
||||
Err(_) => {println!("[!] Unable to parse config!"); exit(0);}
|
||||
}
|
||||
}
|
14
config.toml
14
config.toml
|
@ -1,14 +0,0 @@
|
|||
listen = "[::]:3000"
|
||||
|
||||
[[proxy]]
|
||||
hosts = [ "website.local.gd", "website-alt.local.gd" ]
|
||||
socket = true
|
||||
target = "./www.sock"
|
||||
[proxy.spawn]
|
||||
command = "/usr/bin/node"
|
||||
args = [ "./webserver/index.mjs" ]
|
||||
envs = [ ["PORT", "www.sock"] ]
|
||||
|
||||
[[proxy]]
|
||||
hosts = [ "git.local.gd" ]
|
||||
target = "http://192.168.0.3:80"
|
15
config.yml
Executable file
15
config.yml
Executable file
|
@ -0,0 +1,15 @@
|
|||
listen: "[::]:3000"
|
||||
|
||||
proxy:
|
||||
|
||||
- hosts: [ "website.local.gd", "website-alt.local.gd" ]
|
||||
socket: true
|
||||
target: "./www.sock"
|
||||
spawn:
|
||||
command: "/usr/bin/node"
|
||||
args: [ "./webserver/index.mjs" ]
|
||||
envs: [ ["PORT", "www.sock"] ]
|
||||
timeout: 10
|
||||
|
||||
- hosts: [ "git.local.gd" ]
|
||||
target: "http://192.168.0.3:80"
|
13
data.rs
Normal file → Executable file
13
data.rs
Normal file → Executable file
|
@ -13,19 +13,18 @@ lazy_static! {
|
|||
|
||||
pub struct ServiceData {
|
||||
pub child: Option<Child>,
|
||||
pub running: bool
|
||||
pub running: bool,
|
||||
pub last_active: u64
|
||||
}
|
||||
|
||||
impl ServiceData {
|
||||
pub fn new(child: Option<Child>) -> ServiceData {
|
||||
pub fn new() -> ServiceData {
|
||||
ServiceData {
|
||||
child,
|
||||
running: false
|
||||
child: None,
|
||||
running: false,
|
||||
last_active: 0
|
||||
}
|
||||
}
|
||||
pub fn set_running(&mut self, running: bool) {
|
||||
self.running = running;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_proxy(host_index: Option<&usize>) -> Option<&ProxyConf> {
|
||||
|
|
75
main.rs
Normal file → Executable file
75
main.rs
Normal file → Executable file
|
@ -1,16 +1,16 @@
|
|||
mod conf;
|
||||
mod data;
|
||||
mod services;
|
||||
|
||||
use std::{str::FromStr, process::Command, path::Path, time::Duration};
|
||||
use conf::{ProxyConf, SpawnConf};
|
||||
use data::{HOST_MAP, SERVICES, ServiceData};
|
||||
use hyperlocal::{UnixClientExt};
|
||||
use tokio::{fs, time::sleep};
|
||||
use std::str::FromStr;
|
||||
use data::HOST_MAP;
|
||||
use hyperlocal::UnixClientExt;
|
||||
use services::check_service;
|
||||
use tower::make::Shared;
|
||||
|
||||
use hyper::{service::service_fn, Body, Client, Request, Response, Server};
|
||||
|
||||
use crate::conf::CONFIG;
|
||||
use crate::{conf::CONFIG, services::prepare_services};
|
||||
|
||||
async fn run(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
|
||||
|
||||
|
@ -20,7 +20,7 @@ async fn run(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
|
|||
match proxy {
|
||||
Some(p) => {
|
||||
|
||||
check_service(host_index.unwrap().clone(),p).await;
|
||||
check_service(host_index.unwrap().clone(), p).await;
|
||||
|
||||
// Create new Request
|
||||
let mut request_builder = Request::builder().method(req.method());
|
||||
|
@ -59,69 +59,10 @@ async fn run(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
|
|||
|
||||
}
|
||||
|
||||
fn set_service_running(index: usize) {
|
||||
SERVICES.lock().unwrap().get_mut(index).unwrap().set_running(true);
|
||||
}
|
||||
|
||||
fn is_service_running(index: usize) -> bool {
|
||||
SERVICES.lock().unwrap().get(index).unwrap().running
|
||||
}
|
||||
|
||||
async fn check_service(index: usize, proxy: &ProxyConf) {
|
||||
|
||||
match &proxy.spawn {
|
||||
Some(spawn) => {
|
||||
spawn_service(index, spawn);
|
||||
if !is_service_running(index) {
|
||||
wait_for_service(proxy).await;
|
||||
set_service_running(index);
|
||||
}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn spawn_service(index: usize, spawn: &SpawnConf) -> bool {
|
||||
match SERVICES.lock() {
|
||||
Ok(mut array) => {
|
||||
if array.get(index).is_none() {
|
||||
let command = spawn.command.clone();
|
||||
let args = spawn.args.clone().unwrap_or(vec![]);
|
||||
let envs = spawn.envs.clone().unwrap_or(vec![]);
|
||||
let spawned_child = Command::new(command).args(args).envs(envs).spawn();
|
||||
match spawned_child {
|
||||
Ok(child) => {
|
||||
array.insert(index, ServiceData::new(Some(child)));
|
||||
return true;
|
||||
},
|
||||
Err(_) => println!("Error while spawning process!")
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_) => {}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async fn wait_for_service(proxy: &ProxyConf) {
|
||||
let path = Path::new(&proxy.target);
|
||||
while !path.exists() {
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
|
||||
for proxy in CONFIG.proxy.iter() {
|
||||
if proxy.socket.unwrap_or(false) {
|
||||
let path = Path::new(&proxy.target);
|
||||
if path.exists() {
|
||||
fs::remove_file(path).await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
prepare_services().await;
|
||||
|
||||
let make_service = Shared::new(service_fn(run));
|
||||
|
||||
|
|
127
services.rs
Executable file
127
services.rs
Executable file
|
@ -0,0 +1,127 @@
|
|||
use std::{process::{Command, Stdio, Child}, time::{Duration, SystemTime, UNIX_EPOCH}, path::Path, io::Error, thread};
|
||||
use tokio::{time::sleep, fs};
|
||||
|
||||
use crate::{data::{SERVICES, ServiceData}, conf::{ProxyConf, CONFIG}};
|
||||
|
||||
fn modify_service_data<F>(index: usize, modify_fn: F)
|
||||
where F: FnOnce(&mut ServiceData)
|
||||
{
|
||||
let mut vec = SERVICES.lock().unwrap();
|
||||
if let Some(service_data) = vec.get_mut(index) {
|
||||
modify_fn(service_data);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_service_running(index: usize) {
|
||||
modify_service_data(index, |s| {
|
||||
s.running = true;
|
||||
});
|
||||
}
|
||||
|
||||
fn set_service_last_active(index: usize) {
|
||||
modify_service_data(index, |s| {
|
||||
s.last_active = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
|
||||
});
|
||||
}
|
||||
|
||||
fn is_service_running(index: usize) -> bool {
|
||||
if let Some(service_data) = SERVICES.lock().unwrap().get(index) {
|
||||
service_data.running
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn check_service(index: usize, proxy: &ProxyConf) {
|
||||
|
||||
if proxy.spawn.is_some() {
|
||||
if proxy.socket.unwrap_or(false) && SERVICES.lock().unwrap().get(index).unwrap().child.is_none() {
|
||||
let path = Path::new(&proxy.target);
|
||||
if path.exists() {
|
||||
fs::remove_file(path).await.unwrap();
|
||||
}
|
||||
}
|
||||
start_service(index, proxy);
|
||||
if !is_service_running(index) {
|
||||
wait_for_service(proxy).await;
|
||||
set_service_running(index);
|
||||
}
|
||||
set_service_last_active(index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn start_service(index: usize, proxy: &ProxyConf) -> bool {
|
||||
let mut status = false;
|
||||
let spawn = proxy.spawn.as_ref().unwrap();
|
||||
modify_service_data(index, |s| {
|
||||
if s.child.is_some() {
|
||||
return;
|
||||
}
|
||||
let command = spawn.command.clone();
|
||||
let args = spawn.args.clone().unwrap_or(vec![]);
|
||||
let envs = spawn.envs.clone().unwrap_or(vec![]);
|
||||
let spawned_child = create_child(command, args, envs);
|
||||
match spawned_child {
|
||||
Ok(child) => {
|
||||
s.child = Some(child);
|
||||
status = true;
|
||||
},
|
||||
Err(_) => println!("Error while spawning process!")
|
||||
}
|
||||
});
|
||||
return status;
|
||||
}
|
||||
|
||||
fn stop_service(index: usize) {
|
||||
modify_service_data(index, |s| {
|
||||
match s.child.as_mut() {
|
||||
Some(c) => {
|
||||
c.kill().unwrap();
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
s.running = false;
|
||||
s.child = None;
|
||||
});
|
||||
}
|
||||
|
||||
async fn wait_for_service(proxy: &ProxyConf) {
|
||||
let path = Path::new(&proxy.target);
|
||||
while !path.exists() {
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn prepare_services() {
|
||||
for i in 0..CONFIG.proxy.len() {
|
||||
let mut vec = SERVICES.lock().unwrap();
|
||||
vec.insert(i, ServiceData::new());
|
||||
}
|
||||
|
||||
let interval_duration = Duration::from_secs(10);
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
|
||||
for (i, proxy) in CONFIG.proxy.iter().enumerate() {
|
||||
match proxy.timeout {
|
||||
Some(t) => {
|
||||
{
|
||||
let vec = SERVICES.lock().unwrap();
|
||||
let s = vec.get(i).unwrap();
|
||||
if !s.running || s.last_active+t > now {continue;}
|
||||
}
|
||||
stop_service(i);
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
thread::sleep(interval_duration);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn create_child(command: String, args: Vec<String>, envs: Vec<(String, String)>) -> Result<Child, Error> {
|
||||
let stdio = Stdio::piped();
|
||||
return Command::new(command).args(args).envs(envs).stdout(stdio).spawn();
|
||||
}
|
Loading…
Reference in a new issue