diff --git a/lib/network/app_dio.dart b/lib/network/app_dio.dart index af21d6f..6de436f 100644 --- a/lib/network/app_dio.dart +++ b/lib/network/app_dio.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -176,6 +177,8 @@ class AppDio with DioMixin { return res; } + static final Map _requests = {}; + @override Future> request( String path, { @@ -186,6 +189,13 @@ class AppDio with DioMixin { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { + if(options?.headers?['prevent-parallel'] == 'true') { + while(_requests.containsKey(path)) { + await Future.delayed(const Duration(milliseconds: 20)); + } + _requests[path] = true; + options!.headers!.remove('prevent-parallel'); + } proxy = await getProxy(); if (_proxy != proxy) { Log.info("Network", "Proxy changed to $proxy"); @@ -196,7 +206,7 @@ class AppDio with DioMixin { : rhttp.ProxySettings.proxy(proxy!), )); } - return super.request( + var res = super.request( path, data: data, queryParameters: queryParameters, @@ -205,6 +215,10 @@ class AppDio with DioMixin { onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress, ); + if(_requests.containsKey(path)) { + _requests.remove(path); + } + return res; } } diff --git a/lib/network/cache.dart b/lib/network/cache.dart index 7beea4f..9d9df06 100644 --- a/lib/network/cache.dart +++ b/lib/network/cache.dart @@ -1,5 +1,4 @@ -import 'dart:async'; - +import 'dart:typed_data'; import 'package:dio/dio.dart'; class NetworkCache { @@ -43,7 +42,7 @@ class NetworkCacheManager implements Interceptor { static const _maxCacheSize = 10 * 1024 * 1024; void setCache(NetworkCache cache) { - while(size > _maxCacheSize){ + while (size > _maxCacheSize) { size -= _cache.values.first.size; _cache.remove(_cache.keys.first); } @@ -53,7 +52,7 @@ class NetworkCacheManager implements Interceptor { void removeCache(Uri uri) { var cache = _cache[uri]; - if(cache != null){ + if (cache != null) { size -= cache.size; } _cache.remove(uri); @@ -64,41 +63,29 @@ class NetworkCacheManager implements Interceptor { size = 0; } - var preventParallel = {}; - @override void onError(DioException err, ErrorInterceptorHandler handler) { - if(err.requestOptions.method != "GET"){ + if (err.requestOptions.method != "GET") { return handler.next(err); } - if(preventParallel[err.requestOptions.uri] != null){ - preventParallel[err.requestOptions.uri]!.complete(); - preventParallel.remove(err.requestOptions.uri); - } return handler.next(err); } @override void onRequest( RequestOptions options, RequestInterceptorHandler handler) async { - if(options.method != "GET"){ + if (options.method != "GET") { return handler.next(options); } - if(preventParallel[options.uri] != null){ - await preventParallel[options.uri]!.future; - } var cache = getCache(options.uri); - if (cache == null || !compareHeaders(options.headers, cache.requestHeaders)) { - if(options.headers['cache-time'] != null){ + if (cache == null || + !compareHeaders(options.headers, cache.requestHeaders)) { + if (options.headers['cache-time'] != null) { options.headers.remove('cache-time'); } - if(options.headers['prevent-parallel'] != null){ - options.headers.remove('prevent-parallel'); - preventParallel[options.uri] = Completer(); - } return handler.next(options); } else { - if(options.headers['cache-time'] == 'no'){ + if (options.headers['cache-time'] == 'no') { options.headers.remove('cache-time'); removeCache(options.uri); return handler.next(options); @@ -106,20 +93,21 @@ class NetworkCacheManager implements Interceptor { } var time = DateTime.now(); var diff = time.difference(cache.time); - if (options.headers['cache-time'] == 'long' - && diff < const Duration(hours: 2)) { + if (options.headers['cache-time'] == 'long' && + diff < const Duration(hours: 2)) { return handler.resolve(Response( requestOptions: options, data: cache.data, - headers: Headers.fromMap(cache.responseHeaders), + headers: Headers.fromMap(cache.responseHeaders) + ..set('venera-cache', 'true'), statusCode: 200, )); - } - else if (diff < const Duration(seconds: 5)) { + } else if (diff < const Duration(seconds: 5)) { return handler.resolve(Response( requestOptions: options, data: cache.data, - headers: Headers.fromMap(cache.responseHeaders), + headers: Headers.fromMap(cache.responseHeaders) + ..set('venera-cache', 'true'), statusCode: 200, )); } else if (diff < const Duration(hours: 1)) { @@ -133,7 +121,8 @@ class NetworkCacheManager implements Interceptor { return handler.resolve(Response( requestOptions: options, data: cache.data, - headers: Headers.fromMap(cache.responseHeaders), + headers: Headers.fromMap(cache.responseHeaders) + ..set('venera-cache', 'true'), statusCode: 200, )); } @@ -143,6 +132,10 @@ class NetworkCacheManager implements Interceptor { } static bool compareHeaders(Map a, Map b) { + a.remove('cache-time'); + a.remove('prevent-parallel'); + b.remove('cache-time'); + b.remove('prevent-parallel'); if (a.length != b.length) { return false; } @@ -160,11 +153,11 @@ class NetworkCacheManager implements Interceptor { if (response.requestOptions.method != "GET") { return handler.next(response); } - if(response.statusCode != null && response.statusCode! >= 400){ + if (response.statusCode != null && response.statusCode! >= 400) { return handler.next(response); } var size = _calculateSize(response.data); - if(size != null && size < 1024 * 1024 && size > 0) { + if (size != null && size < 1024 * 1024 && size > 0) { var cache = NetworkCache( uri: response.requestOptions.uri, requestHeaders: response.requestOptions.headers, @@ -175,30 +168,29 @@ class NetworkCacheManager implements Interceptor { ); setCache(cache); } - if(preventParallel[response.requestOptions.uri] != null){ - preventParallel[response.requestOptions.uri]!.complete(); - preventParallel.remove(response.requestOptions.uri); - } handler.next(response); } - static int? _calculateSize(Object? data){ - if(data == null){ + static int? _calculateSize(Object? data) { + if (data == null) { return 0; } - if(data is List) { + if (data is List) { return data.length; } - if(data is String) { - if(data.trim().isEmpty){ + if (data is Uint8List) { + return data.length; + } + if (data is String) { + if (data.trim().isEmpty) { return 0; } - if(data.length < 512 && data.contains("IP address")){ + if (data.length < 512 && data.contains("IP address")) { return 0; } return data.length * 4; } - if(data is Map) { + if (data is Map) { return data.toString().length * 4; } return null; diff --git a/lib/network/images.dart b/lib/network/images.dart index 1c26f89..096296f 100644 --- a/lib/network/images.dart +++ b/lib/network/images.dart @@ -58,8 +58,9 @@ class ImageDownloader { } } - if (configs['onResponse'] != null) { - buffer = configs['onResponse'](buffer); + if (configs['onResponse'] is JSInvokable) { + buffer = (configs['onResponse'] as JSInvokable)([buffer]); + (configs['onResponse'] as JSInvokable).free(); } await CacheManager().writeCache(cacheKey, buffer); @@ -102,7 +103,7 @@ class ImageDownloader { if (configs['onLoadFailed'] is JSInvokable) { onLoadFailed = () async { - dynamic result = configs['onLoadFailed'](); + dynamic result = (configs['onLoadFailed'] as JSInvokable)([]); if (result is Future) { result = await result; } @@ -135,8 +136,9 @@ class ImageDownloader { } } - if (configs['onResponse'] != null) { - buffer = configs['onResponse'](buffer); + if (configs['onResponse'] is JSInvokable) { + buffer = (configs['onResponse'] as JSInvokable)([buffer]); + (configs['onResponse'] as JSInvokable).free(); } var data = Uint8List.fromList(buffer); @@ -158,17 +160,23 @@ class ImageDownloader { ); return; } catch (e) { - if(retryLimit < 0 || onLoadFailed == null) { + if (retryLimit < 0 || onLoadFailed == null) { rethrow; } var newConfig = await onLoadFailed(); + (configs['onLoadFailed'] as JSInvokable).free(); onLoadFailed = null; - if(newConfig == null) { + if (newConfig == null) { rethrow; } configs = newConfig; retryLimit--; } + finally { + if(onLoadFailed != null) { + (configs['onLoadFailed'] as JSInvokable).free(); + } + } } } }