diff --git a/lib/main.dart b/lib/main.dart index 241879a..7351392 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -23,7 +23,6 @@ void main(List args) { } runZonedGuarded(() async { await Rhttp.init(); - AppDio.init(); WidgetsFlutterBinding.ensureInitialized(); await init(); if (App.isAndroid) { diff --git a/lib/network/app_dio.dart b/lib/network/app_dio.dart index b96e292..dbbc7bc 100644 --- a/lib/network/app_dio.dart +++ b/lib/network/app_dio.dart @@ -2,7 +2,6 @@ import 'dart:convert'; import 'dart:io'; import 'package:dio/dio.dart'; -import 'package:dio_compatibility_layer/dio_compatibility_layer.dart'; import 'package:flutter/services.dart'; import 'package:rhttp/rhttp.dart' as rhttp; import 'package:venera/foundation/appdata.dart'; @@ -107,16 +106,10 @@ class MyLogInterceptor implements Interceptor { class AppDio with DioMixin { String? _proxy = proxy; - static rhttp.RhttpCompatibleClient? _compatibleClient; - - static void init() { - _compatibleClient = rhttp.RhttpCompatibleClient.createSync(); - } - AppDio([BaseOptions? options]) { this.options = options ?? BaseOptions(); interceptors.add(MyLogInterceptor()); - httpClientAdapter = ConversionLayerAdapter(_compatibleClient!); + httpClientAdapter = RHttpAdapter(const rhttp.ClientSettings()); interceptors.add(CookieManagerSql(SingleInstanceCookieJar.instance!)); interceptors.add(NetworkCacheManager()); interceptors.add(CloudflareInterceptor()); @@ -197,20 +190,11 @@ class AppDio with DioMixin { if (_proxy != proxy) { Log.info("Network", "Proxy changed to $proxy"); _proxy = proxy; - (httpClientAdapter as ConversionLayerAdapter).close(); - _compatibleClient = await rhttp.RhttpCompatibleClient.create( - settings: rhttp.ClientSettings( - proxySettings: proxy == null - ? const rhttp.ProxySettings.noProxy() - : rhttp.ProxySettings.proxy(proxy!), - timeoutSettings: const rhttp.TimeoutSettings( - connectTimeout: Duration(seconds: 15), - keepAliveTimeout: Duration(seconds: 60), - keepAlivePing: Duration(seconds: 30), - ), - ), - ); - httpClientAdapter = ConversionLayerAdapter(_compatibleClient!); + httpClientAdapter = RHttpAdapter(rhttp.ClientSettings( + proxySettings: proxy == null + ? const rhttp.ProxySettings.noProxy() + : rhttp.ProxySettings.proxy(proxy!), + )); } Log.info( "Network", @@ -229,3 +213,81 @@ class AppDio with DioMixin { ); } } + +class RHttpAdapter implements HttpClientAdapter { + rhttp.ClientSettings settings; + + RHttpAdapter(this.settings) { + settings.copyWith( + redirectSettings: const rhttp.RedirectSettings.limited(5), + timeoutSettings: const rhttp.TimeoutSettings( + connectTimeout: Duration(seconds: 15), + keepAliveTimeout: Duration(seconds: 60), + keepAlivePing: Duration(seconds: 30), + ), + httpVersionPref: rhttp.HttpVersionPref.http1_1, + ); + } + + @override + void close({bool force = false}) {} + + @override + Future fetch( + RequestOptions options, + Stream? requestStream, + Future? cancelFuture, + ) async { + var res = await rhttp.Rhttp.request( + method: switch (options.method) { + 'GET' => rhttp.HttpMethod.get, + 'POST' => rhttp.HttpMethod.post, + 'PUT' => rhttp.HttpMethod.put, + 'PATCH' => rhttp.HttpMethod.patch, + 'DELETE' => rhttp.HttpMethod.delete, + 'HEAD' => rhttp.HttpMethod.head, + 'OPTIONS' => rhttp.HttpMethod.options, + 'TRACE' => rhttp.HttpMethod.trace, + 'CONNECT' => rhttp.HttpMethod.connect, + _ => throw ArgumentError('Unsupported method: ${options.method}'), + }, + url: options.uri.toString(), + settings: settings, + expectBody: rhttp.HttpExpectBody.stream, + body: requestStream == null ? null : rhttp.HttpBody.stream(requestStream), + headers: rhttp.HttpHeaders.rawMap( + Map.fromEntries( + options.headers.entries.map( + (e) => MapEntry(e.key, e.value.toString().trim()), + ), + ), + ), + ); + if (res is! rhttp.HttpStreamResponse) { + throw Exception("Invalid response type: ${res.runtimeType}"); + } + var headers = >{}; + for (var entry in res.headers) { + var key = entry.$1.toLowerCase(); + headers[key] ??= []; + headers[key]!.add(entry.$2); + } + var data = res.body; + if(headers['content-encoding']?.contains('gzip') ?? false) { + // rhttp does not support gzip decoding + var buffer = []; + await for (var chunk in data) { + buffer.addAll(chunk); + } + data = Stream.value(Uint8List.fromList(gzip.decode(buffer))); + buffer.clear(); + } + return ResponseBody( + data, + res.statusCode, + statusMessage: null, + isRedirect: false, + headers: headers, + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 1eb95bd..fdf85c8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -138,14 +138,6 @@ packages: url: "https://pub.dev" source: hosted version: "5.7.0" - dio_compatibility_layer: - dependency: "direct main" - description: - name: dio_compatibility_layer - sha256: bb7ea1dd6fe98b8f5e3d90da408802fc3abf14d4485416e882cf1b2c8fb4b209 - url: "https://pub.dev" - source: hosted - version: "0.1.0" dio_web_adapter: dependency: transitive description: @@ -500,8 +492,8 @@ packages: dependency: "direct main" description: path: "." - ref: HEAD - resolved-ref: c9584f890f54c26b98ca4f465a2346024a528b31 + ref: d1c96cd6503103b3270dfe2f320d4a1c93780f53 + resolved-ref: d1c96cd6503103b3270dfe2f320d4a1c93780f53 url: "https://github.com/venera-app/lodepng_flutter" source: git version: "0.0.1" diff --git a/pubspec.yaml b/pubspec.yaml index 7f14035..c71cbf9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -59,7 +59,6 @@ dependencies: url: https://github.com/venera-app/lodepng_flutter ref: d1c96cd6503103b3270dfe2f320d4a1c93780f53 rhttp: 0.9.1 - dio_compatibility_layer: ^0.1.0 dev_dependencies: flutter_test: