Node の HTTP サーバーは通常のプロキシリクエストをリクエストコールバックで処理します。CONNECT は別の経路をたどります。サーバーはリクエスト、クライアントソケット、head バッファーとともに connect を発行します。

そのイベントは HTTP パースとトンネルの間の引き渡しです。トンネル化されたトラフィックに使い続けられる通常の ServerResponse オブジェクトはありません。

引数何を運ぶか
reqCONNECT リクエスト(宛先のオーソリティとヘッダーを含む)
clientSocketクライアントへの戻り接続
headNode が HTTP ヘッダーより先にすでに読み取ったトンネルデータ(空の場合もある)

バイトを中継する前に HTTP の判断を終える

要求されたホストとポートを検証し、プロキシのアクセスと認証のポリシーを適用し、上流接続を確立します。CONNECT は service.example:443 のようなオーソリティを識別します。コロンごとに盲目的に分割するのではなく、IPv6 のオーソリティを考慮してその形式を解析してください。

トンネルを受け入れたら、成功した HTTP レスポンスとその終端の空行を送信します。その後、クライアントソケットからの後続のバイトを続ける前に、head バイトを上流へ転送します。新しいデータリスナーを接続しても、すでにハンドラーへ別個に渡されたバッファーは取り戻せません。

Node の公式トンネル例は、2 つのストリーム方向を接続する前に serverSocket.write(head) でその引き渡しを行います。その小さな例は遷移を示しています。実運用のプロキシには、接続試行の上限、アクセス制御、ライフサイクル管理も必要です。

成功後、バイトはサービスのプロトコルに属する

成功した CONNECT レスポンスは接続をバイトストリームに変えます。対象が HTTPS サイトなら、その TLS 交換はそのストリームの中で行われます。単にトンネル化するだけでは、プロキシが代替のウェブサイト証明書を発行する必要はありません。

CONNECT を受け入れる前なら、HTTP エラーで拒否理由を説明できます。受け入れた後にストリームへ HTTP 500 を書き込むと、トンネル化されたプロトコルにバイトを注入してしまいます。代わりに、後の失敗では適切な接続を終了または破棄してください。

両方向を バックプレッシャーを考慮したストリーム処理 で接続し、end、error、close の各イベントが相手側をどう終了させるかを決めます。最初の head バッファーは同じ所有モデルの一部として保持してください。それはアプリケーションデータであり、捨てるべき余分な HTTP ヘッダーではありません。

関連記事