mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-04-13 05:20:28 +08:00
feat(script): convert script execution to async and add timeout handling
This commit is contained in:
parent
e6a88cf9c9
commit
749b6c9e30
@ -303,7 +303,7 @@ async fn collect_profile_items() -> ProfileItems {
|
||||
}
|
||||
}
|
||||
|
||||
fn process_global_items(
|
||||
async fn process_global_items(
|
||||
mut config: Mapping,
|
||||
global_merge: ChainItem,
|
||||
global_script: ChainItem,
|
||||
@ -319,7 +319,7 @@ fn process_global_items(
|
||||
|
||||
if let ChainType::Script(script) = global_script.data {
|
||||
let mut logs = vec![];
|
||||
match use_script(script, &config, profile_name) {
|
||||
match use_script(script, config.clone(), profile_name.clone()).await {
|
||||
Ok((res_config, res_logs)) => {
|
||||
exists_keys.extend(use_keys(&res_config));
|
||||
config = res_config;
|
||||
@ -334,7 +334,7 @@ fn process_global_items(
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn process_profile_items(
|
||||
async fn process_profile_items(
|
||||
mut config: Mapping,
|
||||
mut exists_keys: Vec<String>,
|
||||
mut result_map: HashMap<String, ResultLog>,
|
||||
@ -364,7 +364,7 @@ fn process_profile_items(
|
||||
|
||||
if let ChainType::Script(script) = script_item.data {
|
||||
let mut logs = vec![];
|
||||
match use_script(script, &config, profile_name) {
|
||||
match use_script(script, config.clone(), profile_name.clone()).await {
|
||||
Ok((res_config, res_logs)) => {
|
||||
exists_keys.extend(use_keys(&res_config));
|
||||
config = res_config;
|
||||
@ -455,25 +455,26 @@ async fn merge_default_config(
|
||||
config
|
||||
}
|
||||
|
||||
fn apply_builtin_scripts(mut config: Mapping, clash_core: Option<String>, enable_builtin: bool) -> Mapping {
|
||||
async fn apply_builtin_scripts(mut config: Mapping, clash_core: Option<String>, enable_builtin: bool) -> Mapping {
|
||||
if enable_builtin {
|
||||
ChainItem::builtin()
|
||||
let items: Vec<_> = ChainItem::builtin()
|
||||
.into_iter()
|
||||
.filter(|(s, _)| s.is_support(clash_core.as_ref()))
|
||||
.map(|(_, c)| c)
|
||||
.for_each(|item| {
|
||||
logging!(debug, Type::Core, "run builtin script {}", item.uid);
|
||||
if let ChainType::Script(script) = item.data {
|
||||
match use_script(script, &config, &String::from("")) {
|
||||
Ok((res_config, _)) => {
|
||||
config = res_config;
|
||||
}
|
||||
Err(err) => {
|
||||
logging!(error, Type::Core, "builtin script error `{err}`");
|
||||
}
|
||||
.collect();
|
||||
for item in items {
|
||||
logging!(debug, Type::Core, "run builtin script {}", item.uid);
|
||||
if let ChainType::Script(script) = item.data {
|
||||
match use_script(script, config.clone(), String::from("")).await {
|
||||
Ok((res_config, _)) => {
|
||||
config = res_config;
|
||||
}
|
||||
Err(err) => {
|
||||
logging!(error, Type::Core, "builtin script error `{err}`");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config
|
||||
@ -621,7 +622,8 @@ pub async fn enhance() -> (Mapping, HashSet<String>, HashMap<String, ResultLog>)
|
||||
let profile_name = profile.profile_name;
|
||||
|
||||
// process globals
|
||||
let (config, exists_keys, result_map) = process_global_items(config, global_merge, global_script, &profile_name);
|
||||
let (config, exists_keys, result_map) =
|
||||
process_global_items(config, global_merge, global_script, &profile_name).await;
|
||||
|
||||
// process profile-specific items
|
||||
let (config, exists_keys, result_map) = process_profile_items(
|
||||
@ -634,7 +636,8 @@ pub async fn enhance() -> (Mapping, HashSet<String>, HashMap<String, ResultLog>)
|
||||
merge_item,
|
||||
script_item,
|
||||
&profile_name,
|
||||
);
|
||||
)
|
||||
.await;
|
||||
|
||||
// merge default clash config
|
||||
let config = merge_default_config(
|
||||
@ -650,7 +653,7 @@ pub async fn enhance() -> (Mapping, HashSet<String>, HashMap<String, ResultLog>)
|
||||
.await;
|
||||
|
||||
// builtin scripts
|
||||
let mut config = apply_builtin_scripts(config, clash_core, enable_builtin);
|
||||
let mut config = apply_builtin_scripts(config, clash_core, enable_builtin).await;
|
||||
|
||||
config = cleanup_proxy_groups(config);
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use crate::process::AsyncHandler;
|
||||
|
||||
use super::use_lowercase;
|
||||
use anyhow::{Error, Result};
|
||||
use boa_engine::{Context, JsString, JsValue, Source, native_function::NativeFunction};
|
||||
@ -10,11 +12,25 @@ use std::sync::Arc;
|
||||
const MAX_OUTPUTS: usize = 1000;
|
||||
const MAX_OUTPUT_SIZE: usize = 1024 * 1024; // 1MB
|
||||
const MAX_JSON_SIZE: usize = 10 * 1024 * 1024; // 10MB
|
||||
const MAX_LOOP_ITERATIONS: u64 = 10_000_000;
|
||||
const SCRIPT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
|
||||
|
||||
// TODO 使用引用改进上下相关处理,避免不必要 Clone
|
||||
pub fn use_script(script: String, config: &Mapping, name: &String) -> Result<(Mapping, Vec<(String, String)>)> {
|
||||
pub async fn use_script(script: String, config: Mapping, name: String) -> Result<(Mapping, Vec<(String, String)>)> {
|
||||
let handle = AsyncHandler::spawn_blocking(move || use_script_sync(script, &config, &name));
|
||||
match tokio::time::timeout(SCRIPT_TIMEOUT, handle).await {
|
||||
Ok(Ok(result)) => result,
|
||||
Ok(Err(join_err)) => Err(anyhow::anyhow!("script task panicked: {join_err}")),
|
||||
Err(_elapsed) => Err(anyhow::anyhow!("script execution timed out after {:?}", SCRIPT_TIMEOUT)),
|
||||
}
|
||||
}
|
||||
|
||||
fn use_script_sync(script: String, config: &Mapping, name: &String) -> Result<(Mapping, Vec<(String, String)>)> {
|
||||
let mut context = Context::default();
|
||||
|
||||
context
|
||||
.runtime_limits_mut()
|
||||
.set_loop_iteration_limit(MAX_LOOP_ITERATIONS);
|
||||
|
||||
let outputs = Arc::new(Mutex::new(vec![]));
|
||||
let total_size = Arc::new(Mutex::new(0usize));
|
||||
|
||||
@ -189,7 +205,7 @@ fn test_script() {
|
||||
|
||||
let config = &serde_yaml_ng::from_str(config).expect("Failed to parse test config YAML");
|
||||
let (config, results) =
|
||||
use_script(script.into(), config, &String::from("")).expect("Script execution should succeed in test");
|
||||
use_script_sync(script.into(), config, &String::from("")).expect("Script execution should succeed in test");
|
||||
|
||||
let _ = serde_yaml_ng::to_string(&config).expect("Failed to serialize config to YAML");
|
||||
let yaml_config_size = std::mem::size_of_val(&config);
|
||||
@ -243,7 +259,7 @@ fn test_memory_limits() {
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
let config = &serde_yaml_ng::from_str("test: value").expect("Failed to parse test YAML");
|
||||
let result = use_script(script.into(), config, &String::from(""));
|
||||
let result = use_script_sync(script.into(), config, &String::from(""));
|
||||
// 应该失败或被限制
|
||||
assert!(result.is_ok()); // 会被限制但不会 panic
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user