Flutter 100问:http怎样自动获取代理配置

1,405 阅读2分钟

Why

Flutter官网推荐使用http插件进行网络请求。抓包是开发、测试的有效排查网络异常的手段,但是,http插件的网络请求是不走系统代理的。也就是在不进行代码配置的情况下是无法使用抓包工具进行抓包的。

既然网络不走系统代理,那么能不能获取系统代理的host和port,在Flutter端自动设置?http_proxy插件就是为了解决这个问题。

How

引入插件

在项目的pubspec.yaml添加:

dependencies:
  http_proxy: ^1.1.0

代理配置

在main()函数里初始化代理配置

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  HttpProxy httpProxy = await HttpProxy.createHttpProxy();
  HttpOverrides.global=httpProxy;
  runApp(MyApp());
}

这就是Flutter端的所有代码了,然后设置系统代理的host和port就可以使用Charles等抓包工具抓包了。

More

http_proxy是如何工作的呢?下面将会对实现过程做详细介绍。

时机

第一个问题,网络请求是在什么时候进行代理配置?流程如图:

可以发现在HttpClient的findProxyFromEnvironment方法返回代理连接。

static String findProxyFromEnvironment(Uri url,
    {Map<String, String>? environment}) {
  HttpOverrides? overrides = HttpOverrides.current;
  if (overrides == null) {
    return _HttpClient._findProxyFromEnvironment(url, environment);
  }
  return overrides.findProxyFromEnvironment(url, environment);
}

在上面方法中HttpOverrides类负责获取代理配置。因此,只要给http设置一个全局HttpOverrides就可以获取代理配置了。

继承HttpOverrides,重写findProxyFromEnvironment(Uri url, Map<String, String> environment)方法,默认environment是null,这里给environment设置代理host和port。最后调用super方法返回代理连接。

@override
  String findProxyFromEnvironment(Uri url, Map<String, String> environment) {
    if (host == null) {
      return super.findProxyFromEnvironment(url, environment);
    }

    if (environment == null) {
      environment = {};
    }

    if (port != null) {
      environment['http_proxy'] = '$host:$port';
      environment['https_proxy'] = '$host:$port';
    } else {
      environment['http_proxy'] = '$host:8888';
      environment['https_proxy'] = '$host:8888';
    }

    return super.findProxyFromEnvironment(url, environment);
  }

$host$port是获取的系统代理host和port。后面再讲如何获取这两个值。

获取系统代理

Flutter端与原生交互是通过MethodChannel,所以通过MethodChannel获取系统代理配置。

MethodChannel _channel = MethodChannel('com.lm.http.proxy');

Future<String> _getProxyHost() async {
  return await _channel.invokeMethod('getProxyHost');
}

Future<String> _getProxyPort() async {
  return await _channel.invokeMethod('getProxyPort');
}

class HttpProxy extends HttpOverrides {
  String host;
  String port;

  HttpProxy._(this.host, this.port);

  static Future<HttpProxy> createHttpProxy() async {
    return HttpProxy._(await _getProxyHost(), await _getProxyPort());
  }
}

提供系统代理

相应的原生部分需要使用MethodChannel提供系统当前代理host和port

Android端

  1. 初始化MethodChannel
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "com.lm.http.proxy");
    channel.setMethodCallHandler(this);
}

@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
    switch (call.method) {
        case "getProxyHost":
            result.success(getProxyHost());
        break;
        case "getProxyPort":
            result.success(getProxyPort());
        break;
    }
}
  1. 获取系统代理
private static String getProxyHost() {
    return System.getProperty("http.proxyHost");
}

private static String getProxyPort() {
    return System.getProperty("http.proxyPort");
}

IOS端

  1. 初始化MethodChannel
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
  FlutterMethodChannel* channel = [FlutterMethodChannel
      methodChannelWithName:@"com.lm.http.proxy"
            binaryMessenger:[registrar messenger]];
  HttpProxyPlugin* instance = [[HttpProxyPlugin alloc] init];
  [registrar addMethodCallDelegate:instance channel:channel];
}
  1. 获取系统代理
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
  if ([@"getProxyHost" isEqualToString:call.method]) {
      CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
      NSDictionary *dictProxy = (__bridge_transfer id)proxySettings;
      //是否开启了http代理
      if ([[dictProxy objectForKey:@"HTTPEnable"] boolValue]) {
          NSString *proxyHost = [dictProxy objectForKey:@"HTTPProxy"];
          result(proxyHost);
      }else{
        result(nil);
      }
    } else if ([@"getProxyPort" isEqualToString:call.method]) {
      CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
      NSDictionary *dictProxy = (__bridge_transfer id)proxySettings;
      //是否开启了http代理
      if ([[dictProxy objectForKey:@"HTTPEnable"] boolValue]) {
          NSString *proxyPort = [NSString stringWithFormat: @"%ld", [[dictProxy objectForKey:@"HTTPPort"] integerValue]];
          result(proxyPort);
      }else{
        result(nil);
      }
    }else{
      result(FlutterMethodNotImplemented);
    }
}

Repository

github.com/wslaimin/ht…

Communication

如果对Flutter感兴趣,来Flutter泡泡群冒个泡吧!

QQ: 905105199