diff --git a/android/src/main/java/com/browserstack/fluttersystemproxy/flutter_system_proxy/FlutterSystemProxyPlugin.java b/android/src/main/java/com/browserstack/fluttersystemproxy/flutter_system_proxy/FlutterSystemProxyPlugin.java index d30bc29..eebebb0 100644 --- a/android/src/main/java/com/browserstack/fluttersystemproxy/flutter_system_proxy/FlutterSystemProxyPlugin.java +++ b/android/src/main/java/com/browserstack/fluttersystemproxy/flutter_system_proxy/FlutterSystemProxyPlugin.java @@ -3,6 +3,10 @@ import androidx.annotation.NonNull; import android.text.TextUtils; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.URI; import java.util.HashMap; import java.util.Map; @@ -31,12 +35,26 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBindin @Override public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { if (call.method.equals("getDeviceProxy")) { - Map map = new HashMap(); - map.put("http.proxyHost", System.getProperty("http.proxyHost")); - map.put("http.proxyPort", System.getProperty("http.proxyPort")); - map.put("https.proxyHost", System.getProperty("https.proxyHost")); - map.put("https.proxyPort", System.getProperty("https.proxyPort")); - result.success(map); + HashMap _map = new HashMap() { + { + put("host", null); + put("port", null); + } + }; + String url = call.argument("url"); + ProxySelector selector = ProxySelector.getDefault(); + try { + for (Proxy proxy : selector.select(new URI(url))) { + if (proxy.type() == Proxy.Type.HTTP) { + InetSocketAddress addr = (InetSocketAddress) proxy.address(); + _map.put("host", addr.getHostName()); + _map.put("port", Integer.toString(addr.getPort())); + } + } + result.success(_map); + } catch (Exception ex) { + result.error("URL Error",ex.getMessage(),null); + } } else { result.notImplemented(); } diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 00bccc7..d44d61f 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 30 + compileSdkVersion 31 defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 9367d48..9625e10 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 11.0 diff --git a/example/ios/Podfile b/example/ios/Podfile index 1e8c3c9..313ea4a 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index be03354..0b05a0d 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -14,9 +14,9 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_system_proxy/ios" SPEC CHECKSUMS: - Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_system_proxy: 96eb97e3857a1d1bc533a6f7387a1f0dcb63d782 -PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c +PODFILE CHECKSUM: 7368163408c647b7eb699d0d788ba6718e18fb8d -COCOAPODS: 1.11.2 +COCOAPODS: 1.11.3 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index d9b1a5c..aa8ab3e 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ @@ -68,7 +68,6 @@ 3D695DD80D4DCA36C1B83028 /* Pods-Runner.release.xcconfig */, 2B955829EF2302761365E6DB /* Pods-Runner.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -156,7 +155,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -263,7 +262,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -340,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; @@ -358,8 +357,11 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.browserstack.fluttersystemproxy.flutterSystemProxyExample; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.browserstack.fluttersystemproxy.flutterSystemProxy; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -414,7 +416,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; @@ -463,7 +465,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; @@ -482,8 +484,11 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.browserstack.fluttersystemproxy.flutterSystemProxyExample; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.browserstack.fluttersystemproxy.flutterSystemProxy; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -501,8 +506,11 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.browserstack.fluttersystemproxy.flutterSystemProxyExample; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.browserstack.fluttersystemproxy.flutterSystemProxy; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140c..3db53b6 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable diff --git a/example/lib/main.dart b/example/lib/main.dart index 9a89b3e..68a6684 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -90,6 +90,6 @@ Future fetchLocalHost() async { return response.toString(); } catch (e) { print(e); - return "Error"; + return e.toString(); } } diff --git a/ios/Classes/SwiftFlutterSystemProxyPlugin.swift b/ios/Classes/SwiftFlutterSystemProxyPlugin.swift index f881fc3..4c588de 100644 --- a/ios/Classes/SwiftFlutterSystemProxyPlugin.swift +++ b/ios/Classes/SwiftFlutterSystemProxyPlugin.swift @@ -11,28 +11,77 @@ public class SwiftFlutterSystemProxyPlugin: NSObject, FlutterPlugin { public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "getDeviceProxy": - let systemProxySettings = CFNetworkCopySystemProxySettings()?.takeUnretainedValue() ?? [:] as CFDictionary - result(systemProxySettings as NSDictionary) - break - case "executePAC": - let systemProxySettings = CFNetworkCopySystemProxySettings()?.takeUnretainedValue() ?? [:] as CFDictionary - let proxyDict = systemProxySettings as NSDictionary - if(proxyDict.value(forKey:"ProxyAutoConfigEnable")as! Bool){ - let args = call.arguments as! NSDictionary - let url = args.value(forKey:"url") as! String - let host = args.value(forKey:"host") as! String - let js = args.value(forKey:"js") as! String - let jsEngine:JSContext = JSContext() - jsEngine.evaluateScript(js) - let fn = "FindProxyForURL(\"" + url + "\",\""+host+"\")" - let proxy = jsEngine.evaluateScript(fn) - result(proxy?.toString()) - }else{ - result("DIRECT") - } + let args = call.arguments as! NSDictionary + let url = args.value(forKey:"url") as! String + var dict:[String:Any] = [:] + findProxyFromEnvironment(url: url,callback: { host, port in + dict["host"] = host + dict["port"] = port + result(dict) + }) break default: result(FlutterMethodNotImplemented) } } + + func findProxyFromEnvironment(url: String,callback: @escaping (_ host:String?,_ port:Int?)->Void) { + let proxConfigDict = CFNetworkCopySystemProxySettings()?.takeUnretainedValue() as NSDictionary? + if(proxConfigDict != nil){ + if(proxConfigDict!["ProxyAutoConfigEnable"] as? Int == 1){ + let pacUrl = proxConfigDict!["ProxyAutoConfigURLString"] as? String + let pacContent = proxConfigDict!["ProxyAutoConfigJavaScript"] as? String + if(pacContent != nil){ + self.handlePacContent(pacContent: pacContent! as String, url: url, callback: callback) + } + downloadPac(pacUrl: pacUrl!, callback: { pacContent,error in + + if(error != nil){ + callback(nil,nil) + }else{ + self.handlePacContent(pacContent: pacContent!, url: url, callback: callback) + } + }) + } else if (proxConfigDict!["HTTPEnable"] as? Int == 1){ + callback((proxConfigDict!["HTTPProxy"] as? String),(proxConfigDict!["HTTPPort"] as? Int)) + } else if ( proxConfigDict!["HTTPSEnable"] as? Int == 1){ + callback((proxConfigDict!["HTTPSProxy"] as? String),(proxConfigDict!["HTTPSPort"] as? Int)) + } else { + callback(nil,nil) + } + } + } + + func handlePacContent(pacContent: String,url: String, callback:(_ host:String?,_ port:Int?)->Void){ + let proxies = CFNetworkCopyProxiesForAutoConfigurationScript(pacContent as CFString, CFURLCreateWithString(kCFAllocatorDefault, url as CFString, nil), nil)!.takeUnretainedValue() as? [[CFString: Any]] ?? []; + if(proxies.count > 0){ + let proxy = proxies.first{$0[kCFProxyTypeKey] as! CFString == kCFProxyTypeHTTP || $0[kCFProxyTypeKey] as! CFString == kCFProxyTypeHTTPS} + if(proxy != nil){ + let host = proxy?[kCFProxyHostNameKey] ?? nil + let port = proxy?[kCFProxyPortNumberKey] ?? nil + callback(host as? String,port as? Int) + }else{ + callback(nil,nil) + } + }else{ + callback(nil,nil) + } + } + + + func downloadPac(pacUrl:String, callback:@escaping (_ pacContent:String?,_ error: Error?)->Void) { + var pacContent:String = "" + let config = URLSessionConfiguration.default + config.connectionProxyDictionary = [AnyHashable: Any]() + let session = URLSession.init(configuration: config,delegate: nil,delegateQueue: OperationQueue.current) + session.dataTask(with: URL(string: pacUrl)!, completionHandler: { data, response, error in + if(error != nil){ + callback(nil,error) + } + pacContent = String(bytes: data!,encoding: String.Encoding.utf8)! + callback(pacContent,nil) + }).resume() + + } + } diff --git a/lib/flutter_system_proxy.dart b/lib/flutter_system_proxy.dart index 566de35..c61237c 100644 --- a/lib/flutter_system_proxy.dart +++ b/lib/flutter_system_proxy.dart @@ -1,81 +1,25 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:io'; import 'package:flutter/services.dart'; class FlutterSystemProxy { static const MethodChannel _channel = const MethodChannel('flutter_system_proxy'); - - static Future?> _getSystemProxy(String url) async { - bool isHttps = Uri.parse(url).scheme == 'https'; - dynamic proxySettings = await _channel.invokeMethod("getDeviceProxy"); - if (Platform.isAndroid) { - if (isHttps) { - if (!isNullOrEmpty(proxySettings['https.proxyHost']) && - isPort(proxySettings['https.proxyPort'])) { - return { - "enabled": "true", - "host": proxySettings['https.proxyHost'], - "port": proxySettings['https.proxyPort'] - }; - } - } else { - if (!isNullOrEmpty(proxySettings['http.proxyHost']) && - isPort(proxySettings['http.proxyPort'])) { - return { - "enabled": "true", - "host": proxySettings['http.proxyHost'], - "port": proxySettings['http.proxyPort'] - }; - } - } - return null; - } else if (Platform.isIOS) { - if (proxySettings["ProxyAutoConfigEnable"] == 1) { - return { - "pacEnabled": "true", - "pacUrl": proxySettings['ProxyAutoConfigURLString'] - }; - } else { - if (isHttps) { - if (proxySettings['HTTPSEnable'] == 1) { - return { - "enabled": "true", - "host": proxySettings['HTTPSProxy'].toString(), - "port": proxySettings['HTTPSPort'].toString() - }; - } - } else { - if (proxySettings['HTTPEnable'] == 1) { - return { - "enabled": "true", - "host": proxySettings['HTTPProxy'].toString(), - "port": proxySettings['HTTPPort'].toString() - }; - } - } - return null; - } - } + + /// returns host and port from environment + static Future getEnvironmentProxy(String url) async { + return _channel.invokeMethod("getDeviceProxy", {'url': url}); } + /// returns Proxy String static Future findProxyFromEnvironment(String url) async { - var parsedProxy = await _getSystemProxy(url); - var host = Uri.parse(url).host; - if (parsedProxy != null && parsedProxy["enabled"] == "true") { + dynamic proxySettings = await getEnvironmentProxy(url); + if (!isNullOrEmpty(proxySettings['host']) && + isPort(proxySettings['port'])) { return "PROXY " + - (parsedProxy["host"] as String) + + (proxySettings['host'].toString()) + ":" + - (parsedProxy["port"] as String); - } else if (parsedProxy != null && - parsedProxy["pacEnabled"] == "true" && - parsedProxy["pacUrl"] != null) { - String pacLocation = parsedProxy["pacUrl"] as String; - String jsContents = await contents(pacLocation); - String proxy = await _channel.invokeMethod( - "executePAC", {"url": url, "host": host, "js": jsContents}); - return proxy; + (proxySettings['port'].toString()); } else { return HttpClient.findProxyFromEnvironment(Uri.parse(url)); } @@ -86,24 +30,12 @@ bool isNullOrEmpty(String? str) { return str == null || str == ""; } -bool isPort(String? port) { +bool isPort(dynamic port) { if (port == null) return false; - final number = num.tryParse(port); - if (number != null && number > 0) { + final number = num.tryParse(port.toString()); + if (number != null && number > 0 && number <= 65536) { return true; } else { return false; } } - -Future contents(String url) async { - HttpClient client = new HttpClient(); - var completor = new Completer(); - client.findProxy = null; - var request = await client.getUrl(Uri.parse(url)); - var response = await request.close(); - response.transform(utf8.decoder).listen((contents) { - completor.complete(contents); - }); - return completor.future; -} diff --git a/pubspec.yaml b/pubspec.yaml index c2711bb..06303b8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_system_proxy description: A Flutter Plugin to detect System proxy. When using HTTP client that are not proxy aware this plugin can help with finding the proxy from system settings which then can be used with HTTP Client to make a successful request. -version: 0.0.2 -homepage: https://github.com/Rushabhshroff/flutter_system_proxy.git +version: 0.1.0 +homepage: https://github.com/BrowserStackCE/flutter_system_proxy.git environment: sdk: ">=2.12.0 <3.0.0"