From 330a747532f710a29fcd7dd8d0c2126006a8df3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Sat, 9 Oct 2021 16:38:24 +0800 Subject: [PATCH 01/10] :recycle: refactor: offline http cache --- .../ios/Runner.xcodeproj/project.pbxproj | 65 ++-- .../contents.xcworkspacedata | 2 +- kraken/lib/src/dom/elements/a.dart | 40 ++- .../lib/src/foundation/http_cache_object.dart | 4 + kraken/lib/src/foundation/http_client.dart | 302 +++++++++++++++--- .../src/foundation/http_client_request.dart | 152 ++++++--- .../src/foundation/http_client_response.dart | 189 +---------- kraken/lib/src/foundation/http_overrides.dart | 14 +- kraken/lib/src/launcher/bundle.dart | 2 +- kraken/test/src/foundation/http_cache.dart | 16 +- .../foundation/http_client_interceptor.dart | 6 +- 11 files changed, 426 insertions(+), 366 deletions(-) diff --git a/kraken/example/ios/Runner.xcodeproj/project.pbxproj b/kraken/example/ios/Runner.xcodeproj/project.pbxproj index 8e6af387cd..91f7707144 100644 --- a/kraken/example/ios/Runner.xcodeproj/project.pbxproj +++ b/kraken/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ @@ -121,7 +121,6 @@ 0E031F0FC42779475C1A3E54 /* Pods-Runner.release.xcconfig */, BA7B01E8B45A1D7AB3D033F0 /* Pods-Runner.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -203,39 +202,12 @@ buildActionMask = 2147483647; files = ( ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${PODS_ROOT}/../Flutter/Flutter.framework", - "${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework", - "${BUILT_PRODUCTS_DIR}/audioplayers/audioplayers.framework", - "${BUILT_PRODUCTS_DIR}/connectivity/connectivity.framework", - "${BUILT_PRODUCTS_DIR}/device_info/device_info.framework", - "${PODS_ROOT}/../.symlinks/plugins/kraken/ios/kraken_bridge.framework", - "${BUILT_PRODUCTS_DIR}/kraken/kraken.framework", - "${BUILT_PRODUCTS_DIR}/kraken_camera/kraken_camera.framework", - "${BUILT_PRODUCTS_DIR}/kraken_geolocation/kraken_geolocation.framework", - "${BUILT_PRODUCTS_DIR}/kraken_video_player/kraken_video_player.framework", - "${BUILT_PRODUCTS_DIR}/kraken_webview/kraken_webview.framework", - "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework", - "${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework", - "${BUILT_PRODUCTS_DIR}/vibration/vibration.framework", + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/audioplayers.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/kraken_bridge.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/kraken.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/kraken_camera.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/kraken_geolocation.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/kraken_video_player.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/kraken_webview.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/vibration.framework", + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -367,7 +339,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -383,13 +355,17 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 4543SDRJY3; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -449,7 +425,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -498,11 +474,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -515,13 +492,17 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 4543SDRJY3; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -542,13 +523,17 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 4543SDRJY3; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", diff --git a/kraken/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/kraken/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1d526a16ed..919434a625 100644 --- a/kraken/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/kraken/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/kraken/lib/src/dom/elements/a.dart b/kraken/lib/src/dom/elements/a.dart index 452f4306ac..3e27781bd8 100644 --- a/kraken/lib/src/dom/elements/a.dart +++ b/kraken/lib/src/dom/elements/a.dart @@ -23,32 +23,29 @@ class AnchorElement extends Element { addEvent(EVENT_CLICK); } + String get pathname { + if (_href != null) { + return Uri.parse(_href!).path; + } else { + return ''; + } + } + @override void handleMouseEvent(String eventType, { PointerDownEvent? down, PointerUpEvent? up }) { super.handleMouseEvent(eventType, down: down, up: up); String? href = _href; - if (href == null) return; - - Uri uri = Uri.parse(href); - KrakenController rootController = elementManager.controller.view.rootController; - String? sourceUrl = rootController.bundleURL; - String scheme; - if (!uri.hasScheme) { - if (sourceUrl != null) { - Uri sourceUri = Uri.parse(sourceUrl); - scheme = sourceUri.scheme; - } else { - scheme = 'http'; - } - } else { - scheme = uri.scheme; + if (href != null) { + Uri sourceUri = Uri.parse(elementManager.controller.href); + Uri resolvedUri = elementManager.controller.uriParser!.resolve(sourceUri, Uri.parse(href)); + elementManager.controller.view.handleNavigationAction( + sourceUri.toString(), resolvedUri.toString(), _getNavigationType(resolvedUri.scheme)); } - elementManager.controller.view.handleNavigationAction(sourceUrl, href, _getNavigationType(scheme)); } KrakenNavigationType _getNavigationType(String scheme) { - switch (scheme) { + switch (scheme.toLowerCase()) { case 'http': case 'https': case 'file': @@ -60,6 +57,15 @@ class AnchorElement extends Element { return KrakenNavigationType.navigate; } + @override + getProperty(String key) { + switch (key) { + case 'pathname': + return pathname; + } + return super.getProperty(key); + } + @override void setProperty(String key, dynamic value) { super.setProperty(key, value); diff --git a/kraken/lib/src/foundation/http_cache_object.dart b/kraken/lib/src/foundation/http_cache_object.dart index 4853fa79c7..3a75be879a 100644 --- a/kraken/lib/src/foundation/http_cache_object.dart +++ b/kraken/lib/src/foundation/http_cache_object.dart @@ -143,6 +143,7 @@ class HttpCacheObject { /// Read the index file. Future read() async { + if (_valid) return; final bool isIndexFileExist = await _file.exists(); if (!isIndexFileExist) { // Index file not exist, dispose. @@ -370,6 +371,8 @@ class HttpCacheObjectBlob extends EventSink> { // Ensure buffer has been written. await _writer?.flush(); await _writer?.close(); + + _writer = null; } Future exists() { @@ -382,5 +385,6 @@ class HttpCacheObjectBlob extends EventSink> { Future remove() async { await _file.delete(); + close(); } } diff --git a/kraken/lib/src/foundation/http_client.dart b/kraken/lib/src/foundation/http_client.dart index 49b3345bd9..a8fce358f9 100644 --- a/kraken/lib/src/foundation/http_client.dart +++ b/kraken/lib/src/foundation/http_client.dart @@ -2,164 +2,360 @@ * Copyright (C) 2021-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ +library http_client; + import 'dart:async'; import 'dart:io'; -import 'http_client_request.dart'; import 'http_overrides.dart'; +import 'http_client_request.dart'; class ProxyHttpClient implements HttpClient { - ProxyHttpClient({ required this.nativeHttpClient, required this.httpOverrides }); + ProxyHttpClient(HttpClient nativeHttpClient, KrakenHttpOverrides httpOverrides) + : _nativeHttpClient = nativeHttpClient, + _httpOverrides = httpOverrides; - final KrakenHttpOverrides httpOverrides; - final HttpClient nativeHttpClient; + final KrakenHttpOverrides _httpOverrides; + final HttpClient _nativeHttpClient; + + bool _closed = false; @override - bool get autoUncompress => nativeHttpClient.autoUncompress; + bool get autoUncompress => _nativeHttpClient.autoUncompress; @override set autoUncompress(bool _autoUncompress) { - nativeHttpClient.autoUncompress = _autoUncompress; + _nativeHttpClient.autoUncompress = _autoUncompress; } @override - Duration get connectionTimeout => nativeHttpClient.connectionTimeout!; + Duration get connectionTimeout => _nativeHttpClient.connectionTimeout!; @override set connectionTimeout(Duration? _connectionTimeout) { - nativeHttpClient.connectionTimeout = _connectionTimeout; + _nativeHttpClient.connectionTimeout = _connectionTimeout; } @override - Duration get idleTimeout => nativeHttpClient.idleTimeout; + Duration get idleTimeout => _nativeHttpClient.idleTimeout; @override set idleTimeout(Duration _idleTimeout) { - nativeHttpClient.idleTimeout = _idleTimeout; + _nativeHttpClient.idleTimeout = _idleTimeout; } @override - int get maxConnectionsPerHost => nativeHttpClient.maxConnectionsPerHost!; + int get maxConnectionsPerHost => _nativeHttpClient.maxConnectionsPerHost!; @override set maxConnectionsPerHost(int? _maxConnectionsPerHost) { - nativeHttpClient.maxConnectionsPerHost = _maxConnectionsPerHost; + _nativeHttpClient.maxConnectionsPerHost = _maxConnectionsPerHost; } @override - String get userAgent => nativeHttpClient.userAgent!; + String get userAgent => _nativeHttpClient.userAgent!; @override set userAgent(String? _userAgent) { - nativeHttpClient.userAgent = _userAgent; + _nativeHttpClient.userAgent = _userAgent; } @override void addCredentials(Uri url, String realm, HttpClientCredentials credentials) { - nativeHttpClient.addCredentials(url, realm, credentials); + _nativeHttpClient.addCredentials(url, realm, credentials); } @override void addProxyCredentials(String host, int port, String realm, HttpClientCredentials credentials) { - nativeHttpClient.addProxyCredentials(host, port, realm, credentials); + _nativeHttpClient.addProxyCredentials(host, port, realm, credentials); } @override set authenticate(Future Function(Uri url, String scheme, String realm)? f) { - nativeHttpClient.authenticate = f; + _nativeHttpClient.authenticate = f; } @override set authenticateProxy( Future Function(String host, int port, String scheme, String realm)? f) { - nativeHttpClient.authenticateProxy = f; + _nativeHttpClient.authenticateProxy = f; } @override set badCertificateCallback(bool Function(X509Certificate cert, String host, int port)? callback) { - nativeHttpClient.badCertificateCallback = callback; + _nativeHttpClient.badCertificateCallback = callback; } @override void close({bool force = false}) { - nativeHttpClient.close(force: force); + _nativeHttpClient.close(force: force); + _closed = true; } @override - Future delete(String host, int port, String path) { - return nativeHttpClient.delete(host, port, path); + set findProxy(String Function(Uri url)? f) { + _nativeHttpClient.findProxy = f; + } + + Future _openUrl(String method, Uri uri) async { + if (_closed) { + throw StateError('Http client is closed.'); + } + + // Ignore any fragments on the request URI. + uri = uri.removeFragment(); + + return ProxyHttpClientRequest(method, uri, _httpOverrides, _nativeHttpClient); } @override - Future deleteUrl(Uri url) { - return nativeHttpClient.deleteUrl(url); + Future open(String method, String host, int port, String path) { + const int hashMark = 0x23; + const int questionMark = 0x3f; + int fragmentStart = path.length; + int queryStart = path.length; + for (int i = path.length - 1; i >= 0; i--) { + var char = path.codeUnitAt(i); + if (char == hashMark) { + fragmentStart = i; + queryStart = i; + } else if (char == questionMark) { + queryStart = i; + } + } + String? query; + if (queryStart < fragmentStart) { + query = path.substring(queryStart + 1, fragmentStart); + path = path.substring(0, queryStart); + } + // Default to https. + Uri uri = Uri(scheme: 'https', host: host, port: port, path: path, query: query); + return _openUrl(method, uri); } @override - set findProxy(String Function(Uri url)? f) { - nativeHttpClient.findProxy = f; + Future openUrl(String method, Uri url) => _openUrl(method, url); + + @override + Future get(String host, int port, String path) => open('get', host, port, path); + + @override + Future getUrl(Uri url) => _openUrl('get', url); + + @override + Future head(String host, int port, String path) => open('head', host, port, path); + + @override + Future headUrl(Uri url) => _openUrl('head', url); + + @override + Future patch(String host, int port, String path) => open('patch', host, port, path); + + @override + Future patchUrl(Uri url) => _openUrl('patch', url); + + @override + Future post(String host, int port, String path) => open('post', host, port, path); + + @override + Future postUrl(Uri url) => _openUrl('post', url); + + @override + Future put(String host, int port, String path) => open('put', host, port, path); + + @override + Future putUrl(Uri url) => _openUrl('put', url); + + @override + Future delete(String host, int port, String path) => open('delete', host, port, path); + + @override + Future deleteUrl(Uri url) => _openUrl('delete', url); +} + +HttpHeaders createHttpHeaders({ Map? initialHeaders }) { + return _HttpHeaders(initialHeaders: initialHeaders); +} + +class _HttpHeaders implements HttpHeaders { + final Map _headers = {}; + _HttpHeaders({ Map? initialHeaders }) { + if (initialHeaders != null) { + _headers.addAll(initialHeaders); + } } @override - Future get(String host, int port, String path) { - return nativeHttpClient.get(host, port, path).then(_proxyClientRequest); + bool chunkedTransferEncoding = false; + + @override + int get contentLength { + String? val = value(HttpHeaders.contentLengthHeader); + if (val == null) { + return -1; + } + return int.tryParse(val) ?? -1; } @override - Future getUrl(Uri url) { - return nativeHttpClient.getUrl(url).then(_proxyClientRequest); + set contentLength(int contentLength) { + if (contentLength == -1) { + removeAll(HttpHeaders.contentLengthHeader); + } else { + set(HttpHeaders.contentLengthHeader, contentLength.toString()); + } } @override - Future head(String host, int port, String path) { - return nativeHttpClient.head(host, port, path).then(_proxyClientRequest); + ContentType? get contentType { + String? value = _headers[HttpHeaders.contentTypeHeader]; + if (value != null) { + return ContentType.parse(value); + } else { + return null; + } } @override - Future headUrl(Uri url) { - return nativeHttpClient.headUrl(url).then(_proxyClientRequest); + set contentType(ContentType? contentType) { + if (contentType == null) { + removeAll(HttpHeaders.contentTypeHeader); + } else { + set(HttpHeaders.contentTypeHeader, contentType.toString()); + } } @override - Future open(String method, String host, int port, String path) { - return nativeHttpClient.open(method, host, port, path).then(_proxyClientRequest); + DateTime? get date { + String? value = _headers[HttpHeaders.dateHeader]; + if (value != null) { + try { + return HttpDate.parse(value); + } on Exception { + return null; + } + } + return null; } @override - Future openUrl(String method, Uri url) { - return nativeHttpClient.openUrl(method, url).then(_proxyClientRequest); + set date(DateTime? date) { + if (date == null) { + removeAll(HttpHeaders.dateHeader); + } else { + // Format "DateTime" header with date in Greenwich Mean Time (GMT). + String formatted = HttpDate.format(date.toUtc()); + set(HttpHeaders.dateHeader, formatted); + } } @override - Future patch(String host, int port, String path) { - return nativeHttpClient.patch(host, port, path).then(_proxyClientRequest); + DateTime? get expires => DateTime.tryParse(_headers[HttpHeaders.expiresHeader] ?? ''); + + @override + set expires(DateTime? _expires) { + if (_expires == null) return; + String formatted = HttpDate.format(_expires.toUtc()); + set(HttpHeaders.expiresHeader, formatted); } @override - Future patchUrl(Uri url) { - return nativeHttpClient.patchUrl(url).then(_proxyClientRequest); + String? get host => _headers[HttpHeaders.hostHeader]; + + @override + set host(String? _host) { + if (_host == null) return; + set(HttpHeaders.hostHeader, _host); } @override - Future post(String host, int port, String path) { - return nativeHttpClient.post(host, port, path).then(_proxyClientRequest); + DateTime? get ifModifiedSince { + String? value = _headers[HttpHeaders.ifModifiedSinceHeader]; + if (value != null) { + try { + return HttpDate.parse(value); + } on Exception { + return null; + } + } + return null; } @override - Future postUrl(Uri url) { - return nativeHttpClient.postUrl(url).then(_proxyClientRequest); + set ifModifiedSince(DateTime? _ifModifiedSince) { + if (_ifModifiedSince == null) { + _headers.remove(HttpHeaders.ifModifiedSinceHeader); + } else { + // Format "ifModifiedSince" header with date in Greenwich Mean Time (GMT). + String formatted = HttpDate.format(_ifModifiedSince.toUtc()); + set(HttpHeaders.ifModifiedSinceHeader, formatted); + } } + + @override + bool persistentConnection = false; + + @override + int? port = 80; + @override - Future put(String host, int port, String path) { - return nativeHttpClient.put(host, port, path).then(_proxyClientRequest); + List operator [](String name) { + String? v = _headers[name]; + if (v != null) return [v]; + return []; } @override - Future putUrl(Uri url) { - return nativeHttpClient.putUrl(url).then(_proxyClientRequest); + void add(String name, Object value, {bool preserveHeaderCase = false}) { + set(name, value, preserveHeaderCase: preserveHeaderCase); } - Future _proxyClientRequest(HttpClientRequest request) async { - return ProxyHttpClientRequest(request, httpOverrides); + @override + void clear() { + _headers.clear(); + } + + @override + void forEach(void Function(String name, List values) action) { + _headers.forEach((key, value) { + action(key, [value]); + }); + } + + @override + void noFolding(String name) {} + + @override + void remove(String name, Object value) { + removeAll(name); + } + + @override + void removeAll(String name) { + _headers.remove(name); } -} + @override + void set(String name, Object value, {bool preserveHeaderCase = false}) { + if (!preserveHeaderCase) { + name = name.toLowerCase(); + } + _headers[name] = value; + } + + @override + String? value(String name) { + return _headers[name]; + } + + @override + String toString() { + StringBuffer sb = StringBuffer(); + _headers.forEach((String name, dynamic value) { + sb..write(name) + ..write(': ') + ..write(value) + ..write('\n'); + }); + return sb.toString(); + } +} diff --git a/kraken/lib/src/foundation/http_client_request.dart b/kraken/lib/src/foundation/http_client_request.dart index 25c5599ac9..63ce4fa9e8 100644 --- a/kraken/lib/src/foundation/http_client_request.dart +++ b/kraken/lib/src/foundation/http_client_request.dart @@ -5,63 +5,76 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:typed_data'; -import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; import 'http_cache.dart'; import 'http_cache_object.dart'; -import 'http_overrides.dart'; +import 'http_client.dart'; import 'http_client_interceptor.dart'; +import 'http_overrides.dart'; import 'queue.dart'; final _requestQueue = Queue(parallel: 10); class ProxyHttpClientRequest extends HttpClientRequest { - final HttpClientRequest _clientRequest; final KrakenHttpOverrides _httpOverrides; - ProxyHttpClientRequest(HttpClientRequest clientRequest, KrakenHttpOverrides httpOverrides) - : _clientRequest = clientRequest, - _httpOverrides = httpOverrides; + final HttpClient _nativeHttpClient; + final String _method; + final Uri _uri; - @override - Encoding get encoding => _clientRequest.encoding; + HttpClientRequest? _backendRequest; + + // Saving all the data before calling real `close` to [HttpClientRequest]. + final List _data = []; + // Saving cookies. + final List _cookies = []; + // Saving request headers. + final HttpHeaders _httpHeaders = createHttpHeaders(); + + ProxyHttpClientRequest(String method, Uri uri, KrakenHttpOverrides httpOverrides, HttpClient nativeHttpClient) : + _method = method, + _uri = uri, + _httpOverrides = httpOverrides, + _nativeHttpClient = nativeHttpClient; @override - set encoding(Encoding _encoding) { - _clientRequest.encoding = _encoding; - } + Encoding get encoding => _backendRequest?.encoding ?? Encoding.getByName('utf-8')!; @override - void abort([Object? exception, StackTrace? stackTrace]) { - _clientRequest.abort(exception, stackTrace); + set encoding(Encoding _encoding) { + _backendRequest?.encoding = _encoding; } - // Saving all the data before calling real `close` to [HttpClientRequest]. - final List _data = []; - @override void add(List data) { _data.addAll(data); } - @override - void addError(error, [StackTrace? stackTrace]) { - _clientRequest.addError(error, stackTrace); - } - @override Future addStream(Stream> stream) { // Consume stream. Completer completer = Completer(); stream.listen( - _data.addAll, - onError: completer.completeError, - onDone: completer.complete, - cancelOnError: true + _data.addAll, + onError: completer.completeError, + onDone: completer.complete, + cancelOnError: true ); return completer.future; } + @override + void abort([Object? exception, StackTrace? stackTrace]) { + _backendRequest?.abort(exception, stackTrace); + } + + @override + void addError(error, [StackTrace? stackTrace]) { + _backendRequest?.addError(error, stackTrace); + } + Future _beforeRequest(HttpClientInterceptor _clientInterceptor, HttpClientRequest _clientRequest) async { try { return await _clientInterceptor.beforeRequest(_clientRequest); @@ -96,15 +109,15 @@ class ProxyHttpClientRequest extends HttpClientRequest { @override Future close() async { - HttpClientRequest request = _clientRequest; + int? contextId = KrakenHttpOverrides.getContextHeader(headers); + HttpClientRequest request = this; - int? contextId = KrakenHttpOverrides.getContextHeader(_clientRequest); if (contextId != null) { // Set the default origin and referrer. Uri referrer = getReferrer(contextId); - request.headers.set(HttpHeaders.refererHeader, referrer.toString()); + headers.set(HttpHeaders.refererHeader, referrer.toString()); String origin = getOrigin(referrer); - request.headers.set(_HttpHeadersOrigin, origin); + headers.set(_HttpHeadersOrigin, origin); HttpClientInterceptor? clientInterceptor; if (_httpOverrides.hasInterceptor(contextId)) { @@ -125,25 +138,26 @@ class ProxyHttpClientRequest extends HttpClientRequest { if (await cacheObject.hitLocalCache(request)) { HttpClientResponse? cacheResponse = await cacheObject.toHttpClientResponse(); if (cacheResponse != null) { + // // Must cancel the ongoing request, make TCP connection closed. + // _clientRequest.abort(); return cacheResponse; } } // Step 3: Handle negotiate cache request header. - if (request.headers.ifModifiedSince == null - && request.headers.value(HttpHeaders.ifNoneMatchHeader) == null) { + if (headers.ifModifiedSince == null && headers.value(HttpHeaders.ifNoneMatchHeader) == null) { // ETag has higher priority of lastModified. if (cacheObject.eTag != null) { - request.headers.set(HttpHeaders.ifNoneMatchHeader, cacheObject.eTag!); + headers.set(HttpHeaders.ifNoneMatchHeader, cacheObject.eTag!); } else if (cacheObject.lastModified != null) { - request.headers.set(HttpHeaders.ifModifiedSinceHeader, - HttpDate.format(cacheObject.lastModified!)); + headers.set(HttpHeaders.ifModifiedSinceHeader, HttpDate.format(cacheObject.lastModified!)); } } } + request = await _createBackendClientRequest(); // Send the real data to backend client. - _clientRequest.add(_data); + request.add(_data); _data.clear(); // Step 4: Lifecycle of shouldInterceptRequest @@ -194,53 +208,95 @@ class ProxyHttpClientRequest extends HttpClientRequest { } } else { - _clientRequest.add(_data); + request = await _createBackendClientRequest(); + request.add(_data); _data.clear(); } return _requestQueue.add(request.close); } + Future _createBackendClientRequest() async { + HttpClientRequest backendRequest = await _nativeHttpClient.openUrl(_method, _uri); + + if (_cookies.isNotEmpty) { + backendRequest.cookies.addAll(_cookies); + _cookies.clear(); + } + + _httpHeaders.forEach(backendRequest.headers.set); + _httpHeaders.clear(); + + _backendRequest = backendRequest; + return backendRequest; + } + @override - HttpConnectionInfo? get connectionInfo => _clientRequest.connectionInfo; + HttpConnectionInfo? get connectionInfo => _backendRequest?.connectionInfo; @override - List get cookies => _clientRequest.cookies; + List get cookies => _backendRequest?.cookies ?? _cookies; @override - Future get done => _clientRequest.done; + Future get done async { + if (_backendRequest == null) { + await _createBackendClientRequest(); + } + return _backendRequest!.done; + } @override - Future flush() { - return _clientRequest.flush(); + Future flush() async { + if (_backendRequest == null) { + await _createBackendClientRequest(); + } + return _backendRequest!.flush(); } @override - HttpHeaders get headers => _clientRequest.headers; + HttpHeaders get headers => _backendRequest?.headers ?? _httpHeaders; @override - String get method => _clientRequest.method; + String get method => _method; @override - Uri get uri => _clientRequest.uri; + Uri get uri => _uri; @override void write(Object? obj) { - _clientRequest.write(obj); + String string = '$obj'; + if (string.isEmpty) return; + + _data.addAll(Uint8List.fromList( + utf8.encode(string), + )); } @override void writeAll(Iterable objects, [String separator = '']) { - _clientRequest.writeAll(objects, separator); + Iterator iterator = objects.iterator; + if (!iterator.moveNext()) return; + if (separator.isEmpty) { + do { + write(iterator.current); + } while (iterator.moveNext()); + } else { + write(iterator.current); + while (iterator.moveNext()) { + write(separator); + write(iterator.current); + } + } } @override void writeCharCode(int charCode) { - _clientRequest.writeCharCode(charCode); + write(String.fromCharCode(charCode)); } @override void writeln([Object? object = '']) { - _clientRequest.writeln(object); + write(object); + write('\n'); } } diff --git a/kraken/lib/src/foundation/http_client_response.dart b/kraken/lib/src/foundation/http_client_response.dart index 76b6ff77a3..fbaf40cdca 100644 --- a/kraken/lib/src/foundation/http_client_response.dart +++ b/kraken/lib/src/foundation/http_client_response.dart @@ -5,190 +5,7 @@ import 'dart:async'; import 'dart:io'; -class _HttpHeaders implements HttpHeaders { - final Map _headers = {}; - _HttpHeaders({ Map? initialHeaders }) { - if (initialHeaders != null) { - _headers.addAll(initialHeaders); - } - } - - @override - bool chunkedTransferEncoding = false; - - @override - int get contentLength { - String? val = value(HttpHeaders.contentLengthHeader); - if (val == null) { - return -1; - } - return int.tryParse(val) ?? -1; - } - - @override - set contentLength(int contentLength) { - if (contentLength == -1) { - removeAll(HttpHeaders.contentLengthHeader); - } else { - set(HttpHeaders.contentLengthHeader, contentLength.toString()); - } - } - - @override - ContentType? get contentType { - String? value = _headers[HttpHeaders.contentTypeHeader]; - if (value != null) { - return ContentType.parse(value); - } else { - return null; - } - } - - @override - set contentType(ContentType? contentType) { - if (contentType == null) { - removeAll(HttpHeaders.contentTypeHeader); - } else { - set(HttpHeaders.contentTypeHeader, contentType.toString()); - } - } - - @override - DateTime? get date { - String? value = _headers[HttpHeaders.dateHeader]; - if (value != null) { - try { - return HttpDate.parse(value); - } on Exception { - return null; - } - } - return null; - } - - @override - set date(DateTime? date) { - if (date == null) { - removeAll(HttpHeaders.dateHeader); - } else { - // Format "DateTime" header with date in Greenwich Mean Time (GMT). - String formatted = HttpDate.format(date.toUtc()); - set(HttpHeaders.dateHeader, formatted); - } - } - - @override - DateTime? get expires => DateTime.tryParse(_headers[HttpHeaders.expiresHeader] ?? ''); - - @override - set expires(DateTime? _expires) { - if (_expires == null) return; - String formatted = HttpDate.format(_expires.toUtc()); - set(HttpHeaders.expiresHeader, formatted); - } - - @override - String? get host => _headers[HttpHeaders.hostHeader]; - - @override - set host(String? _host) { - if (_host == null) return; - set(HttpHeaders.hostHeader, _host); - } - - @override - DateTime? get ifModifiedSince { - String? value = _headers[HttpHeaders.ifModifiedSinceHeader]; - if (value != null) { - try { - return HttpDate.parse(value); - } on Exception { - return null; - } - } - return null; - } - - @override - set ifModifiedSince(DateTime? _ifModifiedSince) { - if (_ifModifiedSince == null) { - _headers.remove(HttpHeaders.ifModifiedSinceHeader); - } else { - // Format "ifModifiedSince" header with date in Greenwich Mean Time (GMT). - String formatted = HttpDate.format(_ifModifiedSince.toUtc()); - set(HttpHeaders.ifModifiedSinceHeader, formatted); - } - } - - - @override - bool persistentConnection = false; - - @override - int? port = 80; - - @override - List operator [](String name) { - String? v = _headers[name]; - if (v != null) return [v]; - return []; - } - - @override - void add(String name, Object value, {bool preserveHeaderCase = false}) { - set(name, value, preserveHeaderCase: preserveHeaderCase); - } - - @override - void clear() { - _headers.clear(); - } - - @override - void forEach(void Function(String name, List values) action) { - _headers.forEach((key, value) { - action(key, [value]); - }); - } - - @override - void noFolding(String name) {} - - @override - void remove(String name, Object value) { - removeAll(name); - } - - @override - void removeAll(String name) { - _headers.remove(name); - } - - @override - void set(String name, Object value, {bool preserveHeaderCase = false}) { - if (!preserveHeaderCase) { - name = name.toLowerCase(); - } - _headers[name] = value; - } - - @override - String? value(String name) { - return _headers[name]; - } - - @override - String toString() { - StringBuffer sb = StringBuffer(); - _headers.forEach((String name, dynamic value) { - sb..write(name) - ..write(': ') - ..write(value) - ..write('\n'); - }); - return sb.toString(); - } -} +import 'http_client.dart'; class _HttpConnectionInfo implements HttpConnectionInfo { static final _localHttpConnectionInfo = _HttpConnectionInfo(0, InternetAddress.anyIPv4, HttpClient.defaultHttpPort); @@ -217,7 +34,7 @@ class HttpClientStreamResponse extends Stream> implements HttpClientRe final Map _responseHeaders; - _HttpHeaders? _httpHeaders; + HttpHeaders? _httpHeaders; HttpClientStreamResponse(this._data, { this.statusCode = HttpStatus.ok, @@ -246,7 +63,7 @@ class HttpClientStreamResponse extends Stream> implements HttpClientRe } @override - HttpHeaders get headers => _httpHeaders ?? (_httpHeaders = _HttpHeaders(initialHeaders: _responseHeaders)); + HttpHeaders get headers => _httpHeaders ?? (_httpHeaders = createHttpHeaders(initialHeaders: _responseHeaders)); @override bool get isRedirect => statusCode >= 300 && statusCode < 400; diff --git a/kraken/lib/src/foundation/http_overrides.dart b/kraken/lib/src/foundation/http_overrides.dart index 61db6c857d..dde661d6d3 100644 --- a/kraken/lib/src/foundation/http_overrides.dart +++ b/kraken/lib/src/foundation/http_overrides.dart @@ -19,16 +19,16 @@ class KrakenHttpOverrides extends HttpOverrides { return _instance!; } - static int? getContextHeader(HttpClientRequest request) { - String? intVal = request.headers.value(HttpHeaderContext); + static int? getContextHeader(HttpHeaders headers) { + String? intVal = headers.value(HttpHeaderContext); if (intVal == null) { return null; } return int.tryParse(intVal); } - static void setContextHeader(HttpClientRequest request, int contextId) { - request.headers.set(HttpHeaderContext, contextId.toString()); + static void setContextHeader(HttpHeaders headers, int contextId) { + headers.set(HttpHeaderContext, contextId.toString()); } final HttpOverrides? parentHttpOverrides = HttpOverrides.current; @@ -64,11 +64,7 @@ class KrakenHttpOverrides extends HttpOverrides { nativeHttpClient = super.createHttpClient(context); } - HttpClient httpClient = ProxyHttpClient( - nativeHttpClient: nativeHttpClient, - httpOverrides: this, - ); - return httpClient; + return ProxyHttpClient(nativeHttpClient, this); } @override diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index 238f1c7a4f..90b9e1c8b4 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -149,7 +149,7 @@ class NetworkAssetBundle extends AssetBundle { @override Future load(String key) async { final HttpClientRequest request = await httpClient.getUrl(_urlFromKey(key)); - KrakenHttpOverrides.setContextHeader(request, contextId); + KrakenHttpOverrides.setContextHeader(request.headers, contextId); final HttpClientResponse response = await request.close(); if (response.statusCode != HttpStatus.ok) throw FlutterError.fromParts([ diff --git a/kraken/test/src/foundation/http_cache.dart b/kraken/test/src/foundation/http_cache.dart index 2bfdadf7a0..3f70543d7d 100644 --- a/kraken/test/src/foundation/http_cache.dart +++ b/kraken/test/src/foundation/http_cache.dart @@ -18,7 +18,7 @@ void main() { test('Simple http request with expires', () async { var request = await httpClient.openUrl('GET', server.getUri('json_with_content_length_expires_etag_last_modified')); - KrakenHttpOverrides.setContextHeader(request, contextId); + KrakenHttpOverrides.setContextHeader(request.headers, contextId); var response = await request.close(); expect(response.statusCode, 200); expect(response.headers.value(HttpHeaders.expiresHeader), @@ -36,7 +36,7 @@ void main() { // second request var requestSecond = await httpClient.openUrl('GET', server.getUri('json_with_content_length_expires_etag_last_modified')); - KrakenHttpOverrides.setContextHeader(requestSecond, contextId); + KrakenHttpOverrides.setContextHeader(requestSecond.headers, contextId); var responseSecond = await requestSecond.close(); expect(responseSecond.headers.value('cache-hits'), 'HIT'); }); @@ -45,7 +45,7 @@ void main() { // First request to save cache. var req = await httpClient.openUrl('GET', server.getUri('plain_text_with_content_length_and_last_modified')); - KrakenHttpOverrides.setContextHeader(req, contextId); + KrakenHttpOverrides.setContextHeader(req.headers, contextId); req.headers.ifModifiedSince = HttpDate.parse('Sun, 15 Mar 2020 11:32:20 GMT'); var res = await req.close(); expect(String.fromCharCodes(await consolidateHttpClientResponseBytes(res)), 'CachedData'); @@ -61,7 +61,7 @@ void main() { // First request to save cache. var req = await httpClient.openUrl('GET', server.getUri('plain_text_with_etag_and_content_length')); - KrakenHttpOverrides.setContextHeader(req, contextId); + KrakenHttpOverrides.setContextHeader(req.headers, contextId); req.headers.set(HttpHeaders.ifNoneMatchHeader, '"foo"'); var res = await req.close(); @@ -78,7 +78,7 @@ void main() { HttpCacheController.mode = HttpCacheMode.NO_CACHE; var request = await httpClient.openUrl('GET', server.getUri('json_with_content_length_expires_etag_last_modified')); - KrakenHttpOverrides.setContextHeader(request, contextId); + KrakenHttpOverrides.setContextHeader(request.headers, contextId); var response = await request.close(); expect(response.statusCode, 200); expect(response.headers.value(HttpHeaders.expiresHeader), @@ -96,7 +96,7 @@ void main() { // second request var requestSecond = await httpClient.openUrl('GET', server.getUri('json_with_content_length_expires_etag_last_modified')); - KrakenHttpOverrides.setContextHeader(requestSecond, contextId); + KrakenHttpOverrides.setContextHeader(requestSecond.headers, contextId); var responseSecond = await requestSecond.close(); // Note: This line is different. @@ -108,7 +108,7 @@ void main() { HttpCacheController.mode = HttpCacheMode.CACHE_ONLY; var request = await httpClient.openUrl('GET', server.getUri('network')); - KrakenHttpOverrides.setContextHeader(request, contextId); + KrakenHttpOverrides.setContextHeader(request.headers, contextId); var error; try { @@ -128,7 +128,7 @@ void main() { // Local request to save cache. var req = await httpClient.openUrl('GET', uri); - KrakenHttpOverrides.setContextHeader(req, contextId); + KrakenHttpOverrides.setContextHeader(req.headers, contextId); var res = await req.close(); Uint8List bytes = await consolidateHttpClientResponseBytes(res); expect(bytes.lengthInBytes, res.contentLength); diff --git a/kraken/test/src/foundation/http_client_interceptor.dart b/kraken/test/src/foundation/http_client_interceptor.dart index 4e619757a1..c02b485f05 100644 --- a/kraken/test/src/foundation/http_client_interceptor.dart +++ b/kraken/test/src/foundation/http_client_interceptor.dart @@ -15,7 +15,7 @@ void main() { test('beforeRequest', () async { var request = await httpClient.openUrl('GET', server.getUri('json_with_content_length')); - KrakenHttpOverrides.setContextHeader(request, contextId); + KrakenHttpOverrides.setContextHeader(request.headers, contextId); request.headers.add('x-test-id', 'beforeRequest-001'); var res = await request.close(); @@ -32,7 +32,7 @@ void main() { test('afterResponse', () async { var request = await httpClient.openUrl('GET', server.getUri('json_with_content_length')); - KrakenHttpOverrides.setContextHeader(request, contextId); + KrakenHttpOverrides.setContextHeader(request.headers, contextId); request.headers.add('x-test-id', 'afterResponse-001'); var response = await request.close(); @@ -42,7 +42,7 @@ void main() { test('shouldInterceptRequest', () async { var request = await httpClient.openUrl('GET', server.getUri('json_with_content_length')); - KrakenHttpOverrides.setContextHeader(request, contextId); + KrakenHttpOverrides.setContextHeader(request.headers, contextId); request.headers.add('x-test-id', 'shouldInterceptRequest-001'); var response = await request.close(); From e0884108a09a5bcf3d03cf7746a0837d5f9c6deb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Sat, 9 Oct 2021 16:46:12 +0800 Subject: [PATCH 02/10] :recycle: chore: restore main --- .../ios/Runner.xcodeproj/project.pbxproj | 65 ++++++++++++------- .../contents.xcworkspacedata | 2 +- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/kraken/example/ios/Runner.xcodeproj/project.pbxproj b/kraken/example/ios/Runner.xcodeproj/project.pbxproj index 91f7707144..8e6af387cd 100644 --- a/kraken/example/ios/Runner.xcodeproj/project.pbxproj +++ b/kraken/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ @@ -121,6 +121,7 @@ 0E031F0FC42779475C1A3E54 /* Pods-Runner.release.xcconfig */, BA7B01E8B45A1D7AB3D033F0 /* Pods-Runner.profile.xcconfig */, ); + name = Pods; path = Pods; sourceTree = ""; }; @@ -202,12 +203,39 @@ buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework", + "${BUILT_PRODUCTS_DIR}/audioplayers/audioplayers.framework", + "${BUILT_PRODUCTS_DIR}/connectivity/connectivity.framework", + "${BUILT_PRODUCTS_DIR}/device_info/device_info.framework", + "${PODS_ROOT}/../.symlinks/plugins/kraken/ios/kraken_bridge.framework", + "${BUILT_PRODUCTS_DIR}/kraken/kraken.framework", + "${BUILT_PRODUCTS_DIR}/kraken_camera/kraken_camera.framework", + "${BUILT_PRODUCTS_DIR}/kraken_geolocation/kraken_geolocation.framework", + "${BUILT_PRODUCTS_DIR}/kraken_video_player/kraken_video_player.framework", + "${BUILT_PRODUCTS_DIR}/kraken_webview/kraken_webview.framework", + "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework", + "${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework", + "${BUILT_PRODUCTS_DIR}/vibration/vibration.framework", ); name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/audioplayers.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/kraken_bridge.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/kraken.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/kraken_camera.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/kraken_geolocation.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/kraken_video_player.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/kraken_webview.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/vibration.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -339,7 +367,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -355,17 +383,13 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 4543SDRJY3; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -425,7 +449,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -474,12 +498,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -492,17 +515,13 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 4543SDRJY3; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -523,17 +542,13 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 4543SDRJY3; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", diff --git a/kraken/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/kraken/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 919434a625..1d526a16ed 100644 --- a/kraken/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/kraken/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "group:Runner.xcodeproj"> From ec38166713161b8cd4c46c45c883b2548c1d366c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Sat, 9 Oct 2021 16:47:57 +0800 Subject: [PATCH 03/10] :recycle: chore: remove library --- kraken/lib/src/foundation/http_client.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/kraken/lib/src/foundation/http_client.dart b/kraken/lib/src/foundation/http_client.dart index a8fce358f9..c8e1c8f820 100644 --- a/kraken/lib/src/foundation/http_client.dart +++ b/kraken/lib/src/foundation/http_client.dart @@ -2,8 +2,6 @@ * Copyright (C) 2021-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ -library http_client; - import 'dart:async'; import 'dart:io'; import 'http_overrides.dart'; From dec3712b5b1e022849cbbcfe185b62c2d8237354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Sat, 9 Oct 2021 17:50:25 +0800 Subject: [PATCH 04/10] :recycle: chore: img dart --- kraken/lib/src/dom/elements/img.dart | 7 +++++-- kraken/lib/src/foundation/http_client.dart | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index aa6dc46006..8c87a42c68 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -421,8 +421,11 @@ class ImageElement extends Element { if (_source != null) { _removeStreamListener(); - Uri base = Uri.parse(elementManager.controller.href); - Uri resolvedUri = elementManager.controller.uriParser!.resolve(base, Uri.parse(_source!)); + Uri resolvedUri = Uri.parse(_source!); + if (!resolvedUri.hasAbsolutePath) { + Uri base = Uri.parse(elementManager.controller.href); + resolvedUri = elementManager.controller.uriParser!.resolve(base, resolvedUri); + } ImageProvider? imageProvider = _imageProvider ?? CSSUrl.parseUrl(resolvedUri, cache: properties['caching'], contextId: elementManager.contextId); diff --git a/kraken/lib/src/foundation/http_client.dart b/kraken/lib/src/foundation/http_client.dart index c8e1c8f820..f4d17ef21f 100644 --- a/kraken/lib/src/foundation/http_client.dart +++ b/kraken/lib/src/foundation/http_client.dart @@ -2,6 +2,7 @@ * Copyright (C) 2021-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ + import 'dart:async'; import 'dart:io'; import 'http_overrides.dart'; From d3bc5775dca5ce10fb3b305d3fbd9bd0b7ff394b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Mon, 11 Oct 2021 14:14:43 +0800 Subject: [PATCH 05/10] :bug: fix: restore resolve logic --- kraken/lib/src/dom/elements/img.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 8c87a42c68..aa6dc46006 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -421,11 +421,8 @@ class ImageElement extends Element { if (_source != null) { _removeStreamListener(); - Uri resolvedUri = Uri.parse(_source!); - if (!resolvedUri.hasAbsolutePath) { - Uri base = Uri.parse(elementManager.controller.href); - resolvedUri = elementManager.controller.uriParser!.resolve(base, resolvedUri); - } + Uri base = Uri.parse(elementManager.controller.href); + Uri resolvedUri = elementManager.controller.uriParser!.resolve(base, Uri.parse(_source!)); ImageProvider? imageProvider = _imageProvider ?? CSSUrl.parseUrl(resolvedUri, cache: properties['caching'], contextId: elementManager.contextId); From f702dea4a3a8c9d5d9258a5b7287d597103ad079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Mon, 11 Oct 2021 15:12:49 +0800 Subject: [PATCH 06/10] :bug: fix: header value can be object. --- kraken/lib/src/dom/elements/object.dart | 6 +++--- kraken/lib/src/foundation/http_client.dart | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/kraken/lib/src/dom/elements/object.dart b/kraken/lib/src/dom/elements/object.dart index 8a83db61d5..6c6149b552 100644 --- a/kraken/lib/src/dom/elements/object.dart +++ b/kraken/lib/src/dom/elements/object.dart @@ -50,11 +50,11 @@ class ObjectElement extends Element implements ObjectElementHost { _objectElementClient = _objectElementClientFactory(this); } - Future initElementClient() async { + Future initElementClient() async { try { await _objectElementClient.initElementClient(properties); - } catch (e) { - print(e); + } catch (error, stackTrace) { + print('$error\n$stackTrace'); } } diff --git a/kraken/lib/src/foundation/http_client.dart b/kraken/lib/src/foundation/http_client.dart index f4d17ef21f..4cfb947777 100644 --- a/kraken/lib/src/foundation/http_client.dart +++ b/kraken/lib/src/foundation/http_client.dart @@ -175,7 +175,7 @@ HttpHeaders createHttpHeaders({ Map? initialHeaders }) { } class _HttpHeaders implements HttpHeaders { - final Map _headers = {}; + final Map _headers = {}; _HttpHeaders({ Map? initialHeaders }) { if (initialHeaders != null) { _headers.addAll(initialHeaders); @@ -343,7 +343,8 @@ class _HttpHeaders implements HttpHeaders { @override String? value(String name) { - return _headers[name]; + Object? val = _headers[name]; + return val?.toString(); } @override From dbaa0444af9d5de47b5e66a9f376bd5946684452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Mon, 11 Oct 2021 15:35:40 +0800 Subject: [PATCH 07/10] :pencil2: fix: header value --- integration_tests/lib/custom/custom_object_element.dart | 8 ++++---- kraken/lib/src/foundation/http_client.dart | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/integration_tests/lib/custom/custom_object_element.dart b/integration_tests/lib/custom/custom_object_element.dart index 93d682b6ed..d188647a89 100644 --- a/integration_tests/lib/custom/custom_object_element.dart +++ b/integration_tests/lib/custom/custom_object_element.dart @@ -137,8 +137,8 @@ class CustomObjectElement implements ObjectElementClient { @override void dispose() { objectElementHost.updateChildTextureBox(null); - controller!.pause(); - controller!.dispose(); + controller?.pause(); + controller?.dispose(); controller = null; } @@ -147,8 +147,8 @@ class CustomObjectElement implements ObjectElementClient { @override void didDetachRenderer() { - controller!.pause(); - controller!.dispose(); + controller?.pause(); + controller?.dispose(); controller = null; } diff --git a/kraken/lib/src/foundation/http_client.dart b/kraken/lib/src/foundation/http_client.dart index 4cfb947777..d2a8bd3376 100644 --- a/kraken/lib/src/foundation/http_client.dart +++ b/kraken/lib/src/foundation/http_client.dart @@ -289,7 +289,6 @@ class _HttpHeaders implements HttpHeaders { } } - @override bool persistentConnection = false; @@ -298,7 +297,7 @@ class _HttpHeaders implements HttpHeaders { @override List operator [](String name) { - String? v = _headers[name]; + String? v = value(name); if (v != null) return [v]; return []; } @@ -316,7 +315,7 @@ class _HttpHeaders implements HttpHeaders { @override void forEach(void Function(String name, List values) action) { _headers.forEach((key, value) { - action(key, [value]); + action(key, [value.toString()]); }); } From 806e97fa39571571c0a9dc3a248bce1d1c541c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Mon, 11 Oct 2021 17:34:34 +0800 Subject: [PATCH 08/10] :recycle: refactor: add size validation for blob. --- .../lib/src/foundation/http_cache_object.dart | 35 +++++++++++++------ .../src/foundation/http_client_request.dart | 2 +- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/kraken/lib/src/foundation/http_cache_object.dart b/kraken/lib/src/foundation/http_cache_object.dart index 3a75be879a..85a3fb778e 100644 --- a/kraken/lib/src/foundation/http_cache_object.dart +++ b/kraken/lib/src/foundation/http_cache_object.dart @@ -134,11 +134,8 @@ class HttpCacheObject { bool isDateTimeValid() => expiredTime != null && expiredTime!.isAfter(DateTime.now()); // Validate the cache-control and expires. - Future hitLocalCache(HttpClientRequest request) async { - if (!valid) { - await read(); - } - return isDateTimeValid(); + bool hitLocalCache(HttpClientRequest request) { + return valid && isDateTimeValid(); } /// Read the index file. @@ -174,6 +171,12 @@ class HttpCacheObject { contentLength = byteData.getUint32(index, Endian.little); index += 4; + // Invalid cache blob size, mark as invalid. + if (await _blob.length != contentLength) { + _valid = false; + return; + } + // Read url. int urlLength = byteData.getUint32(index, Endian.little); index += 4; @@ -244,10 +247,11 @@ class HttpCacheObject { // Remove all the cached files. Future remove() async { - await Future.wait([ - _file.delete(), - _blob.remove(), - ]); + if (await _file.exists()) { + await _file.delete(); + } + await _blob.remove(); + _valid = false; } @@ -351,6 +355,15 @@ class HttpCacheObjectBlob extends EventSink> { HttpCacheObjectBlob(this.path) : _file = File(path); + // The length of the file. + Future get length async { + if (await exists()) { + return await _file.length(); + } else { + return 0; + } + } + @override void add(List data) { _writer ??= _file.openWrite(); @@ -384,7 +397,9 @@ class HttpCacheObjectBlob extends EventSink> { } Future remove() async { - await _file.delete(); + if (await _file.exists()) { + await _file.delete(); + } close(); } } diff --git a/kraken/lib/src/foundation/http_client_request.dart b/kraken/lib/src/foundation/http_client_request.dart index 63ce4fa9e8..94d8d0f131 100644 --- a/kraken/lib/src/foundation/http_client_request.dart +++ b/kraken/lib/src/foundation/http_client_request.dart @@ -135,7 +135,7 @@ class ProxyHttpClientRequest extends HttpClientRequest { if (HttpCacheController.mode != HttpCacheMode.NO_CACHE) { HttpCacheController cacheController = HttpCacheController.instance(origin); cacheObject = await cacheController.getCacheObject(request.uri); - if (await cacheObject.hitLocalCache(request)) { + if (cacheObject.hitLocalCache(request)) { HttpClientResponse? cacheResponse = await cacheObject.toHttpClientResponse(); if (cacheResponse != null) { // // Must cancel the ongoing request, make TCP connection closed. From 76b07c447af5c2519592dffcf14de3373c283db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Mon, 11 Oct 2021 18:00:42 +0800 Subject: [PATCH 09/10] :bug: fix: get cache multi times --- kraken/lib/src/foundation/http_cache.dart | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/kraken/lib/src/foundation/http_cache.dart b/kraken/lib/src/foundation/http_cache.dart index f5c455e4ea..b8fb098429 100644 --- a/kraken/lib/src/foundation/http_cache.dart +++ b/kraken/lib/src/foundation/http_cache.dart @@ -80,17 +80,19 @@ class HttpCacheController { // Get the CacheObject by uri, no validation needed here. Future getCacheObject(Uri uri) async { + HttpCacheObject cacheObject; + // L2 cache in memory. final String key = _getCacheKey(uri); if (_caches.containsKey(key)) { - return _caches[key]!; + cacheObject = _caches[key]!; + } else { + // Get cache in disk. + final int hash = key.hashCode; + final Directory cacheDirectory = await getCacheDirectory(); + cacheObject = HttpCacheObject(key, cacheDirectory.path, hash: hash, origin: _origin); } - // Get cache in disk. - final int hash = key.hashCode; - final Directory cacheDirectory = await getCacheDirectory(); - HttpCacheObject cacheObject = HttpCacheObject(key, cacheDirectory.path, hash: hash, origin: _origin); - await cacheObject.read(); return cacheObject; From 5a09f1976237f29f34b36acc3cb1524ceafad918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Mon, 11 Oct 2021 18:19:36 +0800 Subject: [PATCH 10/10] :recycle: chore: using base url. --- kraken/lib/src/dom/elements/a.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kraken/lib/src/dom/elements/a.dart b/kraken/lib/src/dom/elements/a.dart index 3e27781bd8..78ef978642 100644 --- a/kraken/lib/src/dom/elements/a.dart +++ b/kraken/lib/src/dom/elements/a.dart @@ -37,10 +37,11 @@ class AnchorElement extends Element { String? href = _href; if (href != null) { - Uri sourceUri = Uri.parse(elementManager.controller.href); - Uri resolvedUri = elementManager.controller.uriParser!.resolve(sourceUri, Uri.parse(href)); + String baseUrl = elementManager.controller.href; + Uri baseUri = Uri.parse(baseUrl); + Uri resolvedUri = elementManager.controller.uriParser!.resolve(baseUri, Uri.parse(href)); elementManager.controller.view.handleNavigationAction( - sourceUri.toString(), resolvedUri.toString(), _getNavigationType(resolvedUri.scheme)); + baseUrl, resolvedUri.toString(), _getNavigationType(resolvedUri.scheme)); } }