2025-06-12 10:47:02 +08:00
|
|
|
import 'dart:async';
|
2024-12-03 21:47:12 +08:00
|
|
|
import 'dart:convert';
|
|
|
|
|
import 'dart:io';
|
2024-08-26 20:44:30 +08:00
|
|
|
import 'dart:typed_data';
|
2024-04-30 23:38:49 +08:00
|
|
|
|
2024-06-03 18:02:05 +08:00
|
|
|
import 'package:dio/dio.dart';
|
2025-03-05 16:08:50 +08:00
|
|
|
import 'package:dio/io.dart';
|
2024-07-02 08:08:31 +08:00
|
|
|
import 'package:fl_clash/common/common.dart';
|
2024-09-08 21:21:21 +08:00
|
|
|
import 'package:fl_clash/models/models.dart';
|
2024-06-03 18:02:05 +08:00
|
|
|
import 'package:fl_clash/state.dart';
|
2024-08-26 20:44:30 +08:00
|
|
|
import 'package:flutter/cupertino.dart';
|
2024-04-30 23:38:49 +08:00
|
|
|
|
|
|
|
|
class Request {
|
2025-07-31 17:09:18 +08:00
|
|
|
late final Dio dio;
|
2025-03-05 16:08:50 +08:00
|
|
|
late final Dio _clashDio;
|
2024-08-26 20:44:30 +08:00
|
|
|
String? userAgent;
|
2024-06-03 18:02:05 +08:00
|
|
|
|
|
|
|
|
Request() {
|
2025-07-31 17:09:18 +08:00
|
|
|
dio = Dio(BaseOptions(headers: {'User-Agent': browserUa}));
|
2025-03-05 16:08:50 +08:00
|
|
|
_clashDio = Dio();
|
2025-07-31 17:09:18 +08:00
|
|
|
_clashDio.httpClientAdapter = IOHttpClientAdapter(
|
|
|
|
|
createHttpClient: () {
|
|
|
|
|
final client = HttpClient();
|
|
|
|
|
client.findProxy = (Uri uri) {
|
|
|
|
|
client.userAgent = globalState.ua;
|
|
|
|
|
return FlClashHttpOverrides.handleFindProxy(uri);
|
|
|
|
|
};
|
|
|
|
|
return client;
|
|
|
|
|
},
|
|
|
|
|
);
|
2024-06-03 18:02:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<Response> getFileResponseForUrl(String url) async {
|
2025-03-05 16:08:50 +08:00
|
|
|
final response = await _clashDio.get(
|
|
|
|
|
url,
|
2025-07-31 17:09:18 +08:00
|
|
|
options: Options(responseType: ResponseType.bytes),
|
2025-03-05 16:08:50 +08:00
|
|
|
);
|
2024-06-03 18:02:05 +08:00
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-02 02:24:12 +08:00
|
|
|
Future<Response> getTextResponseForUrl(String url) async {
|
|
|
|
|
final response = await _clashDio.get(
|
|
|
|
|
url,
|
2025-07-31 17:09:18 +08:00
|
|
|
options: Options(responseType: ResponseType.plain),
|
2025-05-02 02:24:12 +08:00
|
|
|
);
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-26 20:44:30 +08:00
|
|
|
Future<MemoryImage?> getImage(String url) async {
|
|
|
|
|
if (url.isEmpty) return null;
|
2025-07-31 17:09:18 +08:00
|
|
|
final response = await dio.get<Uint8List>(
|
2024-08-26 20:44:30 +08:00
|
|
|
url,
|
2025-07-31 17:09:18 +08:00
|
|
|
options: Options(responseType: ResponseType.bytes),
|
2024-08-26 20:44:30 +08:00
|
|
|
);
|
|
|
|
|
final data = response.data;
|
|
|
|
|
if (data == null) return null;
|
|
|
|
|
return MemoryImage(data);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-03 18:02:05 +08:00
|
|
|
Future<Map<String, dynamic>?> checkForUpdate() async {
|
2025-07-31 17:09:18 +08:00
|
|
|
final response = await dio.get(
|
2025-06-07 01:48:34 +08:00
|
|
|
'https://api.github.com/repos/$repository/releases/latest',
|
2025-07-31 17:09:18 +08:00
|
|
|
options: Options(responseType: ResponseType.json),
|
2024-06-03 18:02:05 +08:00
|
|
|
);
|
|
|
|
|
if (response.statusCode != 200) return null;
|
|
|
|
|
final data = response.data as Map<String, dynamic>;
|
|
|
|
|
final remoteVersion = data['tag_name'];
|
2024-07-02 08:08:31 +08:00
|
|
|
final version = globalState.packageInfo.version;
|
2024-04-30 23:38:49 +08:00
|
|
|
final hasUpdate =
|
2025-04-09 16:46:14 +08:00
|
|
|
utils.compareVersions(remoteVersion.replaceAll('v', ''), version) > 0;
|
2024-06-03 18:02:05 +08:00
|
|
|
if (!hasUpdate) return null;
|
|
|
|
|
return data;
|
2024-04-30 23:38:49 +08:00
|
|
|
}
|
2024-06-05 16:19:23 +08:00
|
|
|
|
2025-02-09 18:39:38 +08:00
|
|
|
final Map<String, IpInfo Function(Map<String, dynamic>)> _ipInfoSources = {
|
2025-07-31 17:09:18 +08:00
|
|
|
'https://ipwho.is/': IpInfo.fromIpWhoIsJson,
|
|
|
|
|
'https://api.myip.com/': IpInfo.fromMyIpJson,
|
2025-06-07 01:48:34 +08:00
|
|
|
'https://ipapi.co/json/': IpInfo.fromIpApiCoJson,
|
2025-07-31 17:09:18 +08:00
|
|
|
'https://ident.me/json/': IpInfo.fromIdentMeJson,
|
|
|
|
|
'http://ip-api.com/json/': IpInfo.fromIpAPIJson,
|
|
|
|
|
'https://api.ip.sb/geoip/': IpInfo.fromIpSbJson,
|
2025-06-07 01:48:34 +08:00
|
|
|
'https://ipinfo.io/json/': IpInfo.fromIpInfoIoJson,
|
2025-02-09 18:39:38 +08:00
|
|
|
};
|
2024-06-05 16:19:23 +08:00
|
|
|
|
2025-06-12 10:47:02 +08:00
|
|
|
Future<Result<IpInfo?>> checkIp({CancelToken? cancelToken}) async {
|
|
|
|
|
var failureCount = 0;
|
|
|
|
|
final futures = _ipInfoSources.entries.map((source) async {
|
|
|
|
|
final Completer<Result<IpInfo?>> completer = Completer();
|
2025-06-07 01:48:34 +08:00
|
|
|
handleFailRes() {
|
|
|
|
|
if (!completer.isCompleted && failureCount == _ipInfoSources.length) {
|
|
|
|
|
completer.complete(Result.success(null));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-31 17:09:18 +08:00
|
|
|
final future = dio.get<Map<String, dynamic>>(
|
2025-06-12 10:47:02 +08:00
|
|
|
source.key,
|
|
|
|
|
cancelToken: cancelToken,
|
2025-07-31 17:09:18 +08:00
|
|
|
options: Options(responseType: ResponseType.json),
|
2025-06-12 10:47:02 +08:00
|
|
|
);
|
2025-07-31 17:09:18 +08:00
|
|
|
future
|
|
|
|
|
.then((res) {
|
|
|
|
|
if (res.statusCode == HttpStatus.ok && res.data != null) {
|
|
|
|
|
completer.complete(Result.success(source.value(res.data!)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
failureCount++;
|
|
|
|
|
handleFailRes();
|
|
|
|
|
})
|
|
|
|
|
.catchError((e) {
|
|
|
|
|
failureCount++;
|
|
|
|
|
if (e is DioException && e.type == DioExceptionType.cancel) {
|
|
|
|
|
completer.complete(Result.error('cancelled'));
|
|
|
|
|
}
|
|
|
|
|
handleFailRes();
|
|
|
|
|
});
|
2025-06-12 10:47:02 +08:00
|
|
|
return completer.future;
|
|
|
|
|
});
|
|
|
|
|
final res = await Future.any(futures);
|
|
|
|
|
cancelToken?.cancel();
|
|
|
|
|
return res;
|
2024-06-05 16:19:23 +08:00
|
|
|
}
|
2024-12-03 21:47:12 +08:00
|
|
|
|
|
|
|
|
Future<bool> pingHelper() async {
|
|
|
|
|
try {
|
2025-07-31 17:09:18 +08:00
|
|
|
final response = await dio
|
2024-12-03 21:47:12 +08:00
|
|
|
.get(
|
2025-06-07 01:48:34 +08:00
|
|
|
'http://$localhost:$helperPort/ping',
|
2025-07-31 17:09:18 +08:00
|
|
|
options: Options(responseType: ResponseType.plain),
|
2024-12-03 21:47:12 +08:00
|
|
|
)
|
2025-07-31 17:09:18 +08:00
|
|
|
.timeout(const Duration(milliseconds: 2000));
|
2024-12-03 21:47:12 +08:00
|
|
|
if (response.statusCode != HttpStatus.ok) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2025-04-18 17:50:46 +08:00
|
|
|
return (response.data as String) == globalState.coreSHA256;
|
2024-12-03 21:47:12 +08:00
|
|
|
} catch (_) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<bool> startCoreByHelper(String arg) async {
|
|
|
|
|
try {
|
2025-07-31 17:09:18 +08:00
|
|
|
final response = await dio
|
2024-12-03 21:47:12 +08:00
|
|
|
.post(
|
2025-06-07 01:48:34 +08:00
|
|
|
'http://$localhost:$helperPort/start',
|
2025-07-31 17:09:18 +08:00
|
|
|
data: json.encode({'path': appPath.corePath, 'arg': arg}),
|
|
|
|
|
options: Options(responseType: ResponseType.plain),
|
2024-12-03 21:47:12 +08:00
|
|
|
)
|
2025-07-31 17:09:18 +08:00
|
|
|
.timeout(const Duration(milliseconds: 2000));
|
2024-12-03 21:47:12 +08:00
|
|
|
if (response.statusCode != HttpStatus.ok) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
final data = response.data as String;
|
|
|
|
|
return data.isEmpty;
|
|
|
|
|
} catch (_) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<bool> stopCoreByHelper() async {
|
|
|
|
|
try {
|
2025-07-31 17:09:18 +08:00
|
|
|
final response = await dio
|
2024-12-03 21:47:12 +08:00
|
|
|
.post(
|
2025-06-07 01:48:34 +08:00
|
|
|
'http://$localhost:$helperPort/stop',
|
2025-07-31 17:09:18 +08:00
|
|
|
options: Options(responseType: ResponseType.plain),
|
2024-12-03 21:47:12 +08:00
|
|
|
)
|
2025-07-31 17:09:18 +08:00
|
|
|
.timeout(const Duration(milliseconds: 2000));
|
2024-12-03 21:47:12 +08:00
|
|
|
if (response.statusCode != HttpStatus.ok) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
final data = response.data as String;
|
|
|
|
|
return data.isEmpty;
|
|
|
|
|
} catch (_) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-30 23:38:49 +08:00
|
|
|
}
|
2024-06-03 18:02:05 +08:00
|
|
|
|
2024-06-07 17:22:55 +08:00
|
|
|
final request = Request();
|