Python HTTP 代理的 POST 正文可能被缓冲在 rfile 中
通过处理器的缓冲输入读取 Python HTTP 代理请求,而不是等待那些已经离开套接字缓冲区的字节。
正向代理读取请求行和头部,将它们转发出去,然后在 select 中等待 POST 正文。客户端其实早已发送。双方看起来都在等待对方。
在 Python 的 BaseHTTPRequestHandler 中,读取头部会经过 rfile,这是一个带缓冲的输入流。这个流可能预先读取,并持有属于正文的字节。等待底层套接字只询问其文件描述符是否就绪;它并不询问 Python 流中是否已经含有可用数据。
沿用读取头部的那个缓冲区
正文也要通过 self.rfile 读取。不要在用带缓冲的流解析完头部之后切换到 self.connection.recv。否则代码可能绕过该流已经持有的字节,去等待那些再也不会被发送的数据。
把 rfile 本身传给 select 并不会让 select 检查它的 Python 缓冲区。就绪 API 是通过对象的文件描述符工作的。
对于只有一个有效 Content-Length 且没有 Transfer-Encoding 的请求,正好消费声明数量的字节,并处理输入提前结束以及你自己的大小和时间限制。不要读到 EOF:使用持久连接的客户端可能在请求结束后仍保持连接打开。
把分帧做成代理的显式部分
分块请求正文需要分块分帧支持。不能靠寻找不存在的 Content-Length 来读取它们。长度元数据含糊时,需要一条明确的拒绝路径,而不是挑一个看起来方便的头部。
阻塞式处理器可以继续使用其流接口进行有界读取。事件驱动的代理需要一种缓冲区模型,把解析器的预读与就绪处理整合起来。更改超时不会让同一套接字的两个独立消费者调和。
这个诊断针对的是入站字节被持有的位置。代理消费了正确的正文之后,仍需要以一致的 HTTP 分帧转发该正文。标准 http.server 处理器是理解该机制的好去处,但它不是生产级的正向代理实现。