1

I am relatively new to C and C++ programming and I am trying to connect to a Websockets server using the boost and beast libraries in C++. I followed the tutorial here but I get the following error

terminate called after throwing an instance of 'boost::wrapexcept<boost::system::system_error>'
  what():  The WebSocket handshake was declined by the remote peer [boost.beast.websocket:20]

This is my code so far. I need help figuring out the problem and also if you guys can include resources where I can become better at C++ and networking in general I would really appreciate it.

#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <rapidjson/allocators.h>
#include <rapidjson/document.h>
#include <rapidjson/prettywriter.h>
#include <ta_libc.h>
#include <cstdlib>
#include <iostream>
#include <string>

using namespace std;
using namespace rapidjson;

namespace beast = boost::beast;
namespace http = beast::http;
namespace websocket = beast::websocket;
namespace net = boost::asio;
using tcp = boost::asio::ip::tcp;

int main() {

    string host = "ws-feed.exchange.coinbase.com";
    auto const port = "443";
    const char doc[] =
        "{ \'type\': \'subscribe\', \'product_ids\': [\'ETH-USD\'], \'channels\': [\'matches\'] }";

    Document document;
    document.Parse(doc);

    net::io_context ioc;
    tcp::resolver resolver{ioc};
    websocket::stream<tcp::socket> ws{ioc};

    auto const results = resolver.resolve(host, port);

    auto ep = net::connect(ws.next_layer(), results);

    host += ":" + to_string(ep.port());

    ws.set_option(websocket::stream_base::decorator(
        [](websocket::request_type& req)
            {
                req.set(http::field::user_agent,
                string(BOOST_BEAST_VERSION_STRING) +
                " websocket-client-coro");
            }));

    ws.handshake(host, "/");

    ws.write(net::buffer(string(doc)));

    beast::flat_buffer buffer;

    ws.read(buffer);

    ws.close(websocket::close_code::normal);

    cout << beast::make_printable(buffer.data()) << endl;

} // main
  • Do the Boost Beast library have built-in support for HTTPS and encrypted connections? – Some programmer dude May 25 '22 at 05:01
  • @Someprogrammerdude it does, but this code isn't using it – Alan Birtles May 25 '22 at 06:28
  • 1
    Please don't delete and repost your questions, you can edit your question Israel l instead – Alan Birtles May 25 '22 at 06:30
  • @AlanBirtles Port `443` is the HTTPS port. Which might be the problem, the OP is trying to connect to the HTTPS port but without the SSL/TLS parts that's needed for it. – Some programmer dude May 25 '22 at 06:30
  • Yes, and that _handshake was declined by the remote peer_ says it. If you would like an example of using SSL, I've done it [here](https://stackoverflow.com/questions/63330104/how-to-use-boostbeast-download-a-file-no-blocking-and-with-responses). – lakeweb May 26 '22 at 00:13

1 Answers1

1

Like others said, you don't use SSL where the server requires it.

Here's a demo. The subtler point is that the server required SNI.

#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/json.hpp>
#include <boost/json/src.hpp> // for header-only lib
#include <iostream>

namespace beast     = boost::beast;
namespace http      = beast::http;
namespace websocket = beast::websocket;
namespace net       = boost::asio;
namespace ssl       = net::ssl;
namespace json      = boost::json;

using tcp = boost::asio::ip::tcp;

int main() {
    std::string host = "ws-feed.exchange.coinbase.com";
    auto const  port = "443";

    json::value doc = {
        {"type", "subscribe"},
        {"product_ids", {"ETH-USD"}},
        {"channels", {"matches"}},
    };

    net::io_context ioc;
    tcp::resolver   resolver{ioc};
    ssl::context    ctx(ssl::context::sslv23_client);
    ctx.set_default_verify_paths();
    websocket::stream<ssl::stream<tcp::socket>> ws{ioc, ctx};

    auto const results = resolver.resolve(host, port);

    // connect raw socket
    auto& raw = beast::get_lowest_layer(ws);
    auto ep = net::connect(raw, results);

    // ssl handshake
    if (!SSL_set_tlsext_host_name(ws.next_layer().native_handle(), host.c_str())) {
        throw boost::system::system_error(
            ::ERR_get_error(), boost::asio::error::get_ssl_category());
    }
    ws.next_layer().handshake(ssl::stream_base::client);

    host += ":" + std::to_string(ep.port());

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

    // websocket handshake
    ws.handshake(host, "/");

    std::cout << doc << std::endl;
    ws.write(net::buffer(serialize(doc)));

    beast::flat_buffer buffer;

    ws.read(buffer);

    std::cout << beast::make_printable(buffer.data()) << std::endl;

    ws.close(websocket::close_code::normal);
}

Results in:

{"type":"subscribe","product_ids":["ETH-USD"],"channels":["matches"]}
{"type":"subscriptions","channels":[{"name":"matches","product_ids":["ETH-USD"]}]}
terminate called after throwing an instance of 'boost::wrapexcept<boost::system::system_error>'
  what():  stream truncated [asio.ssl.stream:1]

The error indicates that the server doesn't gracefully shutdown the connection. That's probably by design.

sehe
  • 350,152
  • 45
  • 431
  • 590