control_callback replaces ping_callback (API Change):

fix #322

The new callback is informed of all frame types including close.

Actions Required:

* Change calls to websocket::stream::ping_callback to use
  websocket::stream::control_callback

* Change user defined ping callbacks to have the new
  signature and adjust the callback definition appropriately.
This commit is contained in:
Vinnie Falco
2017-06-24 12:11:46 -07:00
parent 44824a4166
commit e7b01dc5c4
7 changed files with 110 additions and 60 deletions

View File

@ -5,6 +5,18 @@ Version 67:
* Merge stream_base to stream and tidy
* Use boost::string_view
API Changes:
* control_callback replaces ping_callback
Actions Required:
* Change calls to websocket::stream::ping_callback to use
websocket::stream::control_callback
* Change user defined ping callbacks to have the new
signature and adjust the callback definition appropriately.
--------------------------------------------------------------------------------
Version 66:

View File

@ -25,9 +25,10 @@ During read operations, Beast automatically reads and processes control
frames. Pings are replied to as soon as possible with a pong, received
ping and pongs are delivered to the ping callback. The receipt of a close
frame initiates the WebSocket close procedure, eventually resulting in the
error code [link beast.ref.beast__websocket__error `error::closed`] being delivered
to the caller in a subsequent read operation, assuming no other error
takes place.
error code
[link beast.ref.beast__websocket__error `error::closed`]
being delivered to the caller in a subsequent read operation, assuming
no other error takes place.
A consequence of this automatic behavior is that caller-initiated read
operations can cause socket writes. However, these writes will not
@ -37,16 +38,16 @@ read operations still only count as a read. This means that callers can
have a simultaneously active read, write, and ping operation in progress,
while the implementation also automatically handles control frames.
[heading Ping and Pong Frames]
[heading Control Callback]
Ping and pong messages are control frames which may be sent at any time
by either peer on an established WebSocket connection. They are sent
using the functions
[link beast.ref.beast__websocket__stream.ping `ping`] and
[link beast.ref.beast__websocket__stream.pong `pong`].
To be notified of ping and pong control frames, callers may register a
"ping callback" using [link beast.ref.beast__websocket__stream.set_option `set_option`].
To be notified of control frames, including pings, pongs, and close frames,
callers may register a ['control callback] using
[link beast.ref.beast__websocket__stream.control_callback `control_callback`].
The object provided with this option should be callable with the following
signature:
@ -66,9 +67,10 @@ to receive pings and pongs, a synchronous or asynchronous stream read
function must be active.
[note
When an asynchronous read function receives a ping or pong, the
ping callback is invoked in the same manner as that used to invoke
the final completion handler of the corresponding read function.
When an asynchronous read function receives a control frame, the
control callback is invoked in the same manner as that used to
invoke the final completion handler of the corresponding read
function.
]
[heading Close Frames]

View File

@ -132,6 +132,7 @@
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.beast__websocket__close_code">close_code</link></member>
<member><link linkend="beast.ref.beast__websocket__error">error</link></member>
<member><link linkend="beast.ref.beast__websocket__frame_type">frame_type</link></member>
</simplelist>
</entry>
</row>

View File

@ -437,8 +437,9 @@ operator()(error_code ec,
ping_data payload;
detail::read(payload, d.fb.data());
d.fb.consume(d.fb.size());
if(d.ws.ping_cb_)
d.ws.ping_cb_(false, payload);
if(d.ws.ctrl_cb_)
d.ws.ctrl_cb_(
frame_type::ping, payload);
if(d.ws.wr_close_)
{
// ignore ping when closing
@ -463,8 +464,9 @@ operator()(error_code ec,
code = close_code::none;
ping_data payload;
detail::read(payload, d.fb.data());
if(d.ws.ping_cb_)
d.ws.ping_cb_(true, payload);
if(d.ws.ctrl_cb_)
d.ws.ctrl_cb_(
frame_type::pong, payload);
d.fb.consume(d.fb.size());
d.state = do_read_fh;
break;
@ -478,6 +480,9 @@ operator()(error_code ec,
d.state = do_fail;
break;
}
if(d.ws.ctrl_cb_)
d.ws.ctrl_cb_(frame_type::close,
d.ws.cr_.reason);
if(! d.ws.wr_close_)
{
auto cr = d.ws.cr_;
@ -791,8 +796,8 @@ read_frame(DynamicBuffer& dynabuf, error_code& ec)
ping_data payload;
detail::read(payload, fb.data());
fb.consume(fb.size());
if(ping_cb_)
ping_cb_(false, payload);
if(ctrl_cb_)
ctrl_cb_(frame_type::ping, payload);
write_ping<static_buffer>(fb,
detail::opcode::pong, payload);
boost::asio::write(stream_, fb.data(), ec);
@ -805,8 +810,8 @@ read_frame(DynamicBuffer& dynabuf, error_code& ec)
{
ping_data payload;
detail::read(payload, fb.data());
if(ping_cb_)
ping_cb_(true, payload);
if(ctrl_cb_)
ctrl_cb_(frame_type::pong, payload);
continue;
}
BOOST_ASSERT(fh.op == detail::opcode::close);
@ -814,6 +819,8 @@ read_frame(DynamicBuffer& dynabuf, error_code& ec)
detail::read(cr_, fb.data(), code);
if(code != close_code::none)
goto do_close;
if(ctrl_cb_)
ctrl_cb_(frame_type::close, cr_.reason);
if(! wr_close_)
{
auto cr = cr_;

View File

@ -31,6 +31,7 @@
#include <boost/asio.hpp>
#include <algorithm>
#include <cstdint>
#include <functional>
#include <limits>
#include <type_traits>
@ -47,6 +48,22 @@ using request_type = http::request<http::empty_body>;
/// The type of object holding HTTP Upgrade responses
using response_type = http::response<http::string_body>;
/** The type of received control frame.
Values of this type are passed to the control frame
callback set using @ref stream::control_callback.
*/
enum class frame_type
{
/// A close frame was received
close,
/// A ping frame was received
ping,
/// A pong frame was received
pong
};
//--------------------------------------------------------------------
@ -112,8 +129,8 @@ class stream
friend class frame_test;
using ping_callback_type =
std::function<void(bool, ping_data const&)>;
using control_cb_type =
std::function<void(frame_type, string_view)>;
struct op {};
@ -125,7 +142,7 @@ class stream
std::size_t rd_buf_size_ = 4096; // read buffer size
detail::opcode wr_opcode_ =
detail::opcode::text; // outgoing message type
ping_callback_type ping_cb_; // ping callback
control_cb_type ctrl_cb_; // control callback
role_type role_; // server or client
bool failed_; // the connection failed
@ -453,10 +470,11 @@ public:
return wr_opcode_ == detail::opcode::binary;
}
/** Set the ping callback.
/** Set the control frame callback.
Sets the callback to be invoked whenever a ping or pong is
received during a call to one of the following functions:
Sets the callback to be invoked whenever a ping, pong,
or close control frame is received during a call to one
of the following functions:
@li @ref beast::websocket::stream::read
@li @ref beast::websocket::stream::read_frame
@ -464,33 +482,38 @@ public:
@li @ref beast::websocket::stream::async_read_frame
Unlike completion handlers, the callback will be invoked
for each received ping and pong during a call to any
synchronous or asynchronous read function. The operation is
passive, with no associated error code, and triggered by reads.
for each control frame 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(
bool is_pong, // `true` if this is a pong
ping_data const& payload // Payload of the pong frame
frame_type kind, // The type of frame
string_view payload // The payload in the frame
);
@endcode
The value of `is_pong` will be `true` if a pong control frame
is received, and `false` if a ping control frame is received.
For close frames, the close reason code may be obtained by
calling the function @ref reason.
If the read operation receiving a ping or pong frame is an
asynchronous operation, the callback will be invoked using
If the read operation which receives the control frame is
an asynchronous operation, the callback will be invoked using
the same method as that used to invoke the final handler.
@note It is not necessary to send a close frame upon receipt
of a close frame. The implementation does this automatically.
Attempting to send a close frame after a close frame is
received will result in undefined behavior.
@param cb The callback to set.
*/
void
ping_callback(
std::function<void(bool, ping_data const&)> cb)
control_callback(
std::function<void(frame_type, string_view)> cb)
{
ping_cb_ = std::move(cb);
ctrl_cb_ = std::move(cb);
}
/** Set the read buffer size option.
@ -2757,7 +2780,7 @@ public:
@li A pong frame is sent when a ping frame is received.
@li The @ref ping_callback is invoked when a ping frame
@li The @ref control_callback is invoked when a ping frame
or pong frame is received.
@li The WebSocket close procedure is started if a close frame
@ -2794,7 +2817,7 @@ public:
During reads, the implementation handles control frames as
follows:
@li The @ref ping_callback is invoked when a ping frame
@li The @ref control_callback is invoked when a ping frame
or pong frame is received.
@li A pong frame is sent when a ping frame is received.
@ -2838,7 +2861,7 @@ public:
During reads, the implementation handles control frames as
follows:
@li The @ref ping_callback is invoked when a ping frame
@li The @ref control_callback is invoked when a ping frame
or pong frame is received.
@li A pong frame is sent when a ping frame is received.
@ -2895,7 +2918,7 @@ public:
During reads, the implementation handles control frames as
follows:
@li The @ref ping_callback is invoked when a ping frame
@li The @ref control_callback is invoked when a ping frame
or pong frame is received.
@li A pong frame is sent when a ping frame is received.
@ -2931,7 +2954,7 @@ public:
During reads, the implementation handles control frames as
follows:
@li The @ref ping_callback is invoked when a ping frame
@li The @ref control_callback is invoked when a ping frame
or pong frame is received.
@li A pong frame is sent when a ping frame is received.
@ -2971,7 +2994,7 @@ public:
During reads, the implementation handles control frames as
follows:
@li The @ref ping_callback is invoked when a ping frame
@li The @ref control_callback is invoked when a ping frame
or pong frame is received.
@li A pong frame is sent when a ping frame is received.

View File

@ -187,11 +187,11 @@ boost::asio::ip::tcp::socket sock{ios};
{
stream<boost::asio::ip::tcp::socket> ws{ios};
//[ws_snippet_17
ws.ping_callback(
[](bool is_pong, ping_data const& payload)
ws.control_callback(
[](frame_type kind, string_view payload)
{
// Do something with the payload
boost::ignore_unused(is_pong, payload);
boost::ignore_unused(kind, payload);
});
//]

View File

@ -1656,15 +1656,17 @@ public:
c.close(ws, {close_code::going_away, "Going away"});
restart(error::closed);
bool once;
// send ping and message
bool pong = false;
ws.ping_callback(
[&](bool is_pong, ping_data const& payload)
once = false;
ws.control_callback(
[&](frame_type kind, string_view s)
{
BEAST_EXPECT(is_pong);
BEAST_EXPECT(! pong);
pong = true;
BEAST_EXPECT(payload == "");
BEAST_EXPECT(kind == frame_type::pong);
BEAST_EXPECT(! once);
once = true;
BEAST_EXPECT(s == "");
});
c.ping(ws, "");
ws.binary(true);
@ -1673,18 +1675,21 @@ public:
// receive echoed message
multi_buffer db;
c.read(ws, db);
BEAST_EXPECT(pong == 1);
BEAST_EXPECT(once);
BEAST_EXPECT(ws.got_binary());
BEAST_EXPECT(to_string(db.data()) == "Hello");
}
ws.ping_callback({});
ws.control_callback({});
// send ping and fragmented message
ws.ping_callback(
[&](bool is_pong, ping_data const& payload)
once = false;
ws.control_callback(
[&](frame_type kind, string_view s)
{
BEAST_EXPECT(is_pong);
BEAST_EXPECT(payload == "payload");
BEAST_EXPECT(kind == frame_type::pong);
BEAST_EXPECT(! once);
once = true;
BEAST_EXPECT(s == "payload");
});
ws.ping("payload");
c.write_frame(ws, false, sbuf("Hello, "));
@ -1694,10 +1699,10 @@ public:
// receive echoed message
multi_buffer db;
c.read(ws, db);
BEAST_EXPECT(pong == 1);
BEAST_EXPECT(once);
BEAST_EXPECT(to_string(db.data()) == "Hello, World!");
}
ws.ping_callback({});
ws.control_callback({});
// send pong
c.pong(ws, "");