Support core status check and force restart Optimize proxies page and access page Update flutter and pub dependencies Update go version Optimize more details
181 lines
5.3 KiB
Dart
181 lines
5.3 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:dio/dio.dart';
|
|
import 'package:dio/io.dart';
|
|
import 'package:fl_clash/common/common.dart';
|
|
import 'package:fl_clash/models/models.dart';
|
|
import 'package:fl_clash/state.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
|
|
class Request {
|
|
late final Dio dio;
|
|
late final Dio _clashDio;
|
|
String? userAgent;
|
|
|
|
Request() {
|
|
dio = Dio(BaseOptions(headers: {'User-Agent': browserUa}));
|
|
_clashDio = Dio();
|
|
_clashDio.httpClientAdapter = IOHttpClientAdapter(
|
|
createHttpClient: () {
|
|
final client = HttpClient();
|
|
client.findProxy = (Uri uri) {
|
|
client.userAgent = globalState.ua;
|
|
return FlClashHttpOverrides.handleFindProxy(uri);
|
|
};
|
|
return client;
|
|
},
|
|
);
|
|
}
|
|
|
|
Future<Response> getFileResponseForUrl(String url) async {
|
|
final response = await _clashDio.get(
|
|
url,
|
|
options: Options(responseType: ResponseType.bytes),
|
|
);
|
|
return response;
|
|
}
|
|
|
|
Future<Response> getTextResponseForUrl(String url) async {
|
|
final response = await _clashDio.get(
|
|
url,
|
|
options: Options(responseType: ResponseType.plain),
|
|
);
|
|
return response;
|
|
}
|
|
|
|
Future<MemoryImage?> getImage(String url) async {
|
|
if (url.isEmpty) return null;
|
|
final response = await dio.get<Uint8List>(
|
|
url,
|
|
options: Options(responseType: ResponseType.bytes),
|
|
);
|
|
final data = response.data;
|
|
if (data == null) return null;
|
|
return MemoryImage(data);
|
|
}
|
|
|
|
Future<Map<String, dynamic>?> checkForUpdate() async {
|
|
final response = await dio.get(
|
|
'https://api.github.com/repos/$repository/releases/latest',
|
|
options: Options(responseType: ResponseType.json),
|
|
);
|
|
if (response.statusCode != 200) return null;
|
|
final data = response.data as Map<String, dynamic>;
|
|
final remoteVersion = data['tag_name'];
|
|
final version = globalState.packageInfo.version;
|
|
final hasUpdate =
|
|
utils.compareVersions(remoteVersion.replaceAll('v', ''), version) > 0;
|
|
if (!hasUpdate) return null;
|
|
return data;
|
|
}
|
|
|
|
final Map<String, IpInfo Function(Map<String, dynamic>)> _ipInfoSources = {
|
|
'https://ipwho.is': IpInfo.fromIpWhoIsJson,
|
|
'https://api.myip.com': IpInfo.fromMyIpJson,
|
|
'https://ipapi.co/json': IpInfo.fromIpApiCoJson,
|
|
'https://ident.me/json': IpInfo.fromIdentMeJson,
|
|
'http://ip-api.com/json': IpInfo.fromIpAPIJson,
|
|
'https://api.ip.sb/geoip': IpInfo.fromIpSbJson,
|
|
'https://ipinfo.io/json': IpInfo.fromIpInfoIoJson,
|
|
};
|
|
|
|
Future<Result<IpInfo?>> checkIp({CancelToken? cancelToken}) async {
|
|
var failureCount = 0;
|
|
final futures = _ipInfoSources.entries.map((source) async {
|
|
final Completer<Result<IpInfo?>> completer = Completer();
|
|
handleFailRes() {
|
|
if (!completer.isCompleted && failureCount == _ipInfoSources.length) {
|
|
completer.complete(Result.success(null));
|
|
}
|
|
}
|
|
|
|
final future = dio
|
|
.get<Map<String, dynamic>>(
|
|
source.key,
|
|
cancelToken: cancelToken,
|
|
options: Options(responseType: ResponseType.json),
|
|
)
|
|
.timeout(const Duration(seconds: 10));
|
|
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();
|
|
});
|
|
return completer.future;
|
|
});
|
|
final res = await Future.any(futures);
|
|
cancelToken?.cancel();
|
|
return res;
|
|
}
|
|
|
|
Future<bool> pingHelper() async {
|
|
try {
|
|
final response = await dio
|
|
.get(
|
|
'http://$localhost:$helperPort/ping',
|
|
options: Options(responseType: ResponseType.plain),
|
|
)
|
|
.timeout(const Duration(milliseconds: 2000));
|
|
if (response.statusCode != HttpStatus.ok) {
|
|
return false;
|
|
}
|
|
return (response.data as String) == globalState.coreSHA256;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<bool> startCoreByHelper(String arg) async {
|
|
try {
|
|
final response = await dio
|
|
.post(
|
|
'http://$localhost:$helperPort/start',
|
|
data: json.encode({'path': appPath.corePath, 'arg': arg}),
|
|
options: Options(responseType: ResponseType.plain),
|
|
)
|
|
.timeout(const Duration(milliseconds: 2000));
|
|
if (response.statusCode != HttpStatus.ok) {
|
|
return false;
|
|
}
|
|
final data = response.data as String;
|
|
return data.isEmpty;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<bool> stopCoreByHelper() async {
|
|
try {
|
|
final response = await dio
|
|
.post(
|
|
'http://$localhost:$helperPort/stop',
|
|
options: Options(responseType: ResponseType.plain),
|
|
)
|
|
.timeout(const Duration(milliseconds: 2000));
|
|
if (response.statusCode != HttpStatus.ok) {
|
|
return false;
|
|
}
|
|
final data = response.data as String;
|
|
return data.isEmpty;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
final request = Request();
|