Skip to content

Commit

Permalink
reload api (#220)
Browse files Browse the repository at this point in the history
  • Loading branch information
ibigbug authored Dec 25, 2023
1 parent 29d357f commit 73fe07e
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 85 deletions.
44 changes: 0 additions & 44 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion clash_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ ipnet = "2.9"
url = "2.5"
regex = "1"
byteorder = "1.5"
state = "0.6"
lru_time_cache = "0.11"
hyper = { version = "0.14", features = ["http1","http2","client", "server", "tcp"] }
http = { version = "0.2.11" }
Expand Down
83 changes: 72 additions & 11 deletions clash_lib/src/app/api/handlers/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
use std::sync::Arc;
use std::{path::PathBuf, sync::Arc};

use axum::{
extract::{Query, State},
response::IntoResponse,
routing::get,
Json, Router,
};

use axum::{extract::State, response::IntoResponse, routing::get, Json, Router};
use http::StatusCode;
use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
Expand Down Expand Up @@ -52,7 +58,7 @@ async fn get_configs(State(state): State<ConfigState>) -> impl IntoResponse {

let ports = inbound_manager.get_ports();

axum::response::Json(ConfigRequest {
axum::response::Json(PatchConfigRequest {
port: ports.port,
socks_port: ports.socks_port,
redir_port: ports.redir_port,
Expand All @@ -73,16 +79,71 @@ async fn get_configs(State(state): State<ConfigState>) -> impl IntoResponse {
})
}

async fn update_configs() -> impl IntoResponse {
(
StatusCode::NOT_IMPLEMENTED,
axum::response::Json("don't do this please"),
)
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
struct UpdateConfigRequest {
path: Option<String>,
payload: Option<String>,
}

#[derive(Serialize, Deserialize)]
struct UploadConfigQuery {
force: Option<bool>,
}

async fn update_configs(
_q: Query<UploadConfigQuery>,
State(state): State<ConfigState>,
Json(req): Json<UpdateConfigRequest>,
) -> impl IntoResponse {
let g = state.global_state.lock().await;
match (req.path, req.payload) {
(_, Some(payload)) => {
let msg = format!("config reloading from payload");
let cfg = crate::Config::Str(payload);
match g.reload_tx.send(cfg).await {
Ok(_) => (StatusCode::ACCEPTED, msg).into_response(),
Err(_) => (
StatusCode::INTERNAL_SERVER_ERROR,
"could not signal config reload",
)
.into_response(),
}
}
(Some(mut path), None) => {
if !PathBuf::from(&path).is_absolute() {
path = PathBuf::from(g.cwd.clone())
.join(path)
.to_string_lossy()
.to_string();
}
if !PathBuf::from(&path).exists() {
return (
StatusCode::BAD_REQUEST,
format!("config file {} not found", path),
)
.into_response();
}

let msg = format!("config reloading from file {}", path);
let cfg: crate::Config = crate::Config::File(path);
match g.reload_tx.send(cfg).await {
Ok(_) => (StatusCode::ACCEPTED, msg).into_response(),

Err(_) => (
StatusCode::INTERNAL_SERVER_ERROR,
"could not signal config reload",
)
.into_response(),
}
}
(None, None) => (StatusCode::BAD_REQUEST, "no path or payload provided").into_response(),
}
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
struct ConfigRequest {
struct PatchConfigRequest {
port: Option<u16>,
socks_port: Option<u16>,
redir_port: Option<u16>,
Expand All @@ -95,7 +156,7 @@ struct ConfigRequest {
allow_lan: Option<bool>,
}

impl ConfigRequest {
impl PatchConfigRequest {
fn rebuild_listeners(&self) -> bool {
self.port.is_some()
|| self.socks_port.is_some()
Expand All @@ -108,7 +169,7 @@ impl ConfigRequest {

async fn patch_configs(
State(state): State<ConfigState>,
Json(payload): Json<ConfigRequest>,
Json(payload): Json<PatchConfigRequest>,
) -> impl IntoResponse {
if payload.allow_lan.is_some() {
warn!("setting allow_lan doesn't do anything. please set bind_address to a LAN address instead.");
Expand Down
35 changes: 20 additions & 15 deletions clash_lib/src/common/mmdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ impl MMDB {
http_client: HttpClient,
) -> Result<MMDB, Error> {
debug!("mmdb path: {}", path.as_ref().to_string_lossy());
let reader = Self::load_mmdb(path, download_url, &http_client).await?;
Ok(Self { reader })
}

async fn load_mmdb<P: AsRef<Path>>(
path: P,
download_url: Option<String>,
http_client: &HttpClient,
) -> Result<maxminddb::Reader<Vec<u8>>, Error> {
let mmdb_file = path.as_ref().to_path_buf();

if !mmdb_file.exists() {
Expand All @@ -43,7 +51,7 @@ impl MMDB {
}

match maxminddb::Reader::open_readfile(&path) {
Ok(r) => Ok(MMDB { reader: r }),
Ok(r) => Ok(r),
Err(e) => match e {
maxminddb::MaxMindDBError::InvalidDatabaseError(_)
| maxminddb::MaxMindDBError::IoError(_) => {
Expand All @@ -62,15 +70,13 @@ impl MMDB {
.map_err(|x| {
Error::InvalidConfig(format!("mmdb download failed: {}", x))
})?;
Ok(MMDB {
reader: maxminddb::Reader::open_readfile(&path).map_err(|x| {
Error::InvalidConfig(format!(
"cant open mmdb `{}`: {}",
path.as_ref().to_string_lossy(),
x.to_string()
))
})?,
})
Ok(maxminddb::Reader::open_readfile(&path).map_err(|x| {
Error::InvalidConfig(format!(
"cant open mmdb `{}`: {}",
path.as_ref().to_string_lossy(),
x.to_string()
))
})?)
} else {
return Err(Error::InvalidConfig(format!(
"mmdb `{}` not found and mmdb_download_url is not set",
Expand All @@ -89,8 +95,8 @@ impl MMDB {
}
}

#[async_recursion(?Send)]
async fn download<P: AsRef<Path>>(
#[async_recursion]
async fn download<P: AsRef<Path> + std::marker::Send>(
url: &str,
path: P,
http_client: &HttpClient,
Expand Down Expand Up @@ -129,10 +135,9 @@ impl MMDB {
Ok(())
}

pub fn lookup(&self, ip: IpAddr) -> anyhow::Result<geoip2::Country> {
pub fn lookup(&self, ip: IpAddr) -> std::io::Result<geoip2::Country> {
self.reader
.lookup(ip)
.lookup::<geoip2::Country>(ip)
.map_err(map_io_error)
.map_err(|x| x.into())
}
}
Loading

0 comments on commit 73fe07e

Please sign in to comment.