由浅入深写代理(6)-http-代理

997 阅读2分钟
原文链接: zhuanlan.zhihu.com

本文讲 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 的都没法识别。

下篇教程看如何通过隧道代理解决这个问题。

参考资料

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

* 使用 Python 实现一个简单的 HTTP 代理 - GET