diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5863b5cb..393b2237 100644
--- a/CHANGELOG.md
+++ b/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:
diff --git a/doc/7_6_control.qbk b/doc/7_6_control.qbk
index ec79be7d..d39f4f08 100644
--- a/doc/7_6_control.qbk
+++ b/doc/7_6_control.qbk
@@ -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]
diff --git a/doc/quickref.xml b/doc/quickref.xml
index 0986ad41..3e27d12a 100644
--- a/doc/quickref.xml
+++ b/doc/quickref.xml
@@ -132,6 +132,7 @@
close_codeerror
+ frame_type
diff --git a/include/beast/websocket/impl/read.ipp b/include/beast/websocket/impl/read.ipp
index f3fbde36..516f7466 100644
--- a/include/beast/websocket/impl/read.ipp
+++ b/include/beast/websocket/impl/read.ipp
@@ -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(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_;
diff --git a/include/beast/websocket/stream.hpp b/include/beast/websocket/stream.hpp
index 2047edde..4f77114e 100644
--- a/include/beast/websocket/stream.hpp
+++ b/include/beast/websocket/stream.hpp
@@ -31,6 +31,7 @@
#include
#include
#include
+#include
#include
#include
@@ -47,6 +48,22 @@ using request_type = http::request;
/// The type of object holding HTTP Upgrade responses
using response_type = http::response;
+/** 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;
+ using control_cb_type =
+ std::function;
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 cb)
+ control_callback(
+ std::function 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.
diff --git a/test/websocket/doc_snippets.cpp b/test/websocket/doc_snippets.cpp
index 87733ec1..d3e6c053 100644
--- a/test/websocket/doc_snippets.cpp
+++ b/test/websocket/doc_snippets.cpp
@@ -187,11 +187,11 @@ boost::asio::ip::tcp::socket sock{ios};
{
stream 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);
});
//]
diff --git a/test/websocket/stream.cpp b/test/websocket/stream.cpp
index 2c670a60..02bab544 100644
--- a/test/websocket/stream.cpp
+++ b/test/websocket/stream.cpp
@@ -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, "");