added android proxy controller support
This commit is contained in:
parent
2ab051fca3
commit
d3a834c36a
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
- Deprecated old classes/properties/methods to make them eventually compatible with other Platforms and WebView engines.
|
- Deprecated old classes/properties/methods to make them eventually compatible with other Platforms and WebView engines.
|
||||||
- Added Web support
|
- Added Web support
|
||||||
|
- Added `ProxyController` for Android
|
||||||
- Added `pauseAllMediaPlayback`, `setAllMediaPlaybackSuspended`, `closeAllMediaPresentations`, `requestMediaPlaybackState`, `isInFullscreen`, `getCameraCaptureState`, `setCameraCaptureState`, `getMicrophoneCaptureState`, `setMicrophoneCaptureState` WebView controller methods
|
- Added `pauseAllMediaPlayback`, `setAllMediaPlaybackSuspended`, `closeAllMediaPresentations`, `requestMediaPlaybackState`, `isInFullscreen`, `getCameraCaptureState`, `setCameraCaptureState`, `getMicrophoneCaptureState`, `setMicrophoneCaptureState` WebView controller methods
|
||||||
- Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS` WebView settings
|
- Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS` WebView settings
|
||||||
- Added support for `onPermissionRequest` event on iOS 15.0+
|
- Added support for `onPermissionRequest` event on iOS 15.0+
|
||||||
|
|
|
@ -12,6 +12,7 @@ import com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs.ChromeSafariB
|
||||||
import com.pichillilorenzo.flutter_inappwebview.credential_database.CredentialDatabaseHandler;
|
import com.pichillilorenzo.flutter_inappwebview.credential_database.CredentialDatabaseHandler;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserManager;
|
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserManager;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebViewManager;
|
import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebViewManager;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.proxy.ProxyManager;
|
||||||
|
|
||||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
||||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||||
|
@ -35,6 +36,7 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
|
||||||
public MyWebStorage myWebStorage;
|
public MyWebStorage myWebStorage;
|
||||||
public ServiceWorkerManager serviceWorkerManager;
|
public ServiceWorkerManager serviceWorkerManager;
|
||||||
public WebViewFeatureManager webViewFeatureManager;
|
public WebViewFeatureManager webViewFeatureManager;
|
||||||
|
public ProxyManager proxyManager;
|
||||||
public FlutterWebViewFactory flutterWebViewFactory;
|
public FlutterWebViewFactory flutterWebViewFactory;
|
||||||
public static ValueCallback<Uri> filePathCallbackLegacy;
|
public static ValueCallback<Uri> filePathCallbackLegacy;
|
||||||
public static ValueCallback<Uri[]> filePathCallback;
|
public static ValueCallback<Uri[]> filePathCallback;
|
||||||
|
@ -97,6 +99,7 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
|
||||||
credentialDatabaseHandler = new CredentialDatabaseHandler(this);
|
credentialDatabaseHandler = new CredentialDatabaseHandler(this);
|
||||||
}
|
}
|
||||||
webViewFeatureManager = new WebViewFeatureManager(this);
|
webViewFeatureManager = new WebViewFeatureManager(this);
|
||||||
|
proxyManager = new ProxyManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -141,6 +144,10 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
|
||||||
webViewFeatureManager.dispose();
|
webViewFeatureManager.dispose();
|
||||||
webViewFeatureManager = null;
|
webViewFeatureManager = null;
|
||||||
}
|
}
|
||||||
|
if (proxyManager != null) {
|
||||||
|
proxyManager.dispose();
|
||||||
|
proxyManager = null;
|
||||||
|
}
|
||||||
filePathCallbackLegacy = null;
|
filePathCallbackLegacy = null;
|
||||||
filePathCallback = null;
|
filePathCallback = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
package com.pichillilorenzo.flutter_inappwebview.proxy;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.webkit.ProxyConfig;
|
||||||
|
import androidx.webkit.ProxyController;
|
||||||
|
import androidx.webkit.WebViewFeature;
|
||||||
|
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.ProxyRuleExt;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import io.flutter.plugin.common.MethodCall;
|
||||||
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
|
|
||||||
|
public class ProxyManager implements MethodChannel.MethodCallHandler {
|
||||||
|
|
||||||
|
static final String LOG_TAG = "ProxyManager";
|
||||||
|
|
||||||
|
public MethodChannel channel;
|
||||||
|
@Nullable
|
||||||
|
public static ProxyController proxyController;
|
||||||
|
@Nullable
|
||||||
|
public InAppWebViewFlutterPlugin plugin;
|
||||||
|
|
||||||
|
public ProxyManager(final InAppWebViewFlutterPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
channel = new MethodChannel(plugin.messenger, "com.pichillilorenzo/flutter_inappwebview_proxycontroller");
|
||||||
|
channel.setMethodCallHandler(this);
|
||||||
|
if (WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) {
|
||||||
|
proxyController = ProxyController.getInstance();
|
||||||
|
} else {
|
||||||
|
proxyController = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
|
||||||
|
switch (call.method) {
|
||||||
|
case "setProxyOverride":
|
||||||
|
if (proxyController != null) {
|
||||||
|
HashMap<String, Object> settingsMap = (HashMap<String, Object>) call.argument("settings");
|
||||||
|
ProxySettings settings = new ProxySettings();
|
||||||
|
if (settingsMap != null) {
|
||||||
|
settings.parse(settingsMap);
|
||||||
|
}
|
||||||
|
setProxyOverride(settings, result);
|
||||||
|
} else {
|
||||||
|
result.success(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "clearProxyOverride":
|
||||||
|
if (proxyController != null ) {
|
||||||
|
clearProxyOverride(result);
|
||||||
|
} else {
|
||||||
|
result.success(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result.notImplemented();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setProxyOverride(ProxySettings settings, final MethodChannel.Result result) {
|
||||||
|
if (proxyController != null) {
|
||||||
|
ProxyConfig.Builder proxyConfigBuilder = new ProxyConfig.Builder();
|
||||||
|
for (String bypassRule : settings.bypassRules) {
|
||||||
|
proxyConfigBuilder.addBypassRule(bypassRule);
|
||||||
|
}
|
||||||
|
for (String direct : settings.directs) {
|
||||||
|
proxyConfigBuilder.addDirect(direct);
|
||||||
|
}
|
||||||
|
for (ProxyRuleExt proxyRule : settings.proxyRules) {
|
||||||
|
if (proxyRule.getSchemeFilter() != null) {
|
||||||
|
proxyConfigBuilder.addProxyRule(proxyRule.getUrl(), proxyRule.getSchemeFilter());
|
||||||
|
} else {
|
||||||
|
proxyConfigBuilder.addProxyRule(proxyRule.getUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (settings.bypassSimpleHostnames != null && settings.bypassSimpleHostnames) {
|
||||||
|
proxyConfigBuilder.bypassSimpleHostnames();
|
||||||
|
}
|
||||||
|
if (settings.removeImplicitRules != null && settings.removeImplicitRules) {
|
||||||
|
proxyConfigBuilder.removeImplicitRules();
|
||||||
|
}
|
||||||
|
proxyController.setProxyOverride(proxyConfigBuilder.build(), new Executor() {
|
||||||
|
@Override
|
||||||
|
public void execute(Runnable command) {
|
||||||
|
command.run();
|
||||||
|
}
|
||||||
|
}, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
result.success(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearProxyOverride(final MethodChannel.Result result) {
|
||||||
|
if (proxyController != null) {
|
||||||
|
proxyController.clearProxyOverride(new Executor() {
|
||||||
|
@Override
|
||||||
|
public void execute(Runnable command) {
|
||||||
|
command.run();
|
||||||
|
}
|
||||||
|
}, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
result.success(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
channel.setMethodCallHandler(null);
|
||||||
|
if (proxyController != null) {
|
||||||
|
// Clears the proxy settings
|
||||||
|
proxyController.clearProxyOverride(new Executor() {
|
||||||
|
@Override
|
||||||
|
public void execute(Runnable command) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
proxyController = null;
|
||||||
|
}
|
||||||
|
plugin = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package com.pichillilorenzo.flutter_inappwebview.proxy;
|
||||||
|
|
||||||
|
import androidx.webkit.ProxyConfig;
|
||||||
|
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.ISettings;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.ProxyRuleExt;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ProxySettings implements ISettings<ProxyConfig> {
|
||||||
|
List<String> bypassRules = new ArrayList<>();
|
||||||
|
List<String> directs = new ArrayList<>();
|
||||||
|
List<ProxyRuleExt> proxyRules = new ArrayList<>();
|
||||||
|
Boolean bypassSimpleHostnames = null;
|
||||||
|
Boolean removeImplicitRules = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProxySettings parse(Map<String, Object> settings) {
|
||||||
|
for (Map.Entry<String, Object> pair : settings.entrySet()) {
|
||||||
|
String key = pair.getKey();
|
||||||
|
Object value = pair.getValue();
|
||||||
|
if (value == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case "bypassRules":
|
||||||
|
bypassRules = (List<String>) value;
|
||||||
|
break;
|
||||||
|
case "directs":
|
||||||
|
directs = (List<String>) value;
|
||||||
|
break;
|
||||||
|
case "proxyRules":
|
||||||
|
proxyRules = new ArrayList<>();
|
||||||
|
List<Map<String, String>> proxyRuleMapList = (List<Map<String, String>>) value;
|
||||||
|
for (Map<String, String> proxyRuleMap : proxyRuleMapList) {
|
||||||
|
ProxyRuleExt proxyRuleExt = ProxyRuleExt.fromMap(proxyRuleMap);
|
||||||
|
if (proxyRuleExt != null) {
|
||||||
|
proxyRules.add(proxyRuleExt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "bypassSimpleHostnames":
|
||||||
|
bypassSimpleHostnames = (Boolean) value;
|
||||||
|
break;
|
||||||
|
case "removeImplicitRules":
|
||||||
|
removeImplicitRules = (Boolean) value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> toMap() {
|
||||||
|
List<Map<String, String>> proxyRuleMapList = new ArrayList<>();
|
||||||
|
for (ProxyRuleExt proxyRuleExt : proxyRules) {
|
||||||
|
proxyRuleMapList.add(proxyRuleExt.toMap());
|
||||||
|
}
|
||||||
|
Map<String, Object> settings = new HashMap<>();
|
||||||
|
settings.put("bypassRules", bypassRules);
|
||||||
|
settings.put("directs", directs);
|
||||||
|
settings.put("proxyRules", proxyRuleMapList);
|
||||||
|
settings.put("bypassSimpleHostnames", bypassSimpleHostnames);
|
||||||
|
settings.put("removeImplicitRules", removeImplicitRules);
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getRealSettings(ProxyConfig proxyConfig) {
|
||||||
|
Map<String, Object> realSettings = toMap();
|
||||||
|
List<Map<String, String>> proxyRuleMapList = new ArrayList<>();
|
||||||
|
List<ProxyConfig.ProxyRule> proxyRules = proxyConfig.getProxyRules();
|
||||||
|
for (ProxyConfig.ProxyRule proxyRule : proxyRules) {
|
||||||
|
Map<String, String> proxyRuleMap = new HashMap<>();
|
||||||
|
proxyRuleMap.put("url", proxyRule.getUrl());
|
||||||
|
proxyRuleMap.put("schemeFilter", proxyRule.getSchemeFilter());
|
||||||
|
proxyRuleMapList.add(proxyRuleMap);
|
||||||
|
}
|
||||||
|
realSettings.put("bypassRules", proxyConfig.getBypassRules());
|
||||||
|
realSettings.put("proxyRules", proxyRuleMapList);
|
||||||
|
return realSettings;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package com.pichillilorenzo.flutter_inappwebview.types;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ProxyRuleExt {
|
||||||
|
@Nullable
|
||||||
|
private String schemeFilter;
|
||||||
|
@NonNull
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
public ProxyRuleExt(@Nullable String schemeFilter, @NonNull String url) {
|
||||||
|
this.schemeFilter = schemeFilter;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static ProxyRuleExt fromMap(@Nullable Map<String, String> map) {
|
||||||
|
if (map == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String url = (String) map.get("url");
|
||||||
|
String schemeFilter = (String) map.get("schemeFilter");
|
||||||
|
return new ProxyRuleExt(schemeFilter, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> toMap() {
|
||||||
|
Map<String, String> proxyRuleMap = new HashMap<>();
|
||||||
|
proxyRuleMap.put("url", url);
|
||||||
|
proxyRuleMap.put("schemeFilter", schemeFilter);
|
||||||
|
return proxyRuleMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getSchemeFilter() {
|
||||||
|
return schemeFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSchemeFilter(@Nullable String schemeFilter) {
|
||||||
|
this.schemeFilter = schemeFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(@NonNull String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
ProxyRuleExt that = (ProxyRuleExt) o;
|
||||||
|
|
||||||
|
if (schemeFilter != null ? !schemeFilter.equals(that.schemeFilter) : that.schemeFilter != null)
|
||||||
|
return false;
|
||||||
|
return url.equals(that.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = schemeFilter != null ? schemeFilter.hashCode() : 0;
|
||||||
|
result = 31 * result + url.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ProxyRuleExt{" +
|
||||||
|
"schemeFilter='" + schemeFilter + '\'' +
|
||||||
|
", url='" + url + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,16 @@ Future main() async {
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// var proxyAvailable = await WebViewFeature.isFeatureSupported(
|
||||||
|
// WebViewFeature.PROXY_OVERRIDE);
|
||||||
|
// if (proxyAvailable) {
|
||||||
|
// ProxyController proxyController = ProxyController.instance();
|
||||||
|
// await proxyController.clearProxyOverride();
|
||||||
|
// await proxyController.setProxyOverride(settings: ProxySettings(
|
||||||
|
// proxyRules: [ProxyRule(url: "https://192.168.1.102:4433")],
|
||||||
|
// ));
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
runApp(MyApp());
|
runApp(MyApp());
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
export 'service_worker_controller.dart';
|
export 'service_worker_controller.dart';
|
||||||
export 'webview_feature.dart';
|
export 'webview_feature.dart';
|
||||||
|
export 'proxy_controller.dart';
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'webview_feature.dart';
|
||||||
|
import '../in_app_webview/webview.dart';
|
||||||
|
import '../types/main.dart';
|
||||||
|
|
||||||
|
///Manages setting and clearing a process-specific override for the Android system-wide proxy settings that govern network requests made by [WebView].
|
||||||
|
///
|
||||||
|
///[WebView] may make network requests in order to fetch content that is not otherwise read from the file system or provided directly by application code.
|
||||||
|
///In this case by default the system-wide Android network proxy settings are used to redirect requests to appropriate proxy servers.
|
||||||
|
///
|
||||||
|
///In the rare case that it is necessary for an application to explicitly specify its proxy configuration,
|
||||||
|
///this API may be used to explicitly specify the proxy rules that govern WebView initiated network requests.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android native WebView ([Official API - ProxyController](https://developer.android.com/reference/androidx/webkit/ProxyController))
|
||||||
|
class ProxyController {
|
||||||
|
static ProxyController? _instance;
|
||||||
|
static const MethodChannel _channel = const MethodChannel(
|
||||||
|
'com.pichillilorenzo/flutter_inappwebview_proxycontroller');
|
||||||
|
|
||||||
|
///Gets the [ProxyController] shared instance.
|
||||||
|
///
|
||||||
|
///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.PROXY_OVERRIDE].
|
||||||
|
static ProxyController instance() {
|
||||||
|
return (_instance != null) ? _instance! : _init();
|
||||||
|
}
|
||||||
|
|
||||||
|
static ProxyController _init() {
|
||||||
|
_channel.setMethodCallHandler(_handleMethod);
|
||||||
|
_instance = ProxyController();
|
||||||
|
return _instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<dynamic> _handleMethod(MethodCall call) async {
|
||||||
|
// ProxyController controller = ProxyController.instance();
|
||||||
|
switch (call.method) {
|
||||||
|
default:
|
||||||
|
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||||
|
}
|
||||||
|
// return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Sets [ProxySettings] which will be used by all [WebView]s in the app.
|
||||||
|
///URLs that match patterns in the bypass list will not be directed to any proxy.
|
||||||
|
///Instead, the request will be made directly to the origin specified by the URL.
|
||||||
|
///Network connections are not guaranteed to immediately use the new proxy setting; wait for the method to return before loading a page.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android native WebView ([Official API - ProxyController.setProxyOverride](https://developer.android.com/reference/androidx/webkit/ProxyController#setProxyOverride(androidx.webkit.ProxyConfig,%20java.util.concurrent.Executor,%20java.lang.Runnable)))
|
||||||
|
Future<void> setProxyOverride({required ProxySettings settings}) async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent("settings", () => settings.toMap());
|
||||||
|
return await _channel.invokeMethod('setProxyOverride', args);
|
||||||
|
}
|
||||||
|
|
||||||
|
///Clears the proxy settings.
|
||||||
|
///Network connections are not guaranteed to immediately use the new proxy setting; wait for the method to return before loading a page.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android native WebView ([Official API - ProxyController.clearProxyOverride](https://developer.android.com/reference/androidx/webkit/ProxyController#clearProxyOverride(java.util.concurrent.Executor,%20java.lang.Runnable)))
|
||||||
|
Future<void> clearProxyOverride() async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
return await _channel.invokeMethod('clearProxyOverride', args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///Class that represents the settings used to configure the [ProxyController].
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android native WebView ([Official API - ProxyConfig](https://developer.android.com/reference/androidx/webkit/ProxyConfig))
|
||||||
|
class ProxySettings {
|
||||||
|
///List of bypass rules.
|
||||||
|
///
|
||||||
|
///A bypass rule describes URLs that should skip proxy override settings and make a direct connection instead. These can be URLs or IP addresses. Wildcards are accepted.
|
||||||
|
///For instance, the rule "*example.com" would mean that requests to "http://example.com" and "www.example.com" would not be directed to any proxy,
|
||||||
|
///instead, would be made directly to the origin specified by the URL.
|
||||||
|
List<String> bypassRules;
|
||||||
|
|
||||||
|
///List of scheme filters.
|
||||||
|
///
|
||||||
|
///URLs that match these scheme filters are connected to directly instead of using a proxy server.
|
||||||
|
List<String> directs;
|
||||||
|
|
||||||
|
///List of proxy rules to be used for all URLs. This method can be called multiple times to add multiple rules. Additional rules have decreasing precedence.
|
||||||
|
///
|
||||||
|
///Proxy is a string in the format `[scheme://]host[:port]`.
|
||||||
|
///Scheme is optional, if present must be `HTTP`, `HTTPS` or [SOCKS](https://tools.ietf.org/html/rfc1928) and defaults to `HTTP`.
|
||||||
|
///Host is one of an IPv6 literal with brackets, an IPv4 literal or one or more labels separated by a period.
|
||||||
|
///Port number is optional and defaults to `80` for `HTTP`, `443` for `HTTPS` and `1080` for `SOCKS`.
|
||||||
|
///
|
||||||
|
///The correct syntax for hosts is defined by [RFC 3986](https://tools.ietf.org/html/rfc3986#section-3.2.2).
|
||||||
|
List<ProxyRule> proxyRules;
|
||||||
|
|
||||||
|
///Hostnames without a period in them (and that are not IP literals) will skip proxy settings and be connected to directly instead. Examples: `"abc"`, `"local"`, `"some-domain"`.
|
||||||
|
///
|
||||||
|
///Hostnames with a trailing dot are not considered simple by this definition.
|
||||||
|
bool? bypassSimpleHostnames;
|
||||||
|
|
||||||
|
///By default, certain hostnames implicitly bypass the proxy if they are link-local IPs, or localhost addresses.
|
||||||
|
///For instance hostnames matching any of (non-exhaustive list):
|
||||||
|
///- localhost
|
||||||
|
///- *.localhost
|
||||||
|
///- [::1]
|
||||||
|
///- 127.0.0.1/8
|
||||||
|
///- 169.254/16
|
||||||
|
///- [FE80::]/10
|
||||||
|
///Set this to `true` to override the default behavior and force localhost and link-local URLs to be sent through the proxy.
|
||||||
|
bool? removeImplicitRules;
|
||||||
|
|
||||||
|
ProxySettings(
|
||||||
|
{this.bypassRules = const [],
|
||||||
|
this.directs = const [],
|
||||||
|
this.proxyRules = const [],
|
||||||
|
this.bypassSimpleHostnames,
|
||||||
|
this.removeImplicitRules});
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
"bypassRules": bypassRules,
|
||||||
|
"directs": directs,
|
||||||
|
"proxyRules": proxyRules.map((e) => e.toMap()).toList(),
|
||||||
|
"bypassSimpleHostnames": bypassSimpleHostnames,
|
||||||
|
"removeImplicitRules": removeImplicitRules
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static ProxySettings fromMap(Map<String, dynamic> map) {
|
||||||
|
var settings = ProxySettings();
|
||||||
|
settings.bypassRules = map["bypassRules"];
|
||||||
|
settings.directs = map["directs"];
|
||||||
|
settings.proxyRules = (map["proxyRules"].cast<Map<String, dynamic>>()
|
||||||
|
as List<Map<String, dynamic>>)
|
||||||
|
.map((e) => ProxyRule.fromMap(e)) as List<ProxyRule>;
|
||||||
|
settings.bypassSimpleHostnames = map["bypassSimpleHostnames"];
|
||||||
|
settings.removeImplicitRules = map["removeImplicitRules"];
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return this.toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return toMap().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProxySettings copy() {
|
||||||
|
return ProxySettings.fromMap(this.toMap());
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,8 @@ class ServiceWorkerController {
|
||||||
'com.pichillilorenzo/flutter_inappwebview_serviceworkercontroller');
|
'com.pichillilorenzo/flutter_inappwebview_serviceworkercontroller');
|
||||||
|
|
||||||
///Gets the [ServiceWorkerController] shared instance.
|
///Gets the [ServiceWorkerController] shared instance.
|
||||||
|
///
|
||||||
|
///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.SERVICE_WORKER_BASIC_USAGE].
|
||||||
static ServiceWorkerController instance() {
|
static ServiceWorkerController instance() {
|
||||||
return (_instance != null) ? _instance! : _init();
|
return (_instance != null) ? _instance! : _init();
|
||||||
}
|
}
|
||||||
|
@ -23,7 +25,8 @@ class ServiceWorkerController {
|
||||||
|
|
||||||
ServiceWorkerClient? get serviceWorkerClient => _serviceWorkerClient;
|
ServiceWorkerClient? get serviceWorkerClient => _serviceWorkerClient;
|
||||||
|
|
||||||
///Sets the service worker client.
|
///Sets the client to capture service worker related callbacks.
|
||||||
|
///A [ServiceWorkerClient] should be set before any service workers are active, e.g. a safe place is before any WebView instances are created or pages loaded.
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android native WebView ([Official API - ServiceWorkerControllerCompat.setServiceWorkerClient](https://developer.android.com/reference/androidx/webkit/ServiceWorkerControllerCompat#setServiceWorkerClient(androidx.webkit.ServiceWorkerClientCompat)))
|
///- Android native WebView ([Official API - ServiceWorkerControllerCompat.setServiceWorkerClient](https://developer.android.com/reference/androidx/webkit/ServiceWorkerControllerCompat#setServiceWorkerClient(androidx.webkit.ServiceWorkerClientCompat)))
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import '../in_app_webview/in_app_webview_controller.dart';
|
||||||
|
import '../in_app_webview/in_app_webview_settings.dart';
|
||||||
|
|
||||||
///Class that represents an Android-specific utility class for checking which WebView Support Library features are supported on the device.
|
///Class that represents an Android-specific utility class for checking which WebView Support Library features are supported on the device.
|
||||||
class WebViewFeature {
|
class WebViewFeature {
|
||||||
|
@ -70,15 +72,15 @@ class WebViewFeature {
|
||||||
@override
|
@override
|
||||||
String toString() => _value;
|
String toString() => _value;
|
||||||
|
|
||||||
///
|
///This feature covers [InAppWebViewController.createWebMessageChannel].
|
||||||
static const CREATE_WEB_MESSAGE_CHANNEL =
|
static const CREATE_WEB_MESSAGE_CHANNEL =
|
||||||
const WebViewFeature._internal("CREATE_WEB_MESSAGE_CHANNEL");
|
const WebViewFeature._internal("CREATE_WEB_MESSAGE_CHANNEL");
|
||||||
|
|
||||||
///
|
///This feature covers [InAppWebViewSettings.disabledActionModeMenuItems].
|
||||||
static const DISABLED_ACTION_MODE_MENU_ITEMS =
|
static const DISABLED_ACTION_MODE_MENU_ITEMS =
|
||||||
const WebViewFeature._internal("DISABLED_ACTION_MODE_MENU_ITEMS");
|
const WebViewFeature._internal("DISABLED_ACTION_MODE_MENU_ITEMS");
|
||||||
|
|
||||||
///
|
///This feature covers [InAppWebViewSettings.forceDark].
|
||||||
static const FORCE_DARK = const WebViewFeature._internal("FORCE_DARK");
|
static const FORCE_DARK = const WebViewFeature._internal("FORCE_DARK");
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
|
@ -60,11 +60,6 @@ class InAppBrowserClassSettings {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Map<String, dynamic> instanceToMap(
|
|
||||||
InAppBrowserClassSettings settings) {
|
|
||||||
return settings.toMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return this.toMap();
|
return this.toMap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,3 +145,5 @@ export 'window_features.dart';
|
||||||
export 'web_resource_error.dart';
|
export 'web_resource_error.dart';
|
||||||
export 'web_resource_error_type.dart';
|
export 'web_resource_error_type.dart';
|
||||||
export 'media_capture_state.dart';
|
export 'media_capture_state.dart';
|
||||||
|
export 'proxy_rule.dart';
|
||||||
|
export 'proxy_scheme_filter.dart';
|
|
@ -0,0 +1,36 @@
|
||||||
|
import 'proxy_scheme_filter.dart';
|
||||||
|
|
||||||
|
///Class that holds a scheme filter and a proxy URL.
|
||||||
|
class ProxyRule {
|
||||||
|
///Represents the scheme filter.
|
||||||
|
ProxySchemeFilter? schemeFilter;
|
||||||
|
|
||||||
|
///Represents the proxy URL.
|
||||||
|
String url;
|
||||||
|
|
||||||
|
ProxyRule({required this.url, this.schemeFilter});
|
||||||
|
|
||||||
|
///Gets a possible [ProxyRule] instance from a [Map] value.
|
||||||
|
static ProxyRule? fromMap(Map<String, dynamic>? map) {
|
||||||
|
return map != null
|
||||||
|
? ProxyRule(
|
||||||
|
url: map["url"],
|
||||||
|
schemeFilter: ProxySchemeFilter.fromValue(map["schemeFilter"]))
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Converts instance to a map.
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {"url": url, "schemeFilter": schemeFilter?.toValue()};
|
||||||
|
}
|
||||||
|
|
||||||
|
///Converts instance to a map.
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return this.toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return toMap().toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import '../android/proxy_controller.dart';
|
||||||
|
|
||||||
|
///Class that represent scheme filters used by [ProxyController].
|
||||||
|
class ProxySchemeFilter {
|
||||||
|
final String _value;
|
||||||
|
|
||||||
|
const ProxySchemeFilter._internal(this._value);
|
||||||
|
|
||||||
|
///Set of all values of [ProxySchemeFilter].
|
||||||
|
static final Set<ProxySchemeFilter> values = [
|
||||||
|
ProxySchemeFilter.MATCH_ALL_SCHEMES,
|
||||||
|
ProxySchemeFilter.MATCH_HTTP,
|
||||||
|
ProxySchemeFilter.MATCH_HTTPS,
|
||||||
|
].toSet();
|
||||||
|
|
||||||
|
///Gets a possible [ProxySchemeFilter] instance from a [String] value.
|
||||||
|
static ProxySchemeFilter? fromValue(String? value) {
|
||||||
|
if (value != null) {
|
||||||
|
try {
|
||||||
|
return ProxySchemeFilter.values
|
||||||
|
.firstWhere((element) => element.toValue() == value);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Gets [String] value.
|
||||||
|
String toValue() => _value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => _value;
|
||||||
|
|
||||||
|
///Matches all schemes.
|
||||||
|
static const MATCH_ALL_SCHEMES = const ProxySchemeFilter._internal("*");
|
||||||
|
|
||||||
|
///HTTP scheme.
|
||||||
|
static const MATCH_HTTP = const ProxySchemeFilter._internal("http");
|
||||||
|
|
||||||
|
///HTTPS scheme.
|
||||||
|
static const MATCH_HTTPS =
|
||||||
|
const ProxySchemeFilter._internal("https");
|
||||||
|
|
||||||
|
bool operator ==(value) => value == _value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => _value.hashCode;
|
||||||
|
}
|
|
@ -1,12 +1,14 @@
|
||||||
// Example of the server https is taken from here: https://engineering.circle.com/https-authorized-certs-with-node-js-315e548354a2
|
// Example of the server https is taken from here: https://engineering.circle.com/https-authorized-certs-with-node-js-315e548354a2
|
||||||
// Conversion of client1-crt.pem to certificate.pfx: https://stackoverflow.com/a/38408666/4637638
|
// Conversion of client1-crt.pem to certificate.pfx: https://stackoverflow.com/a/38408666/4637638
|
||||||
const express = require('express')
|
const express = require('express');
|
||||||
const https = require('https')
|
const proxy = require('express-http-proxy');
|
||||||
const cors = require('cors')
|
const https = require('https');
|
||||||
const auth = require('basic-auth')
|
const cors = require('cors');
|
||||||
const app = express()
|
const auth = require('basic-auth');
|
||||||
const appHttps = express()
|
const app = express();
|
||||||
const appAuthBasic = express()
|
const appHttps = express();
|
||||||
|
const appAuthBasic = express();
|
||||||
|
const appProxy = express();
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
|
@ -20,6 +22,7 @@ var options = {
|
||||||
rejectUnauthorized: false
|
rejectUnauthorized: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
appHttps.use('/', proxy('www.google.com'));
|
||||||
appHttps.get('/', (req, res) => {
|
appHttps.get('/', (req, res) => {
|
||||||
console.log(JSON.stringify(req.headers))
|
console.log(JSON.stringify(req.headers))
|
||||||
const cert = req.connection.getPeerCertificate()
|
const cert = req.connection.getPeerCertificate()
|
||||||
|
@ -197,3 +200,7 @@ app.get("/test-download-file", (req, res) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
app.listen(8082)
|
app.listen(8082)
|
||||||
|
|
||||||
|
appProxy.use(cors());
|
||||||
|
appProxy.use('/', proxy('www.google.com'));
|
||||||
|
appProxy.listen(8083);
|
|
@ -108,6 +108,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||||
},
|
},
|
||||||
|
"es6-promise": {
|
||||||
|
"version": "4.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
||||||
|
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
|
||||||
|
},
|
||||||
"escape-html": {
|
"escape-html": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
|
@ -155,6 +160,31 @@
|
||||||
"vary": "~1.1.2"
|
"vary": "~1.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"express-http-proxy": {
|
||||||
|
"version": "1.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-http-proxy/-/express-http-proxy-1.6.3.tgz",
|
||||||
|
"integrity": "sha512-/l77JHcOUrDUX8V67E287VEUQT0lbm71gdGVoodnlWBziarYKgMcpqT7xvh/HM8Jv52phw8Bd8tY+a7QjOr7Yg==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "^3.0.1",
|
||||||
|
"es6-promise": "^4.1.1",
|
||||||
|
"raw-body": "^2.3.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "3.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||||
|
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"finalhandler": {
|
"finalhandler": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "latest",
|
"express": "latest",
|
||||||
|
"express-http-proxy": "^1.6.3",
|
||||||
"https": "latest",
|
"https": "latest",
|
||||||
"multiparty": "^4.2.2"
|
"multiparty": "^4.2.2"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue