use std::{
fmt::{self, Debug},
net::SocketAddr,
sync::{
Arc,
atomic::{AtomicU32, Ordering},
},
time::Duration,
};
use shadowsocks::{ServerConfig, net::ConnectOpts};
use tokio::sync::Mutex;
use crate::{config::ServerInstanceConfig, local::context::ServiceContext};
use super::server_stat::{Score, ServerStat, ServerStatData};
pub struct ServerScore {
stat_data: Mutex<ServerStat>,
score: AtomicU32,
}
impl ServerScore {
pub fn new(user_weight: f32, max_server_rtt: Duration, check_window: Duration) -> Self {
let max_server_rtt = max_server_rtt.as_millis() as u32;
assert!(max_server_rtt > 0);
Self {
stat_data: Mutex::new(ServerStat::new(user_weight, max_server_rtt, check_window)),
score: AtomicU32::new(u32::MAX),
}
}
pub fn score(&self) -> u32 {
self.score.load(Ordering::Acquire)
}
pub async fn push_score(&self, score: Score) -> u32 {
let updated_score = {
let mut stat = self.stat_data.lock().await;
stat.push_score(score)
};
self.score.store(updated_score, Ordering::Release);
updated_score
}
pub async fn push_score_fetch_statistic(&self, score: Score) -> (u32, ServerStatData) {
let (updated_score, data) = {
let mut stat = self.stat_data.lock().await;
(stat.push_score(score), *stat.data())
};
self.score.store(updated_score, Ordering::Release);
(updated_score, data)
}
pub async fn report_failure(&self) -> u32 {
self.push_score(Score::Errored).await
}
pub async fn stat_data(&self) -> ServerStatData {
*self.stat_data.lock().await.data()
}
}
impl Debug for ServerScore {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ServerScore").field("score", &self.score()).finish()
}
}
#[derive(Debug)]
pub struct ServerIdent {
tcp_score: ServerScore,
udp_score: ServerScore,
svr_cfg: ServerInstanceConfig,
connect_opts: ConnectOpts,
}
impl ServerIdent {
pub fn new(
context: Arc<ServiceContext>,
svr_cfg: ServerInstanceConfig,
max_server_rtt: Duration,
check_window: Duration,
) -> Self {
let mut connect_opts = context.connect_opts_ref().clone();
#[cfg(any(target_os = "linux", target_os = "android"))]
if let Some(fwmark) = svr_cfg.outbound_fwmark {
connect_opts.fwmark = Some(fwmark);
}
#[cfg(target_os = "freebsd")]
if let Some(user_cookie) = svr_cfg.outbound_user_cookie {
connect_opts.user_cookie = Some(user_cookie);
}
if let Some(bind_local_addr) = svr_cfg.outbound_bind_addr {
connect_opts.bind_local_addr = Some(SocketAddr::new(bind_local_addr, 0));
}
if let Some(ref bind_interface) = svr_cfg.outbound_bind_interface {
connect_opts.bind_interface = Some(bind_interface.clone());
}
Self {
tcp_score: ServerScore::new(svr_cfg.config.weight().tcp_weight(), max_server_rtt, check_window),
udp_score: ServerScore::new(svr_cfg.config.weight().udp_weight(), max_server_rtt, check_window),
svr_cfg,
connect_opts,
}
}
pub fn connect_opts_ref(&self) -> &ConnectOpts {
&self.connect_opts
}
pub fn server_config(&self) -> &ServerConfig {
&self.svr_cfg.config
}
pub fn server_config_mut(&mut self) -> &mut ServerConfig {
&mut self.svr_cfg.config
}
pub fn server_instance_config(&self) -> &ServerInstanceConfig {
&self.svr_cfg
}
pub fn tcp_score(&self) -> &ServerScore {
&self.tcp_score
}
pub fn udp_score(&self) -> &ServerScore {
&self.udp_score
}
}