mirror of
https://github.com/StarCitizenToolBox/app.git
synced 2026-01-13 19:50:28 +00:00
feat: use rust impl checkHost
This commit is contained in:
parent
3c07d12ee9
commit
da7c4b958d
@ -1,7 +1,6 @@
|
|||||||
import 'package:starcitizen_doctor/api/api.dart';
|
import 'package:starcitizen_doctor/api/api.dart';
|
||||||
import 'package:starcitizen_doctor/common/io/doh_client.dart';
|
import 'package:starcitizen_doctor/common/io/doh_client.dart';
|
||||||
import 'package:starcitizen_doctor/common/io/rs_http.dart';
|
import 'package:starcitizen_doctor/common/rust/api/http_api.dart' as rust_http;
|
||||||
import 'package:starcitizen_doctor/common/rust/http_package.dart';
|
|
||||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||||
|
|
||||||
class URLConf {
|
class URLConf {
|
||||||
@ -43,13 +42,19 @@ class URLConf {
|
|||||||
// 使用 DNS 获取可用列表
|
// 使用 DNS 获取可用列表
|
||||||
final gitApiList = _genFinalList(await dnsLookupTxt("git.dns.scbox.org"));
|
final gitApiList = _genFinalList(await dnsLookupTxt("git.dns.scbox.org"));
|
||||||
dPrint("DNS gitApiList ==== $gitApiList");
|
dPrint("DNS gitApiList ==== $gitApiList");
|
||||||
final fasterGit = await getFasterUrl(gitApiList, "git");
|
final fasterGit = await rust_http.getFasterUrl(
|
||||||
|
urls: gitApiList,
|
||||||
|
pathSuffix: "/SCToolBox/Api/raw/branch/main/sc_doctor/version.json",
|
||||||
|
);
|
||||||
dPrint("gitApiList.Faster ==== $fasterGit");
|
dPrint("gitApiList.Faster ==== $fasterGit");
|
||||||
if (fasterGit != null) {
|
if (fasterGit != null) {
|
||||||
gitApiHome = fasterGit;
|
gitApiHome = fasterGit;
|
||||||
}
|
}
|
||||||
final newsApiList = _genFinalList(await dnsLookupTxt("news.dns.scbox.org"));
|
final newsApiList = _genFinalList(await dnsLookupTxt("news.dns.scbox.org"));
|
||||||
final fasterNews = await getFasterUrl(newsApiList, "news");
|
final fasterNews = await rust_http.getFasterUrl(
|
||||||
|
urls: newsApiList,
|
||||||
|
pathSuffix: "/api/latest",
|
||||||
|
);
|
||||||
dPrint("DNS newsApiList ==== $newsApiList");
|
dPrint("DNS newsApiList ==== $newsApiList");
|
||||||
dPrint("newsApiList.Faster ==== $fasterNews");
|
dPrint("newsApiList.Faster ==== $fasterNews");
|
||||||
if (fasterNews != null) {
|
if (fasterNews != null) {
|
||||||
@ -62,53 +67,12 @@ class URLConf {
|
|||||||
static Future<List<String>> dnsLookupTxt(String host) async {
|
static Future<List<String>> dnsLookupTxt(String host) async {
|
||||||
if (await Api.isUseInternalDNS()) {
|
if (await Api.isUseInternalDNS()) {
|
||||||
dPrint("[URLConf] use internal DNS LookupTxt $host");
|
dPrint("[URLConf] use internal DNS LookupTxt $host");
|
||||||
return RSHttp.dnsLookupTxt(host);
|
return rust_http.dnsLookupTxt(host: host);
|
||||||
}
|
}
|
||||||
dPrint("[URLConf] use DOH LookupTxt $host");
|
dPrint("[URLConf] use DOH LookupTxt $host");
|
||||||
return (await DohClient.resolveTXT(host)) ?? [];
|
return (await DohClient.resolveTXT(host)) ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<String?> getFasterUrl(List<String> urls, String mode) async {
|
|
||||||
String firstUrl = "";
|
|
||||||
int callLen = 0;
|
|
||||||
|
|
||||||
void onCall(RustHttpResponse? response, String url) {
|
|
||||||
callLen++;
|
|
||||||
if (response != null && response.statusCode == 200 && firstUrl.isEmpty) {
|
|
||||||
firstUrl = url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var url in urls) {
|
|
||||||
var reqUrl = url;
|
|
||||||
switch (mode) {
|
|
||||||
case "git":
|
|
||||||
reqUrl = "$url/SCToolBox/Api/raw/branch/main/sc_doctor/version.json";
|
|
||||||
break;
|
|
||||||
case "news":
|
|
||||||
reqUrl = "$url/api/latest";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
RSHttp.head(reqUrl).then(
|
|
||||||
(resp) => onCall(resp, url),
|
|
||||||
onError: (err) {
|
|
||||||
callLen++;
|
|
||||||
dPrint("RSHttp.head error $err");
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
await Future.delayed(const Duration(milliseconds: 16));
|
|
||||||
if (firstUrl.isNotEmpty) {
|
|
||||||
return firstUrl;
|
|
||||||
}
|
|
||||||
if (callLen == urls.length && firstUrl.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<String> _genFinalList(List<String> sList) {
|
static List<String> _genFinalList(List<String> sList) {
|
||||||
List<String> list = [];
|
List<String> list = [];
|
||||||
for (var ll in sList) {
|
for (var ll in sList) {
|
||||||
|
|||||||
@ -34,4 +34,19 @@ Future<List<String>> dnsLookupTxt({required String host}) =>
|
|||||||
Future<List<String>> dnsLookupIps({required String host}) =>
|
Future<List<String>> dnsLookupIps({required String host}) =>
|
||||||
RustLib.instance.api.crateApiHttpApiDnsLookupIps(host: host);
|
RustLib.instance.api.crateApiHttpApiDnsLookupIps(host: host);
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
Future<String?> getFasterUrl({
|
||||||
|
required List<String> urls,
|
||||||
|
String? pathSuffix,
|
||||||
|
}) => RustLib.instance.api.crateApiHttpApiGetFasterUrl(
|
||||||
|
urls: urls,
|
||||||
|
pathSuffix: pathSuffix,
|
||||||
|
);
|
||||||
|
|
||||||
enum MyMethod { options, gets, post, put, delete, head, trace, connect, patch }
|
enum MyMethod { options, gets, post, put, delete, head, trace, connect, patch }
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
import '../frb_generated.dart';
|
import '../frb_generated.dart';
|
||||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||||
|
|
||||||
// These functions are ignored because they are not marked as `pub`: `get_process_path`
|
|
||||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `fmt`
|
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `fmt`
|
||||||
|
|
||||||
Future<void> sendNotify({
|
Future<void> sendNotify({
|
||||||
|
|||||||
@ -69,7 +69,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
|||||||
String get codegenVersion => '2.11.1';
|
String get codegenVersion => '2.11.1';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get rustContentHash => 1227557070;
|
int get rustContentHash => -518970253;
|
||||||
|
|
||||||
static const kDefaultExternalLibraryLoaderConfig =
|
static const kDefaultExternalLibraryLoaderConfig =
|
||||||
ExternalLibraryLoaderConfig(
|
ExternalLibraryLoaderConfig(
|
||||||
@ -95,6 +95,11 @@ abstract class RustLibApi extends BaseApi {
|
|||||||
bool? withCustomDns,
|
bool? withCustomDns,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Future<String?> crateApiHttpApiGetFasterUrl({
|
||||||
|
required List<String> urls,
|
||||||
|
String? pathSuffix,
|
||||||
|
});
|
||||||
|
|
||||||
Future<List<ProcessInfo>> crateApiWin32ApiGetProcessListByName({
|
Future<List<ProcessInfo>> crateApiWin32ApiGetProcessListByName({
|
||||||
required String processName,
|
required String processName,
|
||||||
});
|
});
|
||||||
@ -288,6 +293,39 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> crateApiHttpApiGetFasterUrl({
|
||||||
|
required List<String> urls,
|
||||||
|
String? pathSuffix,
|
||||||
|
}) {
|
||||||
|
return handler.executeNormal(
|
||||||
|
NormalTask(
|
||||||
|
callFfi: (port_) {
|
||||||
|
var arg0 = cst_encode_list_String(urls);
|
||||||
|
var arg1 = cst_encode_opt_String(pathSuffix);
|
||||||
|
return wire.wire__crate__api__http_api__get_faster_url(
|
||||||
|
port_,
|
||||||
|
arg0,
|
||||||
|
arg1,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
codec: DcoCodec(
|
||||||
|
decodeSuccessData: dco_decode_opt_String,
|
||||||
|
decodeErrorData: dco_decode_AnyhowException,
|
||||||
|
),
|
||||||
|
constMeta: kCrateApiHttpApiGetFasterUrlConstMeta,
|
||||||
|
argValues: [urls, pathSuffix],
|
||||||
|
apiImpl: this,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskConstMeta get kCrateApiHttpApiGetFasterUrlConstMeta =>
|
||||||
|
const TaskConstMeta(
|
||||||
|
debugName: "get_faster_url",
|
||||||
|
argNames: ["urls", "pathSuffix"],
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<ProcessInfo>> crateApiWin32ApiGetProcessListByName({
|
Future<List<ProcessInfo>> crateApiWin32ApiGetProcessListByName({
|
||||||
required String processName,
|
required String processName,
|
||||||
|
|||||||
@ -762,6 +762,38 @@ class RustLibWire implements BaseWire {
|
|||||||
)
|
)
|
||||||
>();
|
>();
|
||||||
|
|
||||||
|
void wire__crate__api__http_api__get_faster_url(
|
||||||
|
int port_,
|
||||||
|
ffi.Pointer<wire_cst_list_String> urls,
|
||||||
|
ffi.Pointer<wire_cst_list_prim_u_8_strict> path_suffix,
|
||||||
|
) {
|
||||||
|
return _wire__crate__api__http_api__get_faster_url(
|
||||||
|
port_,
|
||||||
|
urls,
|
||||||
|
path_suffix,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _wire__crate__api__http_api__get_faster_urlPtr =
|
||||||
|
_lookup<
|
||||||
|
ffi.NativeFunction<
|
||||||
|
ffi.Void Function(
|
||||||
|
ffi.Int64,
|
||||||
|
ffi.Pointer<wire_cst_list_String>,
|
||||||
|
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||||
|
)
|
||||||
|
>
|
||||||
|
>('frbgen_starcitizen_doctor_wire__crate__api__http_api__get_faster_url');
|
||||||
|
late final _wire__crate__api__http_api__get_faster_url =
|
||||||
|
_wire__crate__api__http_api__get_faster_urlPtr
|
||||||
|
.asFunction<
|
||||||
|
void Function(
|
||||||
|
int,
|
||||||
|
ffi.Pointer<wire_cst_list_String>,
|
||||||
|
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||||
|
)
|
||||||
|
>();
|
||||||
|
|
||||||
void wire__crate__api__win32_api__get_process_list_by_name(
|
void wire__crate__api__win32_api__get_process_list_by_name(
|
||||||
int port_,
|
int port_,
|
||||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> process_name,
|
ffi.Pointer<wire_cst_list_prim_u_8_strict> process_name,
|
||||||
@ -1312,6 +1344,13 @@ final class wire_cst_list_record_string_string extends ffi.Struct {
|
|||||||
external int len;
|
external int len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class wire_cst_list_String extends ffi.Struct {
|
||||||
|
external ffi.Pointer<ffi.Pointer<wire_cst_list_prim_u_8_strict>> ptr;
|
||||||
|
|
||||||
|
@ffi.Int32()
|
||||||
|
external int len;
|
||||||
|
}
|
||||||
|
|
||||||
final class wire_cst_rsi_launcher_asar_data extends ffi.Struct {
|
final class wire_cst_rsi_launcher_asar_data extends ffi.Struct {
|
||||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> asar_path;
|
external ffi.Pointer<wire_cst_list_prim_u_8_strict> asar_path;
|
||||||
|
|
||||||
@ -1327,13 +1366,6 @@ final class wire_cst_list_prim_u_8_loose extends ffi.Struct {
|
|||||||
external int len;
|
external int len;
|
||||||
}
|
}
|
||||||
|
|
||||||
final class wire_cst_list_String extends ffi.Struct {
|
|
||||||
external ffi.Pointer<ffi.Pointer<wire_cst_list_prim_u_8_strict>> ptr;
|
|
||||||
|
|
||||||
@ffi.Int32()
|
|
||||||
external int len;
|
|
||||||
}
|
|
||||||
|
|
||||||
final class wire_cst_process_info extends ffi.Struct {
|
final class wire_cst_process_info extends ffi.Struct {
|
||||||
@ffi.Uint32()
|
@ffi.Uint32()
|
||||||
external int pid;
|
external int pid;
|
||||||
|
|||||||
@ -30,42 +30,37 @@ class SplashUI extends HookConsumerWidget {
|
|||||||
return null;
|
return null;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return makeDefaultPage(context,
|
return makeDefaultPage(
|
||||||
content: Center(
|
context,
|
||||||
child: Column(
|
content: Center(
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
Image.asset("assets/app_logo.png", width: 192, height: 192),
|
children: [
|
||||||
const SizedBox(height: 32),
|
Image.asset("assets/app_logo.png", width: 192, height: 192),
|
||||||
const ProgressRing(),
|
const SizedBox(height: 32),
|
||||||
const SizedBox(height: 32),
|
const ProgressRing(),
|
||||||
if (step == 0) Text(S.current.app_splash_checking_availability),
|
const SizedBox(height: 32),
|
||||||
if (step == 1) Text(S.current.app_splash_checking_for_updates),
|
if (step == 0) Text(S.current.app_splash_checking_availability),
|
||||||
if (step == 2) Text(S.current.app_splash_almost_done),
|
if (step == 1) Text(S.current.app_splash_checking_for_updates),
|
||||||
],
|
if (step == 2) Text(S.current.app_splash_almost_done),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
automaticallyImplyLeading: false,
|
),
|
||||||
titleRow: Align(
|
automaticallyImplyLeading: false,
|
||||||
alignment: AlignmentDirectional.centerStart,
|
titleRow: Align(
|
||||||
child: Row(
|
alignment: AlignmentDirectional.centerStart,
|
||||||
children: [
|
child: Row(
|
||||||
Image.asset(
|
children: [
|
||||||
"assets/app_logo_mini.png",
|
Image.asset("assets/app_logo_mini.png", width: 20, height: 20, fit: BoxFit.cover),
|
||||||
width: 20,
|
const SizedBox(width: 12),
|
||||||
height: 20,
|
Text(S.current.app_index_version_info(ConstConf.appVersion, ConstConf.isMSE ? "" : " Dev")),
|
||||||
fit: BoxFit.cover,
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
),
|
||||||
Text(S.current.app_index_version_info(
|
);
|
||||||
ConstConf.appVersion, ConstConf.isMSE ? "" : " Dev"))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initApp(BuildContext context, AppGlobalModel appModel,
|
void _initApp(BuildContext context, AppGlobalModel appModel, ValueNotifier<int> stepState, WidgetRef ref) async {
|
||||||
ValueNotifier<int> stepState, WidgetRef ref) async {
|
|
||||||
await appModel.initApp();
|
await appModel.initApp();
|
||||||
final appConf = await Hive.openBox("app_conf");
|
final appConf = await Hive.openBox("app_conf");
|
||||||
final v = appConf.get("splash_alert_info_version", defaultValue: 0);
|
final v = appConf.get("splash_alert_info_version", defaultValue: 0);
|
||||||
@ -92,11 +87,11 @@ class SplashUI extends HookConsumerWidget {
|
|||||||
|
|
||||||
Future<void> _showAlert(BuildContext context, Box<dynamic> appConf) async {
|
Future<void> _showAlert(BuildContext context, Box<dynamic> appConf) async {
|
||||||
final userOk = await showConfirmDialogs(
|
final userOk = await showConfirmDialogs(
|
||||||
context,
|
context,
|
||||||
S.current.app_splash_dialog_u_a_p_p,
|
S.current.app_splash_dialog_u_a_p_p,
|
||||||
MarkdownWidget(data: S.current.app_splash_dialog_u_a_p_p_content),
|
MarkdownWidget(data: S.current.app_splash_dialog_u_a_p_p_content),
|
||||||
constraints:
|
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .5),
|
||||||
BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .5));
|
);
|
||||||
if (userOk) {
|
if (userOk) {
|
||||||
await appConf.put("splash_alert_info_version", _alertInfoVersion);
|
await appConf.put("splash_alert_info_version", _alertInfoVersion);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -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>> {
|
pub async fn dns_lookup_ips(host: String) -> anyhow::Result<Vec<String>> {
|
||||||
http_package::dns_lookup_ips(host).await
|
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
|
||||||
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
|
|||||||
default_rust_auto_opaque = RustAutoOpaqueNom,
|
default_rust_auto_opaque = RustAutoOpaqueNom,
|
||||||
);
|
);
|
||||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1";
|
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
|
// 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(
|
fn wire__crate__api__win32_api__get_process_list_by_name_impl(
|
||||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||||
process_name: impl CstDecode<String>,
|
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)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_process_list_by_name(
|
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_process_list_by_name(
|
||||||
port_: i64,
|
port_: i64,
|
||||||
|
|||||||
@ -9,6 +9,8 @@ use std::str::FromStr;
|
|||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
@ -183,3 +185,90 @@ fn _mix_header(
|
|||||||
req = req.headers(dh.clone());
|
req = req.headers(dh.clone());
|
||||||
req
|
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)
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user