Invoke callback on pings and pongs (API Change):

fix #248

This additionally invokes the pong callback for received pings, allowing
callers to more efficiently detect when a connection is still lively:

* pong_callback renamed to ping_callback
* Callback signature has an extra `bool` to indicate if the received
  control frame is a ping or pong.
This commit is contained in:
Vinnie Falco
2017-07-20 08:12:14 -07:00
parent a5a6563fe2
commit 765cb22b48
9 changed files with 131 additions and 107 deletions
@@ -59,14 +59,14 @@ protected:
std::size_t wr_buf_size_ = 4096; // write buffer size
std::size_t rd_buf_size_ = 4096; // read buffer size
opcode wr_opcode_ = opcode::text; // outgoing message type
pong_cb pong_cb_; // pong callback
ping_cb ping_cb_; // ping callback
role_type role_; // server or client
bool failed_; // the connection failed
bool wr_close_; // sent close frame
op* wr_block_; // op currenly writing
ping_data* pong_data_; // where to put pong payload
ping_data* ping_data_; // where to put pong payload
invokable rd_op_; // invoked after write completes
invokable wr_op_; // invoked after read completes
close_reason cr_; // set from received close frame
@@ -212,7 +212,7 @@ open(role_type role)
rd_.cont = false;
wr_close_ = false;
wr_block_ = nullptr; // should be nullptr on close anyway
pong_data_ = nullptr; // should be nullptr on close anyway
ping_data_ = nullptr; // should be nullptr on close anyway
wr_.cont = false;
wr_.buf_size = 0;
+14 -10
View File
@@ -427,9 +427,11 @@ operator()(error_code ec,
case do_control:
if(d.fh.op == opcode::ping)
{
ping_data data;
detail::read(data, d.fb.data());
ping_data payload;
detail::read(payload, d.fb.data());
d.fb.reset();
if(d.ws.ping_cb_)
d.ws.ping_cb_(false, payload);
if(d.ws.wr_close_)
{
// ignore ping when closing
@@ -437,7 +439,7 @@ operator()(error_code ec,
break;
}
d.ws.template write_ping<static_streambuf>(
d.fb, opcode::pong, data);
d.fb, opcode::pong, payload);
if(d.ws.wr_block_)
{
// suspend
@@ -455,8 +457,8 @@ operator()(error_code ec,
code = close_code::none;
ping_data payload;
detail::read(payload, d.fb.data());
if(d.ws.pong_cb_)
d.ws.pong_cb_(payload);
if(d.ws.ping_cb_)
d.ws.ping_cb_(true, payload);
d.fb.reset();
d.state = do_read_fh;
break;
@@ -762,11 +764,13 @@ read_frame(frame_info& fi, DynamicBuffer& dynabuf, error_code& ec)
// Process control frame
if(fh.op == opcode::ping)
{
ping_data data;
detail::read(data, fb.data());
ping_data payload;
detail::read(payload, fb.data());
fb.reset();
if(ping_cb_)
ping_cb_(false, payload);
write_ping<static_streambuf>(
fb, opcode::pong, data);
fb, opcode::pong, payload);
boost::asio::write(stream_, fb.data(), ec);
failed_ = ec != 0;
if(failed_)
@@ -777,8 +781,8 @@ read_frame(frame_info& fi, DynamicBuffer& dynabuf, error_code& ec)
{
ping_data payload;
detail::read(payload, fb.data());
if(pong_cb_)
pong_cb_(payload);
if(ping_cb_)
ping_cb_(true, payload);
continue;
}
BOOST_ASSERT(fh.op == opcode::close);
+1 -1
View File
@@ -76,7 +76,7 @@ reset()
wr_close_ = false;
wr_.cont = false;
wr_block_ = nullptr; // should be nullptr on close anyway
pong_data_ = nullptr; // should be nullptr on close anyway
ping_data_ = nullptr; // should be nullptr on close anyway
stream_.buffer().consume(
stream_.buffer().size());
+26 -20
View File
@@ -188,7 +188,7 @@ struct message_type
namespace detail {
using pong_cb = std::function<void(ping_data const&)>;
using ping_cb = std::function<void(bool, ping_data const&)>;
} // detail
@@ -233,48 +233,54 @@ struct permessage_deflate
int memLevel = 4;
};
/** Pong callback option.
/** Ping callback option.
Sets the callback to be invoked whenever a pong is received
during a call to @ref beast::websocket::stream::read,
Sets the callback to be invoked whenever a ping or pong is
received during a call to
@ref beast::websocket::stream::read,
@ref beast::websocket::stream::read_frame,
@ref beast::websocket::stream::async_read, or
@ref beast::websocket::stream::async_read_frame.
Unlike completion handlers, the callback will be invoked for
each received pong during a call to any synchronous or
asynchronous read function. The operation is passive, with
no associated error code, and triggered by reads.
each received ping and pong pong during a call to any
synchronous or asynchronous read function. The operation is
passive, with no associated error code, and triggered by reads.
The signature of the callback must be:
@code
void callback(
void
callback(
bool is_pong, // `true` if this is a pong
ping_data const& payload // Payload of the pong frame
);
@endcode
If the read operation receiving a pong frame is an asynchronous
operation, the callback will be invoked using the same method as
that used to invoke the final handler.
The value of `is_pong` will be `true` if a pong control frame
is received, and `false` if a ping control frame is received.
If the read operation receiving a ping or pong frame is an
asynchronous operation, the callback will be invoked using
the same method as that used to invoke the final handler.
@note Objects of this type are used with
@ref beast::websocket::stream::set_option.
To remove the pong callback, construct the option with
no parameters: `set_option(pong_callback{})`
To remove the ping callback, construct the option with
no parameters: `set_option(ping_callback{})`
*/
#if GENERATING_DOCS
using pong_callback = implementation_defined;
using ping_callback = implementation_defined;
#else
struct pong_callback
struct ping_callback
{
detail::pong_cb value;
detail::ping_cb value;
pong_callback() = default;
pong_callback(pong_callback&&) = default;
pong_callback(pong_callback const&) = default;
ping_callback() = default;
ping_callback(ping_callback&&) = default;
ping_callback(ping_callback const&) = default;
explicit
pong_callback(detail::pong_cb f)
ping_callback(detail::ping_cb f)
: value(std::move(f))
{
}
+48 -48
View File
@@ -225,11 +225,11 @@ public:
o = pmd_opts_;
}
/// Set the pong callback
/// Set the ping callback
void
set_option(pong_callback o)
set_option(ping_callback o)
{
pong_cb_ = std::move(o.value);
ping_cb_ = std::move(o.value);
}
/// Set the read buffer size
@@ -1119,12 +1119,11 @@ public:
hold all the message payload bytes (which may be zero in length).
Control frames encountered while reading frame or message data
are handled automatically. Pings are replied to automatically,
pongs are routed to the pong callback if the option is set,
and close frames initiate the WebSocket close procedure. When a
close frame is received, this call will eventually return
@ref error::closed. Because of the need to handle control frames,
read operations can cause writes to take place.
are handled automatically. Pings are replied to with a pong,
received pings and pongs invoke the @ref ping_callback if the
option is set, and close frames initiate the WebSocket close
procedure. When a close frame is received, this call will
eventually return @ref error::closed.
@param op A value to receive the message type.
This object must remain valid until the handler is called.
@@ -1155,13 +1154,12 @@ public:
hold all the message payload bytes (which may be zero in length).
Control frames encountered while reading frame or message data
are handled automatically. Pings are replied to automatically,
pongs are routed to the pong callback if the option is set,
and close frames initiate the WebSocket close procedure. When a
close frame is received, this call will eventually return
@ref error::closed. Because of the need to handle control frames,
read operations can cause writes to take place.
are handled automatically. Pings are replied to with a pong,
received pings and pongs invoke the @ref ping_callback if the
option is set, and close frames initiate the WebSocket close
procedure. When a close frame is received, this call will
eventually return @ref error::closed.
@param op A value to receive the message type.
This object must remain valid until the handler is called.
@@ -1196,14 +1194,17 @@ public:
hold all the message payload bytes (which may be zero in length).
Control frames encountered while reading frame or message data
are handled automatically. Pings are replied to automatically,
pongs are routed to the pong callback if the option is set,
and close frames initiate the WebSocket close procedure. When a
close frame is received, this call will eventually return
@ref error::closed. Because of the need to handle control
frames, these read operations can cause writes to take place.
Despite this, calls to `async_read` and `async_read_frame`
only count as read operations.
are handled automatically. Pings are replied to with a pong,
received pings and pongs invoke the @ref ping_callback if the
option is set, and close frames initiate the WebSocket close
procedure. When a close frame is received, this call will
eventually return @ref error::closed.
Because of the need to handle control frames, read operations
can cause writes to take place. These writes are managed
transparently; callers can still have one active asynchronous
read and asynchronous write operation pending simultaneously
(a user initiated call to @ref async_close counts as a write).
@param op A value to receive the message type.
This object must remain valid until the handler is called.
@@ -1255,13 +1256,12 @@ public:
fi.fin == true, and zero bytes placed into the stream buffer.
Control frames encountered while reading frame or message data
are handled automatically. Pings are replied to automatically,
pongs are routed to the pong callback if the option is set,
and close frames initiate the WebSocket close procedure. When a
close frame is received, this call will eventually return
@ref error::closed. Because of the need to handle control frames,
read operations can cause writes to take place.
are handled automatically. Pings are replied to with a pong,
received pings and pongs invoke the @ref ping_callback if the
option is set, and close frames initiate the WebSocket close
procedure. When a close frame is received, this call will
eventually return @ref error::closed.
@param fi An object to store metadata about the message.
@param dynabuf A dynamic buffer to hold the message data after
@@ -1294,13 +1294,12 @@ public:
fi.fin == true, and zero bytes placed into the stream buffer.
Control frames encountered while reading frame or message data
are handled automatically. Pings are replied to automatically,
pongs are routed to the pong callback if the option is set,
and close frames initiate the WebSocket close procedure. When a
close frame is received, this call will eventually return
@ref error::closed. Because of the need to handle control frames,
read operations can cause writes to take place.
are handled automatically. Pings are replied to with a pong,
received pings and pongs invoke the @ref ping_callback if the
option is set, and close frames initiate the WebSocket close
procedure. When a close frame is received, this call will
eventually return @ref error::closed.
@param fi An object to store metadata about the message.
@param dynabuf A dynamic buffer to hold the message data after
@@ -1338,16 +1337,17 @@ public:
the stream buffer.
Control frames encountered while reading frame or message data
are handled automatically. Pings are replied to automatically,
pongs are routed to the pong callback if the option is set,
and close frames initiate the WebSocket close procedure. When a
close frame is received, this call will eventually return
@ref error::closed. Because of the need to handle control frames,
read operations can cause writes to take place. These writes are
managed transparently; callers can still have one active
asynchronous read and asynchronous write operation pending
simultaneously (a user initiated call to @ref async_close
counts as a write).
are handled automatically. Pings are replied to with a pong,
received pings and pongs invoke the @ref ping_callback if the
option is set, and close frames initiate the WebSocket close
procedure. When a close frame is received, this call will
eventually return @ref error::closed.
Because of the need to handle control frames, read operations
can cause writes to take place. These writes are managed
transparently; callers can still have one active asynchronous
read and asynchronous write operation pending simultaneously
(a user initiated call to @ref async_close counts as a write).
@param fi An object to store metadata about the message.
This object must remain valid until the handler is called.