フォワードプロキシは TCP セグメントの境界を保持しない
フォワードプロキシの両側でトラフィックを調べるときは、TCP のセグメント化、ソケットの読み取り、HTTP のフレーミングを区別します。
HTTP レスポンスが Squid を通過したあと、キャプチャした複数の TCP セグメントに現れるとします。それは通常の TCP の挙動であり、レスポンスが誤って分割された証拠ではありません。
フォワードプロキシは通常、2 つの接続を持ちます:
client ⇄ TCP connection A ⇄ proxy ⇄ TCP connection B ⇄ origin
各接続は独自の TCP 状態、シーケンス番号、パケット化を持ちます。プロキシは一方の接続からデータを読み、もう一方へ書き込むことができ、元のセグメントサイズは保持しません。バッファリング、送信タイミング、輻輳、経路のサイズ制限によって別のまとまり方が生じます。
切り分けておくべき 3 つの境界
| 境界 | 何が決めるか |
|---|---|
| TCP セグメント | トランスポート層とネットワーク経路 |
| ソケットの読み取り結果 | 利用可能なストリームのバイト数と呼び出し側のバッファ |
| HTTP メッセージまたはフレーム | HTTP プロトコルのフレーミング規則 |
1 回の読み取りに、ヘッダーの一部、ヘッダー全体とボディのバイト列、あるいは複数のアプリケーション層の境界をまたぐデータが含まれることがあります。TCP は「HTTP レスポンスの終わり」を読み取り成功の末尾に結び付けるわけではありません。
そのため HTTP/1.1 のパーサーはヘッダーを蓄積し、該当するボディのフレーミング規則(長さ、転送コーディング、許容される接続終了など)を適用します。HTTP/2 のフレームはプロトコルの構造であり、それ自体が TCP セグメントをまたぐことがあります。ネットワークパケットの別名ではありません。
CONNECT トンネルが成功すると、トンネル内のバイトストリームは保たれますが、TCP の区間は依然として 2 つです。プロキシがトンネル内のバイトを変更していなくても、セグメントの境界は異なり得ます。
破損を調べるときは、再構成したアプリケーションのデータを比較してください。パケット数を比べたり、両側で同じパケット長を期待したりするのは、TCP が提供しない境界の保証を求めることになります。