Skip to content

Commit

Permalink
Add useHttp3 with flutter_curl
Browse files Browse the repository at this point in the history
  • Loading branch information
TaYaKi71751 committed Dec 30, 2023
1 parent cd37657 commit 86a1c08
Show file tree
Hide file tree
Showing 20 changed files with 291 additions and 13 deletions.
Binary file added assets/libcurl/android/arm64-v8a/libcurl.so
Binary file not shown.
Binary file added assets/libcurl/android/arm64-v8a/libquiche.so
Binary file not shown.
Binary file added assets/libcurl/android/armeabi-v7a/libcurl.so
Binary file not shown.
Binary file added assets/libcurl/android/armeabi-v7a/libquiche.so
Binary file not shown.
Binary file added assets/libcurl/android/x86_64/libcurl.so
Binary file not shown.
Binary file added assets/libcurl/android/x86_64/libquiche.so
Binary file not shown.
3 changes: 2 additions & 1 deletion assets/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,5 +308,6 @@
"cannotuseios": "You cannot use update on iOS :(",
"exitTheApp": "Exit the app",
"deleteoldlogatstart": "Delete Old Log (At App Start)",
"ignorehttptimeout": "Ignore Http Timeout"
"ignorehttptimeout": "Ignore Http Timeout",
"usehttp3": "Use Http3 (Experimental)"
}
3 changes: 2 additions & 1 deletion assets/locale/eo.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,5 +308,6 @@
"cannotuseios": "You cannot use update on iOS :(",
"exitTheApp": "Exit the app",
"deleteoldlogatstart": "Delete Old Log (At App Start)",
"ignorehttptimeout": "Ignore Http Timeout"
"ignorehttptimeout": "Ignore Http Timeout",
"usehttp3": "Use Http3 (Experimental)"
}
3 changes: 2 additions & 1 deletion assets/locale/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,5 +308,6 @@
"cannotuseios": "You cannot use update on iOS :(",
"exitTheApp": "Exit the app",
"deleteoldlogatstart": "Delete Old Log (At App Start)",
"ignorehttptimeout": "Ignore Http Timeout"
"ignorehttptimeout": "Ignore Http Timeout",
"usehttp3": "Use Http3 (Experimental)"
}
3 changes: 2 additions & 1 deletion assets/locale/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,5 +308,6 @@
"cannotuseios": "You cannot use update on iOS :(",
"exitTheApp": "Exit the app",
"deleteoldlogatstart": "Delete Old Log (At App Start)",
"ignorehttptimeout": "Ignore Http Timeout"
"ignorehttptimeout": "Ignore Http Timeout",
"usehttp3": "Use Http3 (Experimental)"
}
3 changes: 2 additions & 1 deletion assets/locale/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,5 +308,6 @@
"cannotuseios": "iOS에선 사용할 수 없어요 :(",
"exitTheApp": "앱 종료하기",
"deleteoldlogatstart": "Delete Old Log (At App Start)",
"ignorehttptimeout": "Ignore Http Timeout"
"ignorehttptimeout": "Ignore Http Timeout",
"usehttp3": "Use Http3 (Experimental)"
}
3 changes: 2 additions & 1 deletion assets/locale/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,5 +308,6 @@
"cannotuseios": "You cannot use update on iOS :(",
"exitTheApp": "Exit the app",
"deleteoldlogatstart": "Delete Old Log (At App Start)",
"ignorehttptimeout": "Ignore Http Timeout"
"ignorehttptimeout": "Ignore Http Timeout",
"usehttp3": "Use Http3 (Experimental)"
}
3 changes: 2 additions & 1 deletion assets/locale/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,5 +308,6 @@
"cannotuseios": "You cannot use update on iOS :(",
"exitTheApp": "Exit the app",
"deleteoldlogatstart": "Delete Old Log (At App Start)",
"ignorehttptimeout": "Ignore Http Timeout"
"ignorehttptimeout": "Ignore Http Timeout",
"usehttp3": "Use Http3 (Experimental)"
}
3 changes: 2 additions & 1 deletion assets/locale/zh_Hans.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,5 +308,6 @@
"cannotuseios": "You cannot use update on iOS :(",
"exitTheApp": "Exit the app",
"deleteoldlogatstart": "Delete Old Log (At App Start)",
"ignorehttptimeout": "Ignore Http Timeout"
"ignorehttptimeout": "Ignore Http Timeout",
"usehttp3": "Use Http3 (Experimental)"
}
3 changes: 2 additions & 1 deletion assets/locale/zh_Hant.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,5 +308,6 @@
"cannotuseios": "You cannot use update on iOS :(",
"exitTheApp": "Exit the app",
"deleteoldlogatstart": "Delete Old Log (At App Start)",
"ignorehttptimeout": "Ignore Http Timeout"
"ignorehttptimeout": "Ignore Http Timeout",
"usehttp3": "Use Http3 (Experimental)"
}
177 changes: 177 additions & 0 deletions lib/network/libcurl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// This source code is a part of Project Violet.
// Copyright (C) 2020-2023. violet-team. Licensed under the Apache-2.0 License.

import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';

import 'package:device_info_plus/device_info_plus.dart';
import 'package:ffi/ffi.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
import 'package:flutter_curl/flutter_curl.dart' as flutter_curl;
import 'package:violet/log/log.dart';
import 'package:violet/settings/settings.dart';

class Http3Request {
Future<flutter_curl.Client> getClient() async {
late final soPath;
if(Platform.isAndroid) {
soPath = (await _checkSharedLibraryAndroid('libcurl'))!;
(await _checkSharedLibraryAndroid('libquiche'))!;
} else {
throw 'NOT_SUPPORTED';
}
flutter_curl.Client client = flutter_curl.Client(
libPath: soPath,
httpVersions: [flutter_curl.HTTPVersion.http3]
);
await client.init();
return client;
}

Future<flutter_curl.Response> get(
String url,{
Map<String,String>? headers,
String? body,
Duration? timeout,
bool? followRedirects = true
}
) async {
flutter_curl.Client client = await getClient();
flutter_curl.Response? res;
do {
flutter_curl.Request req = flutter_curl.Request(
method: 'GET',
url: res?.headers['location'] ?? url,
headers: headers ?? {},
body: (body?.isNotEmpty ?? false) ? flutter_curl.RequestBody.string(body ?? '') : null,
);
if(res?.headers['location']?.isNotEmpty ?? false){
req.headers.addAll({'Referer': req.url});
}
Logger.info('[Http3 Request] GET ${req.url}');
var _sent = client.send(req);
if(!Settings.ignoreHTTPTimeout && timeout != null){
_sent.timeout(timeout);
}
res = await _sent;
Logger.info('[Http3 Request] GET ${req.url} code: ${res.statusCode}');
if(((res.statusCode) / 100).floor() != 3){
break;
}
} while(followRedirects != false);
return res;
}

Future<flutter_curl.Response> post(
String url,{
Map<String,String>? headers,
String? body,
Duration? timeout,
bool? followRedirects = true,
}
) async {
flutter_curl.Client client = await getClient();
flutter_curl.Response? res;
do {
flutter_curl.Request req = flutter_curl.Request(
method: 'POST',
url: res?.headers['location'] ?? url,
headers: headers ?? {},
body: (body?.isNotEmpty ?? false) ? flutter_curl.RequestBody.string(body ?? '') : null
);
if(res?.headers['location']?.isNotEmpty ?? false){
req.headers.addAll({'Referer': req.url});
}
Logger.info('[Http3 Request] POST ${req.url}');
var _sent = client.send(req);
if(!Settings.ignoreHTTPTimeout && timeout != null){
_sent.timeout(timeout);
}
res = await _sent;
Logger.info('[Http3 Request] POST ${req.url} code: ${res.statusCode}');
if(((res.statusCode) / 100).floor() != 3){
break;
}
} while(followRedirects != false);
return res;
}

static const libraryAbis = [
'arm64-v8a',
'armeabi-v7a',
'x86_64',
];

Future<String?> _checkSharedLibraryAndroid(String soName) async {
if (!Platform.isAndroid) {
return null;
}

final devicePlugin = DeviceInfoPlugin();
final deviceInfo = await devicePlugin.androidInfo;

final targetAbi = deviceInfo.supportedAbis.firstWhere(
(abi) => libraryAbis.contains(abi),
);
if(!libraryAbis.contains(targetAbi)){
return null;
}
final sharedLibraryPath = 'assets/libcurl/android/$targetAbi/${soName}.so';
final sharedLibraryContent = await rootBundle.load(sharedLibraryPath);

final tempDir = await getTemporaryDirectory();
final libraryFile = File('${tempDir.path}/${soName}.so');
if(await libraryFile.exists()){
return libraryFile.path;
}
final createdFile = await libraryFile.create();
final openFile = await createdFile.open(mode: FileMode.write);
final writtenFile =
await openFile.writeFrom(Uint8List.view(sharedLibraryContent.buffer));
await writtenFile.close();

return libraryFile.path;
}

static Future<http.Response> toHttpResponse(flutter_curl.Response res) async {
return http.Response(
String.fromCharCodes(res.body),
res.statusCode,
request: http.Request(
res.request?.method ?? '',
Uri.parse(res.request?.url ?? '')
),
headers: res.headers,
isRedirect: (res.statusCode / 100).floor() == 3,
);
}

static Future<http.StreamedResponse> toStreamedHttpResponse(flutter_curl.Response res) async {
return http.StreamedResponse(
Stream.value(res.body),
res.statusCode,
contentLength: res.body.length,
request: http.Request(
res.request?.method ?? '',
Uri.parse(res.request?.url ?? '')
),
headers: res.headers,
isRedirect: (res.statusCode / 100).floor() == 3,
);
}

static Future<flutter_curl.Request> fromHttpRequest(http.Request req) async {
return flutter_curl.Request(
method: req.method,
url: req.url.toString(),
headers: req.headers,
body: flutter_curl.RequestBody.string(req.body),
verbose: false,
verifySSL: true,
httpVersions: [flutter_curl.HTTPVersion.http3]
);
}
}
49 changes: 45 additions & 4 deletions lib/network/wrapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:http/http.dart';
import 'package:flutter_curl/flutter_curl.dart' as flutter_curl;
import 'package:path_provider/path_provider.dart';
import 'package:violet/component/proxy/proxy.myvipwebtools.com.dart';
import 'package:violet/log/log.dart';
import 'package:violet/network/libcurl.dart';
import 'package:violet/settings/settings.dart';
import 'package:violet/thread/semaphore.dart';

Expand Down Expand Up @@ -97,7 +102,16 @@ Future<http.Response> _ehentaiGet(String url,
StreamedResponse response;

try {
var _sent = client.send(request);
late var _sent;
if(Settings.useHttp3){
try {
_sent = Http3Request().get(url, headers: headers);
} catch(_){
_sent = client.send(request);
}
} else {
_sent = client.send(request);
}
if(!Settings.ignoreHTTPTimeout){
_sent.timeout(
const Duration(seconds: 3),
Expand All @@ -107,7 +121,12 @@ Future<http.Response> _ehentaiGet(String url,
},
);
}
response = await _sent;
var _res = await _sent;
if(_res is flutter_curl.Response){
response = await Http3Request.toStreamedHttpResponse(_res);
} else {
response = _res;
}
} catch (e, st) {
Logger.error('[Http Request] GET: $url\n'
'E:$e\n'
Expand Down Expand Up @@ -163,13 +182,30 @@ Future<http.Response> _scriptGet(String url,
var _headers = headers ?? {};
_headers['user-agent'] = HttpWrapper.userAgent;
if (timeout == null) {
if(Settings.useHttp3){
try {
var _res = await Http3Request().get(url, headers: _headers);
res = await Http3Request.toHttpResponse(_res);
} catch(_){
res = await http.get(Uri.parse(url), headers: _headers);
}
}
res = await http.get(Uri.parse(url), headers: _headers);
} else {
bool isTimeout = false;
var retry = 0;
do {
isTimeout = false;
var _sent = http.get(Uri.parse(url), headers: _headers);
late var _sent;
if(Settings.useHttp3){
try {
_sent = Http3Request().get(url, headers: _headers);
} catch(_){
_sent = http.get(Uri.parse(url), headers: _headers);
}
} else {
_sent = http.get(Uri.parse(url), headers: _headers);
}
if(!Settings.ignoreHTTPTimeout){
_sent
.timeout(
Expand All @@ -181,7 +217,12 @@ Future<http.Response> _scriptGet(String url,
},
);
}
res = await _sent;
var _res = await _sent;
if(_res is flutter_curl.Response){
res = await Http3Request.toHttpResponse(_res);
} else {
res = _res;
}
} while (isTimeout && retry < 10);
}

Expand Down
32 changes: 32 additions & 0 deletions lib/pages/settings/settings_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1492,6 +1492,38 @@ class _SettingsPageState extends State<SettingsPage>
});
},
),
_buildDivider(),
InkWell(
customBorder: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(8.0),
bottomRight: Radius.circular(8.0))),
child: ListTile(
leading: Image.asset(
'assets/images/logo.png',
width: 25,
height: 25,
),
title: Text(Translations.of(context).trans('usehttp3')),
trailing: Switch(
value: Settings.useHttp3,
onChanged: (newValue) async {
await Settings.setUseHttp3(newValue);
setState(() {
_shouldReload = true;
});
},
activeTrackColor: Settings.majorColor,
activeColor: Settings.majorAccentColor,
),
),
onTap: () async {
await Settings.setUseHttp3(!Settings.useHttp3);
setState(() {
_shouldReload = true;
});
},
),
// _buildDivider(),
// InkWell(
// customBorder: const RoundedRectangleBorder(
Expand Down
Loading

0 comments on commit 86a1c08

Please sign in to comment.