NiceLeeのBlog 用爱发电 bilibili~

Java Http/Https代理Demo (上)

2019-02-13
nIceLee

阅读:


上一篇对整个http协议有个大致的了解,是时候进一步尝试做一下代理了。

写在前面

  • HTTP 代理原理及实现
  • 目标是通过中间人代理的方式代理普通Http请求, 使用隧道代理方式代理Https请求。

思考

  • 本文先考虑中间人代理方式
  • 前面Http文件服务器Demo - ProxyHttpServer是来了请求直接读取文件内容回复;现在代理的话其实只是换个方式,也就是来了请求,根据请求访问网页,再把得到的内容回复过去。
    这样的话,变动的地方在于要再开一个线程:
    • 本来的线程用来接收客户端的请求、创建到服务器的连接、构造到服务器的数据;
    • 新建的线程则专门用来接收服务器的数据,转发给客户端。

实现

其实明白了原理,似乎也没啥好bb的。。
创建到服务器的TCP连接,打开面向服务器的监听线程,专用于转发数据给客户端

private void connecToServer(HttpRequest httpRequest) throws IOException {
    //默认ip:port参数
    String dstIp = httpRequest.host;
    int dstPort = 80;
    // connect方法, 从首行url获取目的参数
    if (httpRequest.method.toLowerCase().equals("connect")) {
        dstPort = 443;
        dstIp = httpRequest.url;
    }
    // Get/Post方法,从Host获取目的参数
    Matcher matcher = HttpResource.patternHost.matcher(dstIp);
    if (matcher.find()) {
        dstIp = matcher.group(1);
        dstPort = Integer.parseInt(matcher.group(2));
    }

    if (socketServer == null) {
        socketServer = new Socket();
        socketServer.connect(new InetSocketAddress(dstIp, dstPort));
        // 获取服务器之间的输入输出流
        inFromSever = new StreamReader(monitor, socketServer,
                new BufferedInputStream(socketServer.getInputStream()));
        outToServer = new BufferedOutputStream(socketServer.getOutputStream());

        // 打开面向服务器的监听线程,专用于转发数据给客户端
        ProxyDealer proxyDealer = new ProxyDealer(this);
        SocketServer.httpProxyThreadPool.execute(proxyDealer);
    }
}

处理客户端的消息,构造Http请求发送给服务器

private void doProxyNormal(HttpRequest httpRequest) throws IOException {
    // TODO do something with the httpRequest. Put 'X-forward...', for example.
    httpRequest.headers.put("X-Forwarded-For", "123.123.123.123");

    connecToServer(httpRequest);
    // 向服务器发送Http请求
    outToServer.write(String.format("%s %s %s\r\n", httpRequest.method, httpRequest.url, httpRequest.version).getBytes());
    outToServer.write(String.format("Host: %s\r\n", httpRequest.host).getBytes());
    for (Entry<String, String> entry : httpRequest.headers.entrySet()) {
        if (!entry.getKey().toLowerCase().contains("proxy") && !entry.getKey().toLowerCase().contains("forward")
                && !entry.getKey().toLowerCase().contains("authorization")) {
            outToServer.write((entry.getKey() + ": " + entry.getValue()).getBytes());
            outToServer.write(HttpResource.BREAK_LINE);
        }
    }
    if (httpRequest.dataLength > 0) {
        outToServer.write(String.format("Content-Length: %d\r\n", httpRequest.dataLength).getBytes());
    }
    outToServer.write(HttpResource.BREAK_LINE);
    if (httpRequest.dataLength > 0) {
        int count = 0;
        int rSize = in.read(in.readBuffer);
        System.out.println(new String(in.readBuffer, 0, rSize));
        while (rSize < httpRequest.dataLength - count) {
            outToServer.write(in.readBuffer, 0, rSize);
            rSize = in.read(in.readBuffer);
            count += rSize;
        }
        outToServer.write(in.readBuffer, 0, httpRequest.dataLength - count);
    }
    outToServer.flush();
}

源代码


内容
隐藏