Search code examples
c++boostwebsocketbeast

Boost- WebSocket handshake was declined by the remote peer


I`m trying to connect to Kraken websocket API.But i got "The WebSocket handshake was declined by the remote peer" error.

I`ve wrote a wrapper class for websocket and rest api client of exchanges.It works well with Binance websocket APIs,but Kraken websocket connection was unsuccessful.

I have tried diffrent type of tls(ssl::context ctx_webSocket{ ssl::context::tlsv13_client };) verions too, but the result was same.

    class Exchange {
public:
    Exchange(std::string name, const std::string& http_host) :m_name(std::move(name)) {
        init_http(http_host);
    }


    void init_http(std::string const& host) {

        const auto results{ resolver.resolve(host,"443") };
        get_lowest_layer(stream).connect(results);
        if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str()))
        {
            boost::system::error_code ec{ static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category() };
            throw boost::system::system_error{ ec };
        }
        stream.handshake(ssl::stream_base::client);
    }

    void init_webSocket(std::string const& host, std::string const& port, std::string const& target) {
        auto const results = resolver_webSocket.resolve(host, port);
        net::connect(ws.next_layer().next_layer(), results.begin(), results.end());
        ws.next_layer().handshake(ssl::stream_base::client);
        // Set a decorator to change the User-Agent of the handshake
        ws.set_option(websocket::stream_base::decorator(
            [](websocket::request_type& req)
        {
            req.set(http::field::user_agent,
                std::string(BOOST_BEAST_VERSION_STRING) +
                " websocket-client-coro");
        }));
        ws.handshake(host, target.c_str());
    }

    void read_Socket() {
        ws.read(buffer);

    }
    void write_Socket(const std::string& text) {
        ws.write(net::buffer(text));
    }

    std::string get_socket_data() {
        return beast::buffers_to_string(buffer.data());
    }
    void buffer_clear() {
        buffer.clear();

    }
    void webSocket_close() {
        ws.close(websocket::close_code::none);
    }
private:
    // HTTP REQUEST SET //
    std::string m_name;
    net::io_context ioc;
    ssl::context ctx{ ssl::context::tlsv12_client };
    tcp::resolver resolver{ ioc };
    Stream stream{ ioc, ctx };

    // WEB SOCKET SET //
    std::string m_web_socket_host;
    std::string m_web_socket_port;
    beast::flat_buffer buffer;
    net::io_context ioc_webSocket;
    ssl::context ctx_webSocket{ ssl::context::tlsv13_client };
    tcp::resolver resolver_webSocket{ ioc_webSocket };
    websocket::stream<beast::ssl_stream<tcp::socket>> ws{ ioc_webSocket, ctx_webSocket };

};

int main()
{
    Exchange kraken{ "kraken","api.kraken.com" };

    try
    {

        kraken.init_webSocket("ws.kraken.com", "443", "/");

        while (true)
        {
            kraken.read_Socket();
            std::cout << kraken.get_socket_data();

            return 1;


            kraken.buffer_clear();
        }
        kraken.webSocket_close();
    }


    catch (std::exception const& e)
    {
        std::cerr << "Error: " << e.what() << std::endl;
    }

}

Solution

  • SOLVED. I have found out where the problem is. Before websocket handshake, have to set the SNI host name, here below is the working test code.

    #include <boost/asio.hpp>
    #include <boost/asio/ssl.hpp>
    #include <boost/beast.hpp>
    #include <boost/beast/ssl.hpp>
    #include <iostream>
    
    namespace net       = boost::asio;
    namespace ssl       = net::ssl;
    namespace beast     = boost::beast;
    namespace http      = beast::http;
    namespace websocket = beast::websocket;
    
    using tcp      = net::ip::tcp;
    using Request  = http::request<http::string_body>;
    using Stream   = beast::ssl_stream<beast::tcp_stream>;
    using Response = http::response<http::dynamic_body>;
    
    class Exchange {
      public:
        Exchange(std::string name, const std::string& http_host)
            : m_name(std::move(name))
        {
            init_http(http_host);
        }
    
        void init_http(std::string const& host)
        {
            const auto results{resolver.resolve(host, "443")};
            get_lowest_layer(stream).connect(results);
            // Set SNI Hostname (many hosts need this to handshake successfully)
            if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {
                boost::system::error_code ec{
                    static_cast<int>(::ERR_get_error()),
                    boost::asio::error::get_ssl_category()};
                throw boost::system::system_error{ec};
            }
            stream.handshake(ssl::stream_base::client);
        }
    
        void init_webSocket(std::string const& host, std::string const& port,
                            const char* p = "")
        {
            // Set SNI Hostname (many hosts need this to handshake successfully)
            if (!SSL_set_tlsext_host_name(ws.next_layer().native_handle(),
                                          host.c_str()))
                throw beast::system_error(
                    beast::error_code(static_cast<int>(::ERR_get_error()),
                                      net::error::get_ssl_category()),
                    "Failed to set SNI Hostname");
            auto const results = resolver_webSocket.resolve(host, port);
            net::connect(ws.next_layer().next_layer(), results.begin(),
                         results.end());
            ws.next_layer().handshake(ssl::stream_base::client);
    
            ws.handshake(host, p);
        }
    
        void read_Socket() { ws.read(buffer); }
    
        bool is_socket_open()
        {
            if (ws.is_open())
                return true;
            return false;
        }
    
        void write_Socket(const std::string& text) { ws.write(net::buffer(text)); }
    
        std::string get_socket_data()
        {
            return beast::buffers_to_string(buffer.data());
        }
        void buffer_clear() { buffer.clear(); }
    
        void webSocket_close() { ws.close(websocket::close_code::none); }
    
      private:
        // HTTP REQUEST SET //
        std::string     m_name;
        net::io_context ioc;
        ssl::context    ctx{ssl::context::tlsv12_client};
        tcp::resolver   resolver{ioc};
        Stream          stream{ioc, ctx};
    
        // WEB SOCKET SET //
        std::string        m_web_socket_host;
        std::string        m_web_socket_port;
        beast::flat_buffer buffer;
        net::io_context    ioc_webSocket;
        ssl::context       ctx_webSocket{ssl::context::tlsv12_client};
        tcp::resolver      resolver_webSocket{ioc_webSocket};
        websocket::stream<beast::ssl_stream<tcp::socket>> ws{ioc_webSocket,
                                                             ctx_webSocket};
    };
    
    int main()
    {
        Exchange kraken{"kraken", "ws.kraken.com"};
    
        try {
            kraken.init_webSocket("ws.kraken.com", "443", "/");
            if (kraken.is_socket_open())
                kraken.write_Socket(
                    R"({"event": "subscribe","pair": ["MINA/USD"],"subscription": {"name": "spread"}})");
            while (true) {
                kraken.read_Socket();
                std::cout << kraken.get_socket_data();
    
                kraken.buffer_clear();
            }
            kraken.webSocket_close();
        } catch (std::exception const& e) {
            std::cerr << "Error: " << e.what() << std::endl;
            return 1;
        }
    }