由浅入深写代理(7)-https-代理

本文主要实现隧道代理,让 https 请求也能代理。

隧道代理的原理是:

HTTP 客户端通过 CONNECT 方法请求隧道代理创建一条到达任意目的服务器和端口的 TCP 连接,并对客户端和服务器之间的后继数据进行盲转发。

《由浅入深写代理(7)-https-代理》

步骤如下

1. 客户端发送一个 http CONNECT 请求

CONNECT baidu.com:443 HTTP/1.1

2. 代理收到这样的请求后,拿到目标服务器域名及端口,与目标服务端建立 TCP 连接,并响应给浏览器这样一个 HTTP 报文:

HTTP/1.1 200 Connection Established

3. 建立完隧道以后,客户端与目标服务器照之前的方式发送请求,代理节点只是做转发功能,无从知道转发的流量具体是什么

看代码

import socket
import select
from http.server import BaseHTTPRequestHandler, HTTPServer

class ProxyHandler(BaseHTTPRequestHandler):

    def send_data(self, sock, data):
        print(data)
        bytes_sent = 0
        while True:
            r = sock.send(data[bytes_sent:])
            if r < 0:
                return r
            bytes_sent += r
            if bytes_sent == len(data):
                return bytes_sent

    def handle_tcp(self, sock, remote):
        # 处理 client socket 和 remote socket 的数据流
        try:
            fdset = [sock, remote]
            while True:
                # 用 IO 多路复用 select 监听套接字是否有数据流
                r, w, e = select.select(fdset, [], [])
                if sock in r:
                    data = sock.recv(4096)
                    if len(data) <= 0:
                        break
                    result = self.send_data(remote, data)
                    if result < len(data):
                        raise Exception('failed to send all data')

                if remote in r:
                    data = remote.recv(4096)
                    if len(data) <= 0:
                        break
                    result = self.send_data(sock, data)
                    if result < len(data):
                        raise Exception('failed to send all data')
        except Exception as e:
            raise(e)
        finally:
            sock.close()
            remote.close()

    def do_CONNECT(self):

        # 解析出 host 和 port
        uri = self.path.split(":")
        host, port = uri[0], int(uri[1])
        host_ip = socket.gethostbyname(host)

        remote_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        remote_sock.connect((host_ip, port))
        # 告诉客户端 CONNECT 成功
        self.wfile.write("{protocol_version} 200 Connection Established\r\n\r\n".format(protocol_version=self.protocol_version).encode())

        # 转发请求
        self.handle_tcp(self.connection, remote_sock)

def main():
    try:
        server = HTTPServer(('', 8888), ProxyHandler)
        server.serve_forever()
    except KeyboardInterrupt:
        server.socket.close()

if __name__ == '__main__':
    main()

有一个 do_CONNECT 函数的处理,实现之前隧道的建立,然后 handle_tcp,代码和之前 socks5 代理是一样的。

github 地址: https_server.py

参考链接:

* HTTP 代理原理及实现(一) | JerryQu 的小站

    原文作者:小蜜蜂
    原文地址: https://juejin.im/entry/599ed77b518825243445a40f
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞