From 200463dcd24bcdd4bd62fa0bc8c42670e787aeb4 Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Sun, 12 May 2024 18:44:52 +0800 Subject: [PATCH] feat(local): load servers from remote before running --- crates/shadowsocks-service/src/config.rs | 2 +- src/service/local.rs | 147 +++++++++++++---------- 2 files changed, 86 insertions(+), 63 deletions(-) diff --git a/crates/shadowsocks-service/src/config.rs b/crates/shadowsocks-service/src/config.rs index 7db2de114483..5ad67efc06f0 100644 --- a/crates/shadowsocks-service/src/config.rs +++ b/crates/shadowsocks-service/src/config.rs @@ -2557,7 +2557,7 @@ impl Config { } } - if self.config_type.is_server() && self.server.is_empty() { + if self.config_type.is_server() && self.server.is_empty() && self.online_config.is_none() { let err = Error::new( ErrorKind::MissingField, "missing any valid servers in configuration", diff --git a/src/service/local.rs b/src/service/local.rs index d942a060abd6..d3ff4bb37991 100644 --- a/src/service/local.rs +++ b/src/service/local.rs @@ -575,7 +575,7 @@ pub fn define_command_line_options(mut app: Command) -> Command { /// Create `Runtime` and `main` entry pub fn create(matches: &ArgMatches) -> Result<(Runtime, impl Future), ExitCode> { - let (config, runtime) = { + let (mut config, runtime) = { let config_path_opt = matches.get_one::("CONFIG").cloned().or_else(|| { if !matches.contains_id("SERVER_CONFIG") { match crate::config::get_default_config_path("local.json") { @@ -1000,6 +1000,20 @@ pub fn create(matches: &ArgMatches) -> Result<(Runtime, impl Future (Some(o.config_url), o.update_interval), @@ -1052,7 +1066,7 @@ pub fn create(matches: &ArgMatches) -> Result<(Runtime, impl Future { // continue. - trace!("server-reloader task exited"); + trace!("server-loader task task exited"); } } } @@ -1080,6 +1094,67 @@ struct ServerReloader { online_config_update_interval: Option, } +#[cfg(feature = "local-online-config")] +async fn get_online_config_servers( + online_config_url: &str, +) -> Result, Box> { + use log::warn; + + #[inline] + async fn get_online_config(online_config_url: &str) -> reqwest::Result { + let response = reqwest::get(online_config_url).await?; + if response.url().scheme() != "https" { + warn!( + "SIP008 suggests configuration URL should use https, but current URL is {}", + response.url().scheme() + ); + } + + // Content-Type: application/json; charset=utf-8 + // mandatory in standard SIP008 + match response.headers().get("Content-Type") { + Some(h) => { + if h != "application/json; charset=utf-8" { + warn!( + "SIP008 Content-Type must be \"application/json; charset=utf-8\", but found {}", + h.to_str().unwrap_or("[non-utf8-value]") + ); + } + } + None => { + warn!("missing Content-Type in SIP008 response from {}", online_config_url); + } + } + + response.text().await + } + + let body = match get_online_config(online_config_url).await { + Ok(b) => b, + Err(err) => { + error!( + "server-loader task failed to load from url: {}, error: {}", + online_config_url, err + ); + return Err(Box::new(err)); + } + }; + + // NOTE: ConfigType::Local will force verify local_address and local_port keys. SIP008 standard doesn't include those keys. + let online_config = match Config::load_from_str(&body, ConfigType::Server) { + Ok(c) => c, + Err(err) => { + error!( + "server-loader task failed to load from url: {}, error: {}", + online_config_url, err + ); + return Err(Box::new(err)); + } + }; + + Ok(online_config.server) +} + impl ServerReloader { async fn run_once(&self) -> Result<(), Box> { let start_time = Instant::now(); @@ -1092,7 +1167,7 @@ impl ServerReloader { Ok(c) => c, Err(err) => { error!( - "server-reloader failed to load from file: {}, error: {}", + "server-loader task failed to load from file: {}, error: {}", config_path.display(), err ); @@ -1105,60 +1180,8 @@ impl ServerReloader { // Load servers from online-config (SIP008) #[cfg(feature = "local-online-config")] if let Some(ref online_config_url) = self.online_config_url { - use log::warn; - - #[inline] - async fn get_online_config(online_config_url: &str) -> reqwest::Result { - let response = reqwest::get(online_config_url).await?; - if response.url().scheme() != "https" { - warn!( - "SIP008 suggests configuration URL should use https, but current URL is {}", - response.url().scheme() - ); - } - - // Content-Type: application/json; charset=utf-8 - // mandatory in standard SIP008 - match response.headers().get("Content-Type") { - Some(h) => { - if h != "application/json; charset=utf-8" { - warn!( - "SIP008 Content-Type must be \"application/json; charset=utf-8\", but found {}", - h.to_str().unwrap_or("[non-utf8-value]") - ); - } - } - None => { - warn!("missing Content-Type in SIP008 response from {}", online_config_url); - } - } - - response.text().await - } - - let body = match get_online_config(online_config_url).await { - Ok(b) => b, - Err(err) => { - error!( - "server-reloader failed to load from url: {}, error: {}", - online_config_url, err - ); - return Err(Box::new(err)); - } - }; - - // NOTE: ConfigType::Local will force verify local_address and local_port keys. SIP008 standard doesn't include those keys. - let mut online_config = match Config::load_from_str(&body, ConfigType::Server) { - Ok(c) => c, - Err(err) => { - error!( - "server-reloader failed to load from url: {}, error: {}", - online_config_url, err - ); - return Err(Box::new(err)); - } - }; - servers.append(&mut online_config.server); + let mut online_servers = get_online_config_servers(&online_config_url).await?; + servers.append(&mut online_servers); } let server_len = servers.len(); @@ -1188,14 +1211,14 @@ impl ServerReloader { let fetch_end_time = Instant::now(); if let Err(err) = self.balancer.reset_servers(servers).await { - error!("server-reloader {} servers but found error: {}", server_len, err); + error!("server-loader task {} servers but found error: {}", server_len, err); return Err(Box::new(err)); } let total_end_time = Instant::now(); info!( - "server-reloader reload from {} with {} servers, fetch costs: {:?}, total costs: {:?}", + "server-loader task reload from {} with {} servers, fetch costs: {:?}, total costs: {:?}", ConfigDisplay(&self), server_len, fetch_end_time - start_time, @@ -1212,7 +1235,7 @@ impl ServerReloader { let mut sigusr1 = signal(SignalKind::user_defined1()).expect("signal"); - debug!("server-reloader is now listening USR1"); + debug!("server-loader task is now listening USR1"); while sigusr1.recv().await.is_some() { let _ = self.run_once().await; @@ -1228,11 +1251,11 @@ impl ServerReloader { .online_config_update_interval .unwrap_or(Duration::from_secs(1 * 60 * 60)); - debug!("server-reloader updating in interval {:?}", update_interval); + debug!("server-loader task updating in interval {:?}", update_interval); loop { - let _ = self.run_once().await; time::sleep(update_interval).await; + let _ = self.run_once().await; } }