NiceLeeのBlog 用爱发电 bilibili~

Flutter 关于本地内置Host(三)

2020-02-18
nIceLee

阅读:


如何在程序内实现内置的host.mark
如何用dart实现HTTPS代理.mark

起源

第三个问题来了,有时发送HTTP请求的时候,因为DNS污染的原因,请求并不能成功。
而在手机上如果没有root权限,像PC端那样直接修改hosts不太可能。
这个时候怎么做才好呢?

过程

  • 看了一下dart:io,想要实现这个功能只需要在源码里面改那么一丁点东西,也就是在Socket连接建立的时候将host替换掉,可惜。
  • 目前也没啥好一点的现成的工具,如果是从TCP层面入手的话,如果没有像java netty那样的第三方工具,自己实现整个SSL层很麻烦啊。
  • 工作量少一点的方法是,考虑自己建立一个本地HTTPS代理,想要访问的HTTP请求设置一下代理即可。 思路如下:
    • 接收从Client段发来的CONNECT消息,从头部读取到host和port
    • 在自己维护的表内查询是否有host,并据此建立与服务器的连接
        host = map[host] == null? host : map[host];
        socketWithServer = socket.connect(host, port);
      
    • 与服务器的连接建立后,向客户端回个确认消息
        socketWithClient.add('HTTP/1.1 200 Connection Established\r\n\r\n');
      
    • 充当客户端与服务器之间的水管工,来啥转发啥。
    • 很难想象不到两百行代码就可以搞定,而且里面还有那么多try catch…

代码

功能实现

import 'dart:io';
import 'dart:convert';



class CustomHttpsProxy {
  final Map hosts;
  final int port;

  ServerSocket serverSocket;

  CustomHttpsProxy({this.hosts, this.port});

  Future init() async {
    await ServerSocket.bind(InternetAddress.anyIPv4, port).then((serverSocket) {
      this.serverSocket = serverSocket;
      serverSocket.listen((client) {
        try {
          ClientConnectionHandler(client, hosts).handle();
        } catch (e) {
          print('ClientConnectionHandler exception $e');
        }
      });
    }).catchError((e) {
      print('serverSocket 处理异常$e');
    });
    return serverSocket;
  }

  void close() {
    if (serverSocket != null) {
      serverSocket.close();
    }
  }
}

class ClientConnectionHandler {
  final RegExp regx = RegExp(r'CONNECT ([^ :]+)(?::([0-9]+))? HTTP/1.1\r\n');
  final Map hosts;

  Socket server;
  Socket client;
  String content = '';
  String host;
  int port;

  ClientConnectionHandler(this.client, this.hosts);

  void closeSockets() {
//    print('socket is going to destroy');
    if (server != null) {
      server.destroy();
    }
    client.destroy();
  }

  void dataHandler(data) {
    if (server == null) {
      content += utf8.decode(data);
      final m = regx.firstMatch(content);
      if (m != null) {
        host = m.group(1);
        port = m.group(2) == null ? 443 : int.parse(m.group(2));
        final realHost = hosts != null && hosts.containsKey(host) ? hosts[host] : host;
        try {
          ServerConnectionHandler(realHost, port, this)
              .handle()
              .catchError((e) {
            print('Server error $e');
            closeSockets();
          });
        } catch (e) {
          print('Server exception $e');
          closeSockets();
        }
      }
    } else {
      try {
        server.add(data);
      } catch (e) {
        print('sever has been shut down');
        closeSockets();
      }
    }
  }

  void errorHandler(error, StackTrace trace) {
    print('client socket error: $error');
  }

  void doneHandler() {
    closeSockets();
  }

  void handle() {
    client.listen(dataHandler,
        onError: errorHandler, onDone: doneHandler, cancelOnError: true);
  }
}

class ServerConnectionHandler {
  final String RESPONSE = 'HTTP/1.1 200 Connection Established\r\n\r\n';
  final String host;
  final int port;
  final ClientConnectionHandler handler;
  Socket server;
  Socket client;
  String content = '';

  ServerConnectionHandler(this.host, this.port, this.handler) {
    client = handler.client;
  }

  //接收报文
  void dataHandler(data) {
    try {
      client.add(data);
    } on Exception catch (e) {
      print('client has been shut down $e');
      handler.closeSockets();
    }
  }

  void errorHandler(error, StackTrace trace) {
    print('server socket error: $error');
  }

  void doneHandler() {
    handler.closeSockets();
  }

  Future handle() async {
//    print('尝试建立连接: $host:$port');
    server = await Socket.connect(host, port, timeout: Duration(seconds: 60));
    server.listen(dataHandler,
        onError: errorHandler, onDone: doneHandler, cancelOnError: true);
    handler.server = server;
    client.write(RESPONSE);
  }
}

一个样例

import 'dart:io';
import 'dart:convert';
import 'package:httpProxy/httpProxy.dart';

void main(List<String> arguments) {
  runPoxyAndDownloadFile();
}

void runPoxyAndDownloadFile() async {
  final hostMap = {
    'www.abc.com': '123.123.123.123',
  };
  var proxy = CustomHttpsProxy(hosts: hostMap, port: 4041);
  final result = await proxy.init();
  print('proxy established: ${result != null}');

  await doHttpGet();
  proxy.close();
}

Future doHttpGet() async {
  var httpClient = HttpClient();
  httpClient.findProxy = (uri) => 'PROXY localhost:4041';
  var request = await httpClient.getUrl(Uri.parse('https://www.abc.com/favicon.ico'));
  var response = await request.close();
//  var responseBody = await response.transform(Utf8Decoder()).join();
  var file = File('1.txt');
  var raf = file.openSync(mode: FileMode.write);
  response.listen((data) {
    raf.writeFromSync(data);
  }, onDone: () => raf.closeSync());
}

内容
隐藏