Beast がプロキシの CONNECT 応答ヘッダーを読んだら、応答ステータスと動的バッファの両方を確認します。読み取り操作はヘッダーの終端を超えたバイトを取得し、渡されたバッファに保持することがあります。ソケットだけを読むオーバーロードで TLS を開始すると、保持されたバイトは TLS の入力経路の外に残ります。

次の層が SSL ストリームである WebSocket ストリームでは、バッファ付きハンドシェイクのオーバーロードを使います。既存の yield_context セッションで、buffer を一連の操作の間ずっと生かしたままにします:

http::response_parser<http::empty_body> response;
http::async_read_header(
    beast::get_lowest_layer(ws), buffer, response, yield[ec]);
if (ec) return;

auto status = response.get().result_int();
if (status < 200 || status >= 300) return;

std::size_t used = ws.next_layer().async_handshake(
    ssl::stream_base::client, buffer.data(), yield[ec]);
if (ec) return;
buffer.consume(used);

早期リターンはセッションの失敗出口です。通常のエラー報告と後始末をそこで行います。トンネルを確立するのは成功した CONNECT 応答だけです。拒否ページを TLS の入力にしてはいけません。

バッファ付きオーバーロードはエラーコードとバイト数で完了します。その数は、渡したバッファから何バイト消費したかを表します。完了後に consume(used) でその接頭辞だけを正確に取り除きます。操作の開始前にバッファを消したり、ハンドシェイク待機中にそのストレージを変更したり、渡したバイトをすべて消費したと仮定したりしないでください。

CONNECT 直後にクライアントが開始する通常の TLS では、バッファが空のことがあります。このオーバーロードはその場合も扱います。目的はストリーム境界の保持であり、任意の後続プロキシデータを有効な TLS にするものではありません。ハンドシェイクの前に、宛先ホスト名の検証と SNI 設定を SSL 層に置いたままにし、TLS が成功してから初めて WebSocket アップグレードを開始してください。

関連記事