diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4aec8f03..f1f6179c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+1.0.0-b27
+
+API Changes:
+
+* Invoke callback on pings and pongs
+
+--------------------------------------------------------------------------------
+
1.0.0-b26
* Tidy up warnings and tests
diff --git a/doc/quickref.xml b/doc/quickref.xml
index 238a49fe..0bfafa6d 100644
--- a/doc/quickref.xml
+++ b/doc/quickref.xml
@@ -129,7 +129,7 @@
keep_alivemessage_typepermessage_deflate
- pong_callback
+ ping_callbackread_buffer_sizeread_message_maxwrite_buffer_size
diff --git a/doc/websocket.qbk b/doc/websocket.qbk
index 904a703c..e87e7fad 100644
--- a/doc/websocket.qbk
+++ b/doc/websocket.qbk
@@ -309,9 +309,9 @@ and received a close frame.
During read operations, Beast automatically reads and processes control
frames. Pings are replied to as soon as possible with a pong, received
-pongs are delivered to the pong callback. The receipt of a close frame
-initiates the WebSocket close procedure, eventually resulting in the error
-code [link beast.ref.websocket__error `error::closed`] being delivered
+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.websocket__error `error::closed`] being delivered
to the caller in a subsequent read operation, assuming no other error
takes place.
@@ -331,29 +331,33 @@ using the functions
[link beast.ref.websocket__stream.ping `ping`] and
[link beast.ref.websocket__stream.pong `pong`].
-To receive pong control frames, callers may register a "pong callback" using
-[link beast.ref.websocket__stream.set_option `set_option`]. The object provided
-with this option should be callable with the following signature:
+To be notified of ping and pong control frames, callers may register a
+"ping callback" using [link beast.ref.websocket__stream.set_option `set_option`].
+The object provided with this option should be callable with the following
+signature:
```
- void on_pong(ping_data const& payload);
+ void on_ping(bool is_pong, ping_data const& payload);
...
- ws.set_option(pong_callback{&on_pong});
+ ws.set_option(ping_callback{&on_ping});
```
-When a pong callback is registered, any pongs received through either
-synchronous read functions or asynchronous read functions will invoke the
-pong callback, passing the payload in the pong message as the argument.
+When a ping callback is registered, all pings and pongs received through
+either synchronous read functions or asynchronous read functions will
+invoke the ping callback, with the value of `is_pong` set to `true` if a
+pong was received else `false` if a ping was received. The payload of
+the ping or pong control frame is passed in the payload argument.
Unlike regular completion handlers used in calls to asynchronous initiation
-functions, the pong callback only needs to be set once. The callback is not
-reset when a pong is received. The same callback is used for both synchronous
-and asynchronous reads. The pong callback is passive; in order to receive
-pongs, a synchronous or asynchronous stream read function must be active.
+functions, the ping callback only needs to be set once. The callback is not
+reset when a ping or pong is received. The same callback is used for both
+synchronous and asynchronous reads. The ping callback is passive; in order
+to receive pings and pongs, a synchronous or asynchronous stream read
+function must be active.
[note
- When an asynchronous read function receives a pong, the the pong
- 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 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.
]
[heading Close Frames]
diff --git a/include/beast/websocket/detail/stream_base.hpp b/include/beast/websocket/detail/stream_base.hpp
index b183fc46..4f1fa999 100644
--- a/include/beast/websocket/detail/stream_base.hpp
+++ b/include/beast/websocket/detail/stream_base.hpp
@@ -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;
diff --git a/include/beast/websocket/impl/read.ipp b/include/beast/websocket/impl/read.ipp
index b495e304..71eae270 100644
--- a/include/beast/websocket/impl/read.ipp
+++ b/include/beast/websocket/impl/read.ipp
@@ -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(
- 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(
- 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);
diff --git a/include/beast/websocket/impl/stream.ipp b/include/beast/websocket/impl/stream.ipp
index a8c2ff61..7a58c6aa 100644
--- a/include/beast/websocket/impl/stream.ipp
+++ b/include/beast/websocket/impl/stream.ipp
@@ -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());
diff --git a/include/beast/websocket/option.hpp b/include/beast/websocket/option.hpp
index e656d064..8f176f2c 100644
--- a/include/beast/websocket/option.hpp
+++ b/include/beast/websocket/option.hpp
@@ -188,7 +188,7 @@ struct message_type
namespace detail {
-using pong_cb = std::function;
+using ping_cb = std::function;
} // 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))
{
}
diff --git a/include/beast/websocket/stream.hpp b/include/beast/websocket/stream.hpp
index 8c1c3cde..b7752435 100644
--- a/include/beast/websocket/stream.hpp
+++ b/include/beast/websocket/stream.hpp
@@ -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.
diff --git a/test/websocket/stream.cpp b/test/websocket/stream.cpp
index 63256766..2554cc72 100644
--- a/test/websocket/stream.cpp
+++ b/test/websocket/stream.cpp
@@ -1062,9 +1062,10 @@ public:
// send ping and message
bool pong = false;
- ws.set_option(pong_callback{
- [&](ping_data const& payload)
+ ws.set_option(ping_callback{
+ [&](bool is_pong, ping_data const& payload)
{
+ BEAST_EXPECT(is_pong);
BEAST_EXPECT(! pong);
pong = true;
BEAST_EXPECT(payload == "");
@@ -1081,12 +1082,13 @@ public:
BEAST_EXPECT(op == opcode::binary);
BEAST_EXPECT(to_string(db.data()) == "Hello");
}
- ws.set_option(pong_callback{});
+ ws.set_option(ping_callback{});
// send ping and fragmented message
- ws.set_option(pong_callback{
- [&](ping_data const& payload)
+ ws.set_option(ping_callback{
+ [&](bool is_pong, ping_data const& payload)
{
+ BEAST_EXPECT(is_pong);
BEAST_EXPECT(payload == "payload");
}});
ws.ping("payload");
@@ -1101,7 +1103,7 @@ public:
BEAST_EXPECT(pong == 1);
BEAST_EXPECT(to_string(db.data()) == "Hello, World!");
}
- ws.set_option(pong_callback{});
+ ws.set_option(ping_callback{});
// send pong
c.pong(ws, "");