preface

A few days ago, some students reported that HTTP requests in Flutter could not capture packets by Fiddler. The author liked to use Charles packet capture tool, so he took some time to write a small demo to test it. The conclusion was that Charles could not capture request packets by setting the proxy on his mobile phone. Then the problem was analyzed:

  1. It is confirmed that the GET request initiated by HTTP is used. Theoretically, THE HTTP protocol should be able to be caught by Charles. If the packet is not caught, it may be that there is no proxy transfer.

  2. The reason why HTTP requests are not propped through wifi is because some HTTP frameworks used by Android are propped normally. Is it possible that there is an API method in the code to set the request not to propped? Therefore, I read the HTTP source code of Flutter and finally found the answer.

HTTP request source tracing

Dart is an abstract HttpClient class that implements member methods in http_impl. Dart.

Future<HttpClientRequest> getUrl(Uri url) => _openUrl("get", url);

Future<_HttpClientRequest> _openUrl(String method, Uri uri) {
  .
  .
  .
  // Check to see if a proxy server should be used for this connection.
  var proxyConf = const _ProxyConfiguration.direct();
  if(_findProxy ! = null) { // TODO(sgjesse): Keep a map of these as normally only a few // configuration strings will be used. try { proxyConf = new _ProxyConfiguration(_findProxy(uri)); } catch (error, stackTrace) {returnnew Future.error(error, stackTrace); }}return _getConnection(uri.host, port, proxyConf, isSecure)
      .then((_ConnectionInfo info) {
    .
    .
    .
  });
}
Copy the code

Check to see if a proxy server should be used for this connection. Check if a proxy server should be used for this connection.

Next, there is a proxyConf object initialization and a statement to create a new proxyConf object based on _findProxy, then a connection is created via _getConnection(uri.host, port, proxyConf, isSecure), The _getConnection source is as follows:

Future<_ConnectionInfo> _getConnection(String uriHost, int uriPort,
    _ProxyConfiguration proxyConf, bool isSecure) {
  Iterator<_Proxy> proxies = proxyConf.proxies.iterator;

  Future<_ConnectionInfo> connect(error) {
    if(! proxies.moveNext())return new Future.error(error);
    _Proxy proxy = proxies.current;
    String host = proxy.isDirect ? uriHost : proxy.host;
    int port = proxy.isDirect ? uriPort : proxy.port;
    return _getConnectionTarget(host, port, isSecure)
        .connect(uriHost, uriPort, proxy, this)
        // On error, continue with next proxy.
        .catchError(connect);
  }

  return connect(new HttpException("No proxies given"));
}
Copy the code

From the code we can see that the requested host and port are reset based on the agent configuration information, and then the actual connection is created.

The _findProxy variable is related to the configured proxy information.

The default value for _findProxy is defined in the _HttpClient class in the http__impl. Dart file

Function _findProxy = HttpClient.findProxyFromEnvironment;
Copy the code

HttpClient class findProxyFromEnvironment method implementation

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);
}
Copy the code

Implementation of _findProxyFromEnvironment method in _HttpClient class

static String _findProxyFromEnvironment(
    Uri url, Map<String, String> environment) {
  checkNoProxy(String option) {
    if (option == null) return null;
    Iterator<String> names = option.split(",").map((s) => s.trim()).iterator;
    while (names.moveNext()) {
      var name = names.current;
      if ((name.startsWith("[") &&
              name.endsWith("]") &&
              "[${url.host}]. "" == name) ||
          (name.isNotEmpty && url.host.endsWith(name))) {
        return "DIRECT"; }}return null;
  }

  checkProxy(String option) {
    if (option == null) return null;
    option = option.trim();
    if (option.isEmpty) return null;
    int pos = option.indexOf(": / /");
    if (pos >= 0) {
      option = option.substring(pos + 3);
    }
    pos = option.indexOf("/");
    if (pos >= 0) {
      option = option.substring(0, pos);
    }
    // Add default port if no port configured.
    if (option.indexOf("[") == 0) {
      var pos = option.lastIndexOf(":");
      if (option.indexOf("]") > pos) option = "$option: 1080";
    } else {
      if (option.indexOf(":") == -1) option = "$option: 1080";
    }
    return "PROXY $option";
  }

  // Default to using the process current environment.
  if (environment == null) environment = _platformEnvironmentCache;

  String proxyCfg;

  String noProxy = environment["no_proxy"];
  if (noProxy == null) noProxy = environment["NO_PROXY"];
  if ((proxyCfg= checkNoProxy(noProxy)) ! = null) {return proxyCfg;
  }

  if (url.scheme == "http") {
    String proxy = environment["http_proxy"];
    if (proxy == null) proxy = environment["HTTP_PROXY"];
    if ((proxyCfg= checkProxy(proxy)) ! = null) {return proxyCfg; }}else if (url.scheme == "https") {
    String proxy = environment["https_proxy"];
    if (proxy == null) proxy = environment["HTTPS_PROXY"];
    if ((proxyCfg= checkProxy(proxy)) ! = null) {return proxyCfg; }}return "DIRECT";
}
Copy the code

From the above code, you can see that the proxy configuration is read from the environment. When setting the proxy, you must specify http_proxy or https_proxy, etc. ProxyConf = new _ProxyConfiguration(_findProxy(URI)); By default, the environment is empty, so if you want to use a proxy in a Flutter HTTP request, you need to specify the corresponding proxy configuration, that is, set httpClient.findProxy.

Sample code:

_getHttpData() async {
  var httpClient = new HttpClient();
  httpClient.findProxy = (url) {
    return HttpClient.findProxyFromEnvironment(url, environment: {"http_proxy": 'http://192.168.124.7:8888'}); }; var uri = new Uri.http('t.weather.sojson.com'.'/api/weather/city/101210101');
  var request = await httpClient.getUrl(uri);
  var response = await request.close();
  if (response.statusCode == 200) {
    print('Request successful');
    var responseBody = await response.transform(Utf8Decoder()).join();
    print('responseBody = $responseBody');
  } else {
    print('Request failed'); }}Copy the code

After setting the above code, you can use Fiddler or Charles to capture packets.

Note:

  • The code has set the agent, mobile phone wifi no longer need to set the agent;

  • 192.168.124.7 This IP address is the IP address of Charles’s computer where we need to capture packets.

The second packet capture solution

If apps written using Flutter do not manually set up proxies, an alternative scheme can be used to capture packets.

To set a hotspot on a PC, use a Mobile phone to connect to the Internet, and use Wireshark to capture data packets.

The detailed steps are as follows (in macOS) :

1. Open system preferences and find “Share”

2. Open Share. The following window is displayed

3. Click the Wi-Fi option button in the lower right corner, the following information is displayed, fill in the corresponding information and click ok to save

4. Go back to the “Sharing” window and open the service “Internet Sharing” in the left window.

5. On the Wireshark page, double-click the network that corresponds to the open hotspot

6. If the corresponding IP address of the interface domain name t.weather.sojson.com is 58.222.18.24, enter ip.dst == 58.222.18.24 in the preceding text box and initiate a network request through the mobile APP

View the IP address of the interface

$ping t.weather.sojson.com ping nm.ctn.aicdn.com (58.222.18.24): 56 data bytes 64 bytes from 58.222.18.24: Icmp_seq =0 TTL =54 time=16.792 ms 64 bytes from 58.222.18.24: Icmp_seq =1 TTL =54 time=16.926 ms 64 bytes from 58.222.18.24: ICmp_seq =2 TTL =54 time=15.804 msCopy the code

7. Select the corresponding HTTP request, arrow specified line, right-click, and choose Follow->HTTP Stream option

8. The network request information window is displayed as follows

Write in the last

This article shares two solutions to capture HTTP packets in Flutter. You can use them based on your actual situation. If you have any other Flutter related questions, please leave a message via our public account.

Description:

This article is reprinted from the corresponding “Flutter Programming Guide” wechat official account. For more Flutter related articles, open our wechat and scan our QR code to follow our wechat official account.