feat: use rust impl checkHost

This commit is contained in:
xkeyC
2025-11-25 21:19:36 +08:00
parent 3c07d12ee9
commit da7c4b958d
9 changed files with 276 additions and 94 deletions

View File

@@ -59,3 +59,17 @@ pub async fn dns_lookup_txt(host: String) -> anyhow::Result<Vec<String>> {
pub async fn dns_lookup_ips(host: String) -> anyhow::Result<Vec<String>> {
http_package::dns_lookup_ips(host).await
}
/// Get the fastest URL from a list of URLs by testing them concurrently.
/// Returns the first URL that responds successfully, canceling other requests.
///
/// # Arguments
/// * `urls` - List of base URLs to test
/// * `path_suffix` - Optional path suffix to append to each URL (e.g., "/api/version")
/// If None, tests the base URL directly
pub async fn get_faster_url(
urls: Vec<String>,
path_suffix: Option<String>,
) -> anyhow::Result<Option<String>> {
http_package::get_faster_url(urls, path_suffix).await
}

View File

@@ -37,7 +37,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
default_rust_auto_opaque = RustAutoOpaqueNom,
);
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1";
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1227557070;
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -518970253;
// Section: executor
@@ -156,6 +156,33 @@ fn wire__crate__api__http_api__fetch_impl(
},
)
}
fn wire__crate__api__http_api__get_faster_url_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
urls: impl CstDecode<Vec<String>>,
path_suffix: impl CstDecode<Option<String>>,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "get_faster_url",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let api_urls = urls.cst_decode();
let api_path_suffix = path_suffix.cst_decode();
move |context| async move {
transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>(
(move || async move {
let output_ok =
crate::api::http_api::get_faster_url(api_urls, api_path_suffix).await?;
Ok(output_ok)
})()
.await,
)
}
},
)
}
fn wire__crate__api__win32_api__get_process_list_by_name_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
process_name: impl CstDecode<String>,
@@ -1684,6 +1711,15 @@ mod io {
)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__http_api__get_faster_url(
port_: i64,
urls: *mut wire_cst_list_String,
path_suffix: *mut wire_cst_list_prim_u_8_strict,
) {
wire__crate__api__http_api__get_faster_url_impl(port_, urls, path_suffix)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_process_list_by_name(
port_: i64,

View File

@@ -9,6 +9,8 @@ use std::str::FromStr;
use std::sync::{Arc, RwLock};
use std::time::Duration;
use url::Url;
use tokio::sync::mpsc;
use tokio::task::JoinHandle;
#[derive(Debug)]
#[allow(non_camel_case_types)]
@@ -183,3 +185,90 @@ fn _mix_header(
req = req.headers(dh.clone());
req
}
/// Get the fastest URL from a list of URLs by testing them concurrently.
/// Returns the first URL that responds successfully (HTTP 200), and cancels all other pending requests.
///
/// # Arguments
/// * `urls` - List of base URLs to test
/// * `path_suffix` - Optional path suffix to append to each URL for testing
/// If None, tests the base URL directly
///
/// # Returns
/// * `Ok(Some(url))` - The first base URL that responded successfully
/// * `Ok(None)` - All URLs failed or the list was empty
pub async fn get_faster_url(
urls: Vec<String>,
path_suffix: Option<String>,
) -> anyhow::Result<Option<String>> {
if urls.is_empty() {
return Ok(None);
}
let (tx, mut rx) = mpsc::channel(urls.len());
let mut handles: Vec<JoinHandle<()>> = Vec::new();
// Spawn a task for each URL
for url in urls.iter() {
let url_clone = url.clone();
let tx_clone = tx.clone();
let path_suffix_clone = path_suffix.clone();
let handle = tokio::spawn(async move {
// Build request URL
let req_url = if let Some(suffix) = path_suffix_clone {
format!("{}{}", url_clone, suffix)
} else {
url_clone.clone()
};
// Perform HEAD request
let result = fetch(
Method::HEAD,
req_url,
None,
None,
None,
Some(false),
).await;
// Send result back through channel
if let Ok(response) = result {
if response.status_code == 200 {
let _ = tx_clone.send(Some(url_clone)).await;
return;
}
}
// Send None if request failed
let _ = tx_clone.send(None).await;
});
handles.push(handle);
}
// Drop the original sender so the channel closes when all tasks complete
drop(tx);
// Wait for the first successful response
let mut completed = 0;
let total = urls.len();
while let Some(result) = rx.recv().await {
if let Some(url) = result {
// Found a successful URL - abort all other tasks
for handle in handles {
handle.abort();
}
return Ok(Some(url));
}
completed += 1;
if completed >= total {
// All requests completed without success
break;
}
}
Ok(None)
}