After Beast reads a proxy’s CONNECT response header, inspect both the response status and the dynamic buffer. The read operation can fetch bytes beyond the header terminator and retain them in the supplied buffer. Starting TLS with an overload that only reads the socket leaves those retained bytes outside the TLS input path.

For a WebSocket stream whose next layer is the SSL stream, use the buffered handshake overload. In an existing yield_context session, with buffer kept alive throughout the operations:

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);

The early returns represent the session’s failure exits; attach its normal error reporting and cleanup there. Only a successful CONNECT response establishes the tunnel. A rejection page must not become TLS input.

The buffered overload completes with an error code and a byte count. That count describes how many bytes it consumed from the supplied buffers. Remove exactly that prefix with consume(used), after completion. Do not clear the buffer before starting the operation, mutate its storage while the handshake is pending, or assume the operation consumed every supplied byte.

With ordinary client-initiated TLS immediately after CONNECT, the buffer may be empty. The overload also handles that case. Its purpose is preserving stream boundaries; it does not make arbitrary trailing proxy data valid TLS. Keep the application’s destination hostname verification and SNI configuration on the SSL layer before handshaking, and start the WebSocket upgrade only after TLS succeeds.

Tënë sô zo