Python HTTP プロキシの POST ボディは rfile にバッファされ得る
ソケットバッファをすでに離れたバイトを待つのではなく、ハンドラのバッファ付き入力から Python HTTP プロキシのリクエストを読み取ります。
フォワードプロキシはリクエスト行とヘッダーを読み、それらを転送し、その後 select で POST ボディを待つ。クライアントはすでに送信済みだ。双方が相手を待っているように見える。
Python の BaseHTTPRequestHandler では、ヘッダーの読み取りは rfile というバッファ付き入力ストリームを通る。このストリームは先読みして、ボディに属するバイトを保持することがある。基になるソケットを待つのは、そのファイルディスクリプタが準備できたかだけを問い、Python のストリームがすでに使えるデータを含んでいるかは問わない。
ヘッダーを読んだバッファに従う
ボディも self.rfile から読む。バッファ付きストリームでヘッダーを解析した後に self.connection.recv へ切り替えてはいけない。そうしないと、そのストリームが保持しているバイトを飛ばし、二度と送られないデータを待つことになる。
rfile 自体を select に渡しても、select がその Python バッファを検査するわけではない。準備完了 API はオブジェクトのファイルディスクリプタを通じて動作する。
有効な Content-Length が 1 つあり Transfer-Encoding がないリクエストでは、宣言されたバイト数を正確に消費し、入力の早期終了とサイズ・時間の制限を扱う。EOF まで読んではならない。持続的接続を使うクライアントはリクエスト完了後も接続を開いたままにすることがある。
フレーミングをプロキシの明示的な一部にする
チャンク形式のリクエストボディにはチャンク形式のフレーミング対応が必要だ。存在しない Content-Length を探して読むことはできない。曖昧な長さのメタデータには、都合のよいヘッダーを選ぶのではなく、定義された拒否経路が必要だ。
ブロッキングハンドラは上限付きの読み取りでストリームインターフェースを使い続けられる。イベント駆動型のプロキシには、パーサーの先読みと準備完了処理を統合するバッファモデルが必要だ。タイムアウトを変えても、同じソケットの 2 つの独立した消費者は調和しない。
この診断は、受信バイトがどこに保持されるかに固有のものだ。プロキシが正しいボディを消費した後も、そのボディを一貫した HTTP フレーミングで転送する必要がある。標準の http.server ハンドラは仕組みを理解するのに有用な場所だが、本番のフォワードプロキシ実装ではない。