#[cfg(not(target_env = "msvc"))] use tikv_jemallocator::Jemalloc; #[cfg(not(target_env = "msvc"))] #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; mod cert_file_reader; mod config; mod constants; mod error; mod log; use crate::{ config::{build_settings, parse_opts, ConfigToml, ConfigTomlReloader}, constants::CONFIG_WATCH_DELAY_SECS, log::*, }; use hot_reload::{ReloaderReceiver, ReloaderService}; use rpxy_lib::entrypoint; fn main() { init_logger(); let mut runtime_builder = tokio::runtime::Builder::new_multi_thread(); runtime_builder.enable_all(); runtime_builder.thread_name("rpxy"); let runtime = runtime_builder.build().unwrap(); runtime.block_on(async { // Initially load options let Ok(parsed_opts) = parse_opts() else { error!("Invalid toml file"); std::process::exit(1); }; if !parsed_opts.watch { if let Err(e) = rpxy_service_without_watcher(&parsed_opts.config_file_path, runtime.handle().clone()).await { error!("rpxy service existed: {e}"); std::process::exit(1); } } else { let (config_service, config_rx) = ReloaderService::::new( &parsed_opts.config_file_path, CONFIG_WATCH_DELAY_SECS, false, ) .await .unwrap(); tokio::select! { Err(e) = config_service.start() => { error!("config reloader service exited: {e}"); std::process::exit(1); } Err(e) = rpxy_service_with_watcher(config_rx, runtime.handle().clone()) => { error!("rpxy service existed: {e}"); std::process::exit(1); } } } }); } async fn rpxy_service_without_watcher( config_file_path: &str, runtime_handle: tokio::runtime::Handle, ) -> Result<(), anyhow::Error> { info!("Start rpxy service"); let config_toml = match ConfigToml::new(config_file_path) { Ok(v) => v, Err(e) => { error!("Invalid toml file: {e}"); std::process::exit(1); } }; let (proxy_conf, app_conf) = match build_settings(&config_toml) { Ok(v) => v, Err(e) => { error!("Invalid configuration: {e}"); return Err(anyhow::anyhow!(e)); } }; entrypoint(&proxy_conf, &app_conf, &runtime_handle) .await .map_err(|e| anyhow::anyhow!(e)) } async fn rpxy_service_with_watcher( mut config_rx: ReloaderReceiver, runtime_handle: tokio::runtime::Handle, ) -> Result<(), anyhow::Error> { info!("Start rpxy service with dynamic config reloader"); // Initial loading config_rx.changed().await?; let config_toml = config_rx.borrow().clone().unwrap(); let (mut proxy_conf, mut app_conf) = match build_settings(&config_toml) { Ok(v) => v, Err(e) => { error!("Invalid configuration: {e}"); return Err(anyhow::anyhow!(e)); } }; // Continuous monitoring loop { tokio::select! { _ = entrypoint(&proxy_conf, &app_conf, &runtime_handle) => { error!("rpxy entrypoint exited"); break; } _ = config_rx.changed() => { if config_rx.borrow().is_none() { error!("Something wrong in config reloader receiver"); break; } let config_toml = config_rx.borrow().clone().unwrap(); match build_settings(&config_toml) { Ok((p, a)) => { (proxy_conf, app_conf) = (p, a) }, Err(e) => { error!("Invalid configuration. Configuration does not updated: {e}"); continue; } }; info!("Configuration updated. Force to re-bind TCP/UDP sockets"); } else => break } } Err(anyhow::anyhow!("rpxy or continuous monitoring service exited")) }