Files
MWClash/lib/common/request.dart

209 lines
5.4 KiB
Dart
Raw Normal View History

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
2024-04-30 23:38:49 +08:00
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';
2024-04-30 23:38:49 +08:00
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;
2024-04-30 23:38:49 +08:00
final hasUpdate =
utils.compareVersions(remoteVersion.replaceAll('v', ''), version) > 0;
if (!hasUpdate) return null;
return data;
2024-04-30 23:38:49 +08:00
}
2024-06-05 16:19:23 +08:00
final Map<String, IpInfo Function(Map<String, dynamic>)> _ipInfoSources = {
'https://ipwho.is/': IpInfo.fromIpwhoIsJson,
'https://api.ip.sb/geoip/': IpInfo.fromIpSbJson,
'https://ipapi.co/json/': IpInfo.fromIpApiCoJson,
'https://ipinfo.io/json/': IpInfo.fromIpInfoIoJson,
};
2024-06-05 16:19:23 +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();
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,
),
);
future.then((res) {
if (res.statusCode == HttpStatus.ok && res.data != null) {
completer.complete(Result.success(source.value(res.data!)));
} else {
failureCount++;
handleFailRes();
2024-06-05 16:19:23 +08:00
}
}).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;
2024-06-05 16:19:23 +08:00
}
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;
}
}
2024-04-30 23:38:49 +08:00
}
final request = Request();