Hi,
We are using boost beast in a high performance manner, i.e. we need to be able to communicate >1000 transactions per second per connection. For our application it is crucial to use a timeout on the stream which is provided by your library.
We encounter a critical exception when a client has a timeout policy enabled (which sends ping/pong messages by the beast library) and when we stress the async_write function (see example client below) by sending messages all the time. The bug results in an unhandled exception:
terminate called after throwing an instance of 'boost::wrapexcept<boost::asio::bad_executor>'
what(): bad executor
Aborted (core dumped)
Stack trace:
__GI_raise(int sig) (/build/glibc-KRRWSm/glibc-2.29/sysdeps/unix/sysv/linux/raise.c:50)
__GI_abort() (/build/glibc-KRRWSm/glibc-2.29/stdlib/abort.c:79)
[Unknown/Just-In-Time compiled code] (Unknown Source:0)
std::terminate() (Unknown Source:0)
__cxa_throw (Unknown Source:0)
boost::throw_exception<boost::asio::bad_executor>(const boost::asio::bad_executor & e) (/usr/local/include/boost/throw_exception.hpp:70)
boost::asio::executor::get_impl(const boost::asio::executor * const this) (/usr/local/include/boost/asio/executor.hpp:309)
boost::asio::executor::post<boost::asio::detail::work_dispatcher<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> >, true>::idle_ping_op<boost::asio::executor> >, std::allocator<void> >(const boost::asio::executor * const this, boost::asio::detail::work_dispatcher<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> > >::idle_ping_op<boost::asio::executor> > && f, const std::allocator<void> & a) (/usr/local/include/boost/asio/impl/executor.hpp:358)
boost::asio::detail::initiate_post::operator()<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> >, true>::idle_ping_op<boost::asio::executor>, boost::asio::executor const&>(const boost::asio::detail::initiate_post * const this, boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> >, true>::idle_ping_op<boost::asio::executor> && handler, const boost::asio::executor & ex) (/usr/local/include/boost/asio/impl/post.hpp:54)
boost::asio::async_result<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> >, true>::idle_ping_op<boost::asio::executor>, void ()>::initiate<boost::asio::detail::initiate_post, boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> >, true>::idle_ping_op<boost::asio::executor>, boost::asio::executor const&>(boost::asio::detail::initiate_post&&, boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> >, true>::idle_ping_op<boost::asio::executor>&&, boost::asio::executor const&)(boost::asio::detail::initiate_post && initiation, boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> >, true>::idle_ping_op<boost::asio::executor> && token, args#0) (/usr/local/include/boost/asio/async_result.hpp:82)
boost::asio::async_initiate<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> >, true>::idle_ping_op<boost::asio::executor>, void (), boost::asio::detail::initiate_post, boost::asio::executor const&>(boost::asio::detail::initiate_post&&, boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> >, true>::idle_ping_op<boost::asio::executor>&, boost::asio::executor const&)(boost::asio::detail::initiate_post && initiation, boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> >, true>::idle_ping_op<boost::asio::executor> & token, args#0) (/usr/local/include/boost/asio/async_result.hpp:257)
boost::asio::post<boost::asio::executor, boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> >, true>::idle_ping_op<boost::asio::executor> >(const boost::asio::executor & ex, boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> >, true>::idle_ping_op<boost::asio::executor> && token) (/usr/local/include/boost/asio/impl/post.hpp:74)
boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> >, true>::idle_ping_op<boost::asio::executor>::operator()(boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::executor, boost::beast::unlimited_rate_policy> >, true>::idle_ping_op<boost::asio::executor> * const this, boost::beast::error_code ec, std::size_t bytes_transferred) (/usr/local/include/boost/beast/websocket/impl/ping.hpp:179)
....
It seems that op_idle_ping implementation in the beast library cannot find the strand to execute the ping operation and an exception is raised (see websocket/impl/ping.hpp:179, I am not sure though). It is our belief that the library should function perfectly fine when the async_write function is properly stressed like our example.
Version of Beast
#define BOOST_BEAST_VERSION 248
(using boost 1_70_00)
Steps necessary to reproduce the problem
We have adjusted the client websocket async c++ example to reproduce the bug. Basically we added the timeout function with ping enabled and we keep on sending the message on the stream in a proper way.
//
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
//------------------------------------------------------------------------------
//
// Example: WebSocket SSL client, asynchronous
//
//------------------------------------------------------------------------------
#include "certificates.hpp"
#include <boost/beast/core.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/beast/websocket/ssl.hpp>
#include <boost/asio/strand.hpp>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
//------------------------------------------------------------------------------
// Report a failure
void fail(beast::error_code ec, char const *what)
{
std::cerr << what << ": " << ec.message() << "\n";
}
// Sends a WebSocket message and prints the response
class session : public std::enable_shared_from_this<session>
{
tcp::resolver resolver_;
websocket::stream<
beast::ssl_stream<beast::tcp_stream>>
ws_;
beast::flat_buffer buffer_;
std::string host_;
std::string text_;
public:
// Resolver and socket require an io_context
explicit session(net::io_context &ioc, ssl::context &ctx)
: resolver_(net::make_strand(ioc)), ws_(net::make_strand(ioc), ctx)
{
}
// Start the asynchronous operation
void
run(
char const *host,
char const *port,
char const *text)
{
// Save these for later
host_ = host;
text_ = text;
// Look up the domain name
resolver_.async_resolve(
host,
port,
beast::bind_front_handler(
&session::on_resolve,
shared_from_this()));
}
void
on_resolve(
beast::error_code ec,
tcp::resolver::results_type results)
{
if (ec)
return fail(ec, "resolve");
// Set a timeout on the operation
beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30));
// Make the connection on the IP address we get from a lookup
beast::get_lowest_layer(ws_).async_connect(
results,
beast::bind_front_handler(
&session::on_connect,
shared_from_this()));
}
void
on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type)
{
if (ec)
return fail(ec, "connect");
// Set a timeout on the operation
beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30));
// Perform the SSL handshake
ws_.next_layer().async_handshake(
ssl::stream_base::client,
beast::bind_front_handler(
&session::on_ssl_handshake,
shared_from_this()));
}
void
on_ssl_handshake(beast::error_code ec)
{
if (ec)
return fail(ec, "ssl_handshake");
// Turn off the timeout on the tcp_stream, because
// the websocket stream has its own timeout system.
beast::get_lowest_layer(ws_).expires_never();
// Set timeout
auto timeout = 3;
beast::websocket::stream_base::timeout opt{
std::chrono::seconds(timeout), // close timeout
std::chrono::seconds(timeout), // idle timeout
true}; // pings enabled (results in weird bug)
ws_.set_option(opt);
// Perform the websocket handshake
ws_.async_handshake(host_, "/",
beast::bind_front_handler(
&session::on_handshake,
shared_from_this()));
}
void
on_handshake(beast::error_code ec)
{
if (ec)
return fail(ec, "handshake");
// Start writing to the stream
ws_.async_write(
net::buffer(text_),
beast::bind_front_handler(
&session::on_write,
shared_from_this()));
// Start reading from the stream
ws_.async_read(
buffer_,
beast::bind_front_handler(
&session::on_read,
shared_from_this()));
}
void
on_write(
beast::error_code ec,
std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
if (ec)
return fail(ec, "write");
// Write again
ws_.async_write(
net::buffer(text_),
beast::bind_front_handler(
&session::on_write,
shared_from_this()));
}
void
on_read(
beast::error_code ec,
std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
if (ec)
return fail(ec, "read");
// Clear buffer
buffer_.clear();
// Read again
ws_.async_read(
buffer_,
beast::bind_front_handler(
&session::on_read,
shared_from_this()));
}
};
//------------------------------------------------------------------------------
int main(int argc, char **argv)
{
// Check command line arguments.
if (argc != 4)
{
std::cerr << "Usage: websocket-client-async-ssl <host> <port> <text>\n"
<< "Example:\n"
<< " websocket-client-async-ssl echo.websocket.org 443 \"Hello, world!\"\n";
return EXIT_FAILURE;
}
auto const host = argv[1];
auto const port = argv[2];
auto const text = argv[3];
// The io_context is required for all I/O
boost::asio::io_context ioc;
// The SSL context is required, and holds certificates
ssl::context ctx{ssl::context::tlsv13};
// Launch the asynchronous operation
std::make_shared<session>(ioc, ctx)->run(host, port, text);
ioc.run();
return EXIT_SUCCESS;
}
All relevant compiler information
We are using ubuntu 19.04 and gnu compiler
g++ (Ubuntu 8.3.0-6ubuntu1) 8.3.0
The server can be anything, a quick running server in nodejs is for example (using the certificate and key of your example directory):
const WebSocket = require('ws');
const fs = require('fs');
const https = require('https');
let cert = `-----BEGIN CERTIFICATE-----
MIIDaDCCAlCgAwIBAgIJAO8vBu8i8exWMA0GCSqGSIb3DQEBCwUAMEkxCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJDQTEtMCsGA1UEBwwkTG9zIEFuZ2VsZXNPPUJlYXN0
Q049d3d3LmV4YW1wbGUuY29tMB4XDTE3MDUwMzE4MzkxMloXDTQ0MDkxODE4Mzkx
MlowSTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMS0wKwYDVQQHDCRMb3MgQW5n
ZWxlc089QmVhc3RDTj13d3cuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQDJ7BRKFO8fqmsEXw8v9YOVXyrQVsVbjSSGEs4Vzs4cJgcF
xqGitbnLIrOgiJpRAPLy5MNcAXE1strVGfdEf7xMYSZ/4wOrxUyVw/Ltgsft8m7b
Fu8TsCzO6XrxpnVtWk506YZ7ToTa5UjHfBi2+pWTxbpN12UhiZNUcrRsqTFW+6fO
9d7xm5wlaZG8cMdg0cO1bhkz45JSl3wWKIES7t3EfKePZbNlQ5hPy7Pd5JTmdGBp
yY8anC8u4LPbmgW0/U31PH0rRVfGcBbZsAoQw5Tc5dnb6N2GEIbq3ehSfdDHGnrv
enu2tOK9Qx6GEzXh3sekZkxcgh+NlIxCNxu//Dk9AgMBAAGjUzBRMB0GA1UdDgQW
BBTZh0N9Ne1OD7GBGJYz4PNESHuXezAfBgNVHSMEGDAWgBTZh0N9Ne1OD7GBGJYz
4PNESHuXezAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCmTJVT
LH5Cru1vXtzb3N9dyolcVH82xFVwPewArchgq+CEkajOU9bnzCqvhM4CryBb4cUs
gqXWp85hAh55uBOqXb2yyESEleMCJEiVTwm/m26FdONvEGptsiCmF5Gxi0YRtn8N
V+KhrQaAyLrLdPYI7TrwAOisq2I1cD0mt+xgwuv/654Rl3IhOMx+fKWKJ9qLAiaE
fQyshjlPP9mYVxWOxqctUdQ8UnsUKKGEUcVrA08i1OAnVKlPFjKBvk+r7jpsTPcr
9pWXTO9JrYMML7d+XRSZA1n3856OqZDX4403+9FnXCvfcLZLLKTBvwwFgEFGpzjK
UEVbkhd5qstF6qWK
-----END CERTIFICATE-----`;
let key = `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJ7BRKFO8fqmsE
Xw8v9YOVXyrQVsVbjSSGEs4Vzs4cJgcFxqGitbnLIrOgiJpRAPLy5MNcAXE1strV
GfdEf7xMYSZ/4wOrxUyVw/Ltgsft8m7bFu8TsCzO6XrxpnVtWk506YZ7ToTa5UjH
fBi2+pWTxbpN12UhiZNUcrRsqTFW+6fO9d7xm5wlaZG8cMdg0cO1bhkz45JSl3wW
KIES7t3EfKePZbNlQ5hPy7Pd5JTmdGBpyY8anC8u4LPbmgW0/U31PH0rRVfGcBbZ
sAoQw5Tc5dnb6N2GEIbq3ehSfdDHGnrvenu2tOK9Qx6GEzXh3sekZkxcgh+NlIxC
Nxu//Dk9AgMBAAECggEBAK1gV8uETg4SdfE67f9v/5uyK0DYQH1ro4C7hNiUycTB
oiYDd6YOA4m4MiQVJuuGtRR5+IR3eI1zFRMFSJs4UqYChNwqQGys7CVsKpplQOW+
1BCqkH2HN/Ix5662Dv3mHJemLCKUON77IJKoq0/xuZ04mc9csykox6grFWB3pjXY
OEn9U8pt5KNldWfpfAZ7xu9WfyvthGXlhfwKEetOuHfAQv7FF6s25UIEU6Hmnwp9
VmYp2twfMGdztz/gfFjKOGxf92RG+FMSkyAPq/vhyB7oQWxa+vdBn6BSdsfn27Qs
bTvXrGe4FYcbuw4WkAKTljZX7TUegkXiwFoSps0jegECgYEA7o5AcRTZVUmmSs8W
PUHn89UEuDAMFVk7grG1bg8exLQSpugCykcqXt1WNrqB7x6nB+dbVANWNhSmhgCg
VrV941vbx8ketqZ9YInSbGPWIU/tss3r8Yx2Ct3mQpvpGC6iGHzEc/NHJP8Efvh/
CcUWmLjLGJYYeP5oNu5cncC3fXUCgYEA2LANATm0A6sFVGe3sSLO9un1brA4zlZE
Hjd3KOZnMPt73B426qUOcw5B2wIS8GJsUES0P94pKg83oyzmoUV9vJpJLjHA4qmL
CDAd6CjAmE5ea4dFdZwDDS8F9FntJMdPQJA9vq+JaeS+k7ds3+7oiNe+RUIHR1Sz
VEAKh3Xw66kCgYB7KO/2Mchesu5qku2tZJhHF4QfP5cNcos511uO3bmJ3ln+16uR
GRqz7Vu0V6f7dvzPJM/O2QYqV5D9f9dHzN2YgvU9+QSlUeFK9PyxPv3vJt/WP1//
zf+nbpaRbwLxnCnNsKSQJFpnrE166/pSZfFbmZQpNlyeIuJU8czZGQTifQKBgHXe
/pQGEZhVNab+bHwdFTxXdDzr+1qyrodJYLaM7uFES9InVXQ6qSuJO+WosSi2QXlA
hlSfwwCwGnHXAPYFWSp5Owm34tbpp0mi8wHQ+UNgjhgsE2qwnTBUvgZ3zHpPORtD
23KZBkTmO40bIEyIJ1IZGdWO32q79nkEBTY+v/lRAoGBAI1rbouFYPBrTYQ9kcjt
1yfu4JF5MvO9JrHQ9tOwkqDmNCWx9xWXbgydsn/eFtuUMULWsG3lNjfst/Esb8ch
k5cZd6pdJZa4/vhEwrYYSuEjMCnRb0lUsm7TsHxQrUd6Fi/mUuFU/haC0o0chLq7
pVOUFq5mW8p0zbtfHbjkgxyF
-----END PRIVATE KEY-----`;
const server = https.createServer({
key: key,
cert: cert
});
const wss = new WebSocket.Server({ server });
wss.on('connection', function connection(ws) {
ws.on('ping', () => {
console.log('received ping');
});
ws.on('pong', () => {
console.log('received pong');
});
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
});
// Start listen
server.listen(8080);
Hi,
We are using boost beast in a high performance manner, i.e. we need to be able to communicate >1000 transactions per second per connection. For our application it is crucial to use a timeout on the stream which is provided by your library.
We encounter a critical exception when a client has a timeout policy enabled (which sends ping/pong messages by the beast library) and when we stress the async_write function (see example client below) by sending messages all the time. The bug results in an unhandled exception:
Stack trace:
It seems that op_idle_ping implementation in the beast library cannot find the strand to execute the ping operation and an exception is raised (see websocket/impl/ping.hpp:179, I am not sure though). It is our belief that the library should function perfectly fine when the async_write function is properly stressed like our example.
Version of Beast
#define BOOST_BEAST_VERSION 248
(using boost 1_70_00)
Steps necessary to reproduce the problem
We have adjusted the client websocket async c++ example to reproduce the bug. Basically we added the timeout function with ping enabled and we keep on sending the message on the stream in a proper way.
All relevant compiler information
We are using ubuntu 19.04 and gnu compiler
The server can be anything, a quick running server in nodejs is for example (using the certificate and key of your example directory):