使用 DNS 分流

This commit is contained in:
2024-02-07 22:19:43 +08:00
parent 95b4b8b947
commit 9ee02e9312
13 changed files with 247 additions and 70 deletions

View File

@ -8,6 +8,7 @@ import 'package:path_provider/path_provider.dart';
import 'package:starcitizen_doctor/api/analytics.dart';
import 'package:starcitizen_doctor/api/api.dart';
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
import 'package:starcitizen_doctor/common/io/rs_http.dart';
import 'package:starcitizen_doctor/common/rust/frb_generated.dart';
import 'package:starcitizen_doctor/data/app_version_data.dart';
import 'package:starcitizen_doctor/global_ui_model.dart';
@ -67,6 +68,7 @@ class AppConf {
/// check Rust bridge
await RustLib.init();
await RSHttp.init();
dPrint("---- rust bridge inited -----");
await SystemHelper.initPowershellPath();

View File

@ -1,5 +1,6 @@
import 'package:dio/dio.dart';
import 'package:starcitizen_doctor/base/ui_model.dart';
import 'package:starcitizen_doctor/common/io/rs_http.dart';
import 'package:starcitizen_doctor/common/rust/http_package.dart';
class URLConf {
/// HOME API
@ -7,83 +8,92 @@ class URLConf {
static String rssApiHome = "https://rss.sctoolbox.sccsgo.com";
static const String xkeycApiHome = "https://sctoolbox.xkeyc.com";
static bool isUsingFallback = false;
static bool isUrlCheckPass = false;
/// URLS
static String giteaAttachmentsUrl = "$gitApiHome/SCToolBox/Release";
static String gitlabLocalizationUrl =
static String get giteaAttachmentsUrl => "$gitApiHome/SCToolBox/Release";
static String get gitlabLocalizationUrl =>
"$gitApiHome/SCToolBox/LocalizationData";
static String apiRepoPath = "$gitApiHome/SCToolBox/api/raw/branch/main/";
static String gitlabApiPath = "https://$gitApiHome/api/v1/";
static String get apiRepoPath => "$gitApiHome/SCToolBox/api/raw/branch/main/";
static String webTranslateHomeUrl =
static String get gitlabApiPath => "https://$gitApiHome/api/v1/";
static String get webTranslateHomeUrl =>
"$gitApiHome/SCToolBox/ScWeb_Chinese_Translate/raw/branch/main/json/locales";
static String rssVideoUrl =
static String get rssVideoUrl =>
"$rssApiHome/bilibili/user/channel/27976358/290653";
static String rssTextUrl1 = "$rssApiHome/bilibili/user/article/40102960";
static String rssTextUrl2 =
static String get rssTextUrl1 => "$rssApiHome/bilibili/user/article/40102960";
static String get rssTextUrl2 =>
"$rssApiHome/baidu/tieba/user/%E7%81%AC%E7%81%ACG%E7%81%AC%E7%81%AC&";
static const feedbackUrl = "https://txc.qq.com/products/614843";
static const devReleaseUrl =
"https://git.sctoolbox.sccsgo.com/SCToolBox/Release/releases";
static String get devReleaseUrl => "$gitApiHome/SCToolBox/Release/releases";
static const _gitApiList = [
"https://git.sctoolbox.sccsgo.com",
"https://sctb-git.xkeyc.com"
];
static const _rssApiList = [
"https://rss.sctoolbox.sccsgo.com",
"https://rss.42kit.com"
];
static checkHost() async {
final dio = Dio(BaseOptions(connectTimeout: const Duration(seconds: 5)));
bool hasAvailable = false;
// 寻找可用的 git API
for (var value in _gitApiList) {
try {
final resp = await dio.head(value);
if (resp.statusCode == 200) {
dPrint("[URLConf].checkHost passed $value");
gitApiHome = value;
hasAvailable = true;
break;
}
isUsingFallback = true;
continue;
} catch (e) {
dPrint("[URLConf].checkHost $value Error= $e");
isUsingFallback = true;
continue;
}
static Future<bool> checkHost() async {
// 使用 DNS 获取可用列表
final gitApiList =
_genFinalList(await RSHttp.dnsLookupTxt("git.dns.scbox.org"));
dPrint("DNS gitApiList ==== $gitApiList");
final fasterGit = await getFasterUrl(gitApiList);
dPrint("gitApiList.Faster ==== $fasterGit");
if (fasterGit != null) {
gitApiHome = fasterGit;
}
// 寻找可用的 RSS API
for (var value in _rssApiList) {
try {
final resp = await dio.head(value);
if (resp.statusCode == 200) {
rssApiHome = value;
hasAvailable = true;
dPrint("[URLConf].checkHost passed $value");
break;
}
isUsingFallback = true;
continue;
} catch (e) {
dPrint("[URLConf].checkHost $value Error= $e");
isUsingFallback = true;
continue;
final rssApiList =
_genFinalList(await RSHttp.dnsLookupTxt("rss.dns.scbox.org"));
final fasterRss = await getFasterUrl(rssApiList);
dPrint("DNS rssApiList ==== $rssApiList");
dPrint("rssApiList.Faster ==== $fasterRss");
if (fasterRss != null) {
rssApiHome = fasterRss;
}
isUrlCheckPass = fasterGit != null && fasterRss != null;
return isUrlCheckPass;
}
static Future<String?> getFasterUrl(List<String> urls) async {
String firstUrl = "";
int callLen = 0;
void onCall(RustHttpResponse? response, String url) {
callLen++;
if (response != null && response.statusCode == 200 && firstUrl.isEmpty) {
firstUrl = url;
}
}
if (!hasAvailable) {
isUsingFallback = false;
for (var value in urls) {
RSHttp.head(value).then((resp) => onCall(resp, value), 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) {
List<String> list = [];
for (var ll in sList) {
final ssList = ll.split(",");
for (var value in ssList) {
list.add(value);
}
}
return list;
}
}

View File

@ -1,10 +1,19 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:starcitizen_doctor/common/conf/app_conf.dart';
import 'package:starcitizen_doctor/common/rust/api/http_api.dart' as rust_http;
import 'package:starcitizen_doctor/common/rust/api/http_api.dart';
import 'package:starcitizen_doctor/common/rust/http_package.dart';
class RSHttp {
static init() async {
await rust_http.setDefaultHeader(headers: {
"User-Agent":
"SCToolBox/${AppConf.appVersion} (${AppConf.appVersionCode}) ${AppConf.isMSE ? "" : " DEV"} RSHttp"
});
}
static Future<String> getText(String url,
{Map<String, String>? headers}) async {
final r = await rust_http.fetch(
@ -26,4 +35,15 @@ class RSHttp {
method: MyMethod.post, url: url, headers: headers, inputData: data);
return r.statusCode == 200;
}
static Future<RustHttpResponse> head(String url,
{Map<String, String>? headers}) async {
final r = await rust_http.fetch(
method: MyMethod.head, url: url, headers: headers);
return r;
}
static Future<List<String>> dnsLookupTxt(String host) async {
return await rust_http.dnsLookupTxt(host: host);
}
}

View File

@ -24,6 +24,9 @@ Future<RustHttpResponse> fetch(
inputData: inputData,
hint: hint);
Future<List<String>> dnsLookupTxt({required String host, dynamic hint}) =>
RustLib.instance.api.dnsLookupTxt(host: host, hint: hint);
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::rust_async::RwLock<reqwest :: Version>>
@sealed
class ReqwestVersion extends RustOpaque {

View File

@ -74,6 +74,8 @@ abstract class RustLibApi extends BaseApi {
required int connectionCount,
dynamic hint});
Future<List<String>> dnsLookupTxt({required String host, dynamic hint});
Future<RustHttpResponse> fetch(
{required MyMethod method,
required String url,
@ -160,6 +162,31 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
argNames: ["url", "savePath", "fileName", "connectionCount"],
);
@override
Future<List<String>> dnsLookupTxt({required String host, dynamic hint}) {
return handler.executeNormal(NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_String(host, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer,
funcId: 5, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_list_String,
decodeErrorData: null,
),
constMeta: kDnsLookupTxtConstMeta,
argValues: [host],
apiImpl: this,
hint: hint,
));
}
TaskConstMeta get kDnsLookupTxtConstMeta => const TaskConstMeta(
debugName: "dns_lookup_txt",
argNames: ["host"],
);
@override
Future<RustHttpResponse> fetch(
{required MyMethod method,
@ -283,6 +310,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return raw as int;
}
@protected
List<String> dco_decode_list_String(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return (raw as List<dynamic>).map(dco_decode_String).toList();
}
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
@ -473,6 +506,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return deserializer.buffer.getInt32();
}
@protected
List<String> sse_decode_list_String(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
var len_ = sse_decode_i_32(deserializer);
var ans_ = <String>[];
for (var idx_ = 0; idx_ < len_; ++idx_) {
ans_.add(sse_decode_String(deserializer));
}
return ans_;
}
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
@ -685,6 +730,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
serializer.buffer.putInt32(self);
}
@protected
void sse_encode_list_String(List<String> self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_i_32(self.length, serializer);
for (final item in self) {
sse_encode_String(item, serializer);
}
}
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self, SseSerializer serializer) {

View File

@ -50,6 +50,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
int dco_decode_i_32(dynamic raw);
@protected
List<String> dco_decode_list_String(dynamic raw);
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@ -122,6 +125,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
int sse_decode_i_32(SseDeserializer deserializer);
@protected
List<String> sse_decode_list_String(SseDeserializer deserializer);
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@ -202,6 +208,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
void sse_encode_i_32(int self, SseSerializer serializer);
@protected
void sse_encode_list_String(List<String> self, SseSerializer serializer);
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self, SseSerializer serializer);

View File

@ -49,6 +49,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
int dco_decode_i_32(dynamic raw);
@protected
List<String> dco_decode_list_String(dynamic raw);
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@ -121,6 +124,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
int sse_decode_i_32(SseDeserializer deserializer);
@protected
List<String> sse_decode_list_String(SseDeserializer deserializer);
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@ -201,6 +207,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
void sse_encode_i_32(int self, SseSerializer serializer);
@protected
void sse_encode_list_String(List<String> self, SseSerializer serializer);
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self, SseSerializer serializer);

View File

@ -35,7 +35,7 @@ class AppGlobalUIModel extends BaseUIModel {
await Future.delayed(const Duration(milliseconds: 100));
if (AppConf.networkVersionData == null) {
showToast(context,
"检查更新失败!请检查网络连接... \n进入离线模式.. \n\n请谨慎在离线模式中使用。\n请尝试更换无污染的DNS。 \n当前版本构建日期:${AppConf.appVersionDate}\n QQ群940696487 \n错误信息:$checkUpdateError");
"网络异常,这可能是服务器正在维护或遭受攻击... \n进入离线模式.. \n\n请谨慎在离线模式中使用。 \n当前版本构建日期:${AppConf.appVersionDate}\n QQ群940696487 \n错误信息:$checkUpdateError");
return false;
}
final lastVersion = AppConf.isMSE

View File

@ -133,12 +133,6 @@ class HomeUIModel extends BaseUIModel {
appUpdateTimer = Timer.periodic(const Duration(minutes: 30), (timer) {
_checkLocalizationUpdate();
});
Future.delayed(const Duration(milliseconds: 100)).then((value) {
if (URLConf.isUsingFallback) {
if (!mounted) return;
showToast(context!, "因源服务器异常(机房故障或遭受攻击),当前正在使用备用线路,可能会出现访问速度下降,敬请谅解。");
}
});
super.initModel();
}