本文主要实现隧道代理,让 https 请求也能代理。
隧道代理的原理是:
HTTP 客户端通过 CONNECT 方法请求隧道代理创建一条到达任意目的服务器和端口的 TCP 连接,并对客户端和服务器之间的后继数据进行盲转发。
步骤如下
1. 客户端发送一个 http CONNECT 请求
CONNECT baidu.com:443 HTTP/1.12. 代理收到这样的请求后,拿到目标服务器域名及端口,与目标服务端建立 TCP 连接,并响应给浏览器这样一个 HTTP 报文:
HTTP/1.1 200 Connection Established3. 建立完隧道以后,客户端与目标服务器照之前的方式发送请求,代理节点只是做转发功能,无从知道转发的流量具体是什么
看代码
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 代理是一样的。