本文讲 http 代理,顾名思义,http 代理代理的是 http 请求,其实这里面分两类
- 普通代理 这种代理扮演的是「中间人」角色,对于连接到它的客户端来说,它是服务端;对于要连接的服务端来说,它是客户端。它就负责在两端之间来回传送 HTTP 报文。隧道代理。它通过 HTTP 协议正文部分(Body)完成通讯,以 HTTP 的方式实现任意基于 TCP 的应用层协议代理。这种代理使用 HTTP 的 CONNECT 方法建立连接。
通俗一点讲,普通代理解析 http 包,然后将请求转发到目标地址,但是没法解析 https 的包,所以也就没法代理 https 的请求,但是隧道代理可以代理 https 的请求或者其他的一些协议请求。
0x01 普通代理
代码很简单
import socket from urllib.parse import urlparse from http.server import BaseHTTPRequestHandler, HTTPServer class ProxyHandler(BaseHTTPRequestHandler): def _recv_data_from_remote(self, sock): data = b'' while True: recv_data = sock.recv(4096) if not recv_data: break data += recv_data sock.close() return data def do_GET(self): # 解析 GET 请求信息 uri = urlparse(self.path) scheme, host, path = uri.scheme, uri.hostname, uri.path host_ip = socket.gethostbyname(host) port = 443 if scheme == "https" else 80 # 为了简单起见,Connection 都为 close, 也就不需要 Proxy-Connection 判断了 del self.headers['Proxy-Connection'] self.headers['Connection'] = 'close' # 构造新的 http 请求 send_data = "GET {path} {protocol_version}\r\n".format(path=path, protocol_version=self.protocol_version) headers = '' for key, value in self.headers.items(): headers += "{key}: {value}\r\n".format(key=key, value=value) headers += '\r\n' send_data += headers sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host_ip, port)) # 发送请求到目标地址 sock.sendall(send_data.encode()) data = self._recv_data_from_remote(sock) self.wfile.write(data) def main(): try: server = HTTPServer(('', 8888), ProxyHandler) server.serve_forever() except KeyboardInterrupt: server.socket.close() if __name__ == '__main__': main()
这里面就实现了 get 请求的转发,只用单线程的方式来处理,其他的有兴趣的同学可以自己扩展下。
看下效果
python3 http_server.py
配置完代理后可以发现 http 的请求都能正常转发,但是 https 的都没法识别。
下篇教程看如何通过隧道代理解决这个问题。