Search code examples
c++websocketboostboost-beast

Boost.Beast WebSocket with SSL: Multiple async_handshake calls needed for proper initialization?


I'm encountering an issue while trying to establish a WebSocket connection with SSL using Boost.Beast and Boost.Asio. The WebSocket connection seems to be working only when I call both async_handshake on the beast::websocket::stream and on the SSL next_layer().

// Relevant part of the code
class WebSocketClient : public std::enable_shared_from_this<WebSocketClient> {
public:
    // ... (other members)

    void onConnect(const boost::system::error_code& ec, const tcp::endpoint&) {
        if (!ec) {
            // WebSocket initialization
            ws_ = beast::websocket::stream<ssl::stream<beast::tcp_stream>>(io_context_, ssl_context_);

            // This version works for me, but is it correct?
            ws_.next_layer().async_handshake(ssl::stream_base::client, std::bind(&WebSocketClient::onSslHandshake, shared_from_this(), std::placeholders::_1));
            
            // Perform WebSocket handshake asynchronously
            ws_.async_handshake(host_, target_, "/", std::bind(&WebSocketClient::onHandshake, shared_from_this(), std::placeholders::_1));
        } else {
            std::cerr << "Connection error: " << ec.message() << std::endl;
        }
    }
    // ... (other methods)
private:
    asio::io_context& io_context_;
    ssl::context& ssl_context_;
    beast::websocket::stream<ssl::stream<beast::tcp_stream>> ws_;
    std::string host_;
    std::string target_;

    // ... (other members)
};

Is it normal to call async_handshake on both the beast::websocket::stream and the SSL next_layer() in this scenario? It seems to work, but I want to ensure that I'm following the correct and standard approach for establishing a WebSocket connection with SSL using Boost.Beast.

If I only use handshake on the ws I get: uninitialized (SSL routines)

Any insights or suggestions would be greatly appreciated!


Solution

  • You do not have an issue.

    You specifically state that you use both Websocket and SSL.

    Both protocols require a handshake. Because SSL is the lower layer, it comes first. It's even explicit in your code that it's the lower layer: ws_.next_layer() is not the Websocket layer, it's the SSL layer.

    They're different protocols and different handshakes, and both are required because you want to use them.


    Note that you did not complain about connecting, which is yet another layer down:

     ws_.next_layer().next_layer().async_connect(...)
    

    In fact it's the same situation, the only difference being the name of the function. However, on the TCP level of things, it very much involves another handshake (https://en.wikipedia.org/wiki/Handshake_(computing), https://www.geeksforgeeks.org/tcp-3-way-handshake-process/)


    Caution

    Your code is broken because it does both handshakes "in parallel". It might happen to do work today, but the only reliable way is to do initiate the Websocket handshake AFTER the SSL handshake completes, e.g. in onSslHandshake