mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 21:07:26 +02:00
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:
12
CHANGELOG.md
12
CHANGELOG.md
@ -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:
|
||||
|
@ -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]
|
||||
|
@ -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>
|
||||
|
@ -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_;
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
});
|
||||
//]
|
||||
|
||||
|
@ -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, "");
|
||||
|
Reference in New Issue
Block a user