control frame callbacks are non-const references:

fix #653

Actions Required:

* Modify calls to set the control frame callback, to
  pass non-const reference instances, and manage the
  lifetime of the instance.
This commit is contained in:
Vinnie Falco
2017-07-28 12:04:55 -07:00
parent 629872b28f
commit e15448a83a
4 changed files with 82 additions and 50 deletions

View File

@ -20,6 +20,16 @@ WebSocket:
* Refactor accept, handshake ops
* Use read buffer instead of buffered stream
API Changes
* control frame callbacks are non-const references
Actions Required:
* Modify calls to set the control frame callback, to
pass non-const reference instances, and manage the
lifetime of the instance.
--------------------------------------------------------------------------------
Version 90:

View File

@ -561,7 +561,7 @@ public:
return wr_opcode_ == detail::opcode::binary;
}
/** Set the control frame callback.
/** Set a callback to be invoked on each incoming control frame.
Sets the callback to be invoked whenever a ping, pong,
or close control frame is received during a call to one
@ -577,7 +577,11 @@ public:
or asynchronous read function. The operation is passive,
with no associated error code, and triggered by reads.
The signature of the callback must be:
For close frames, the close reason code may be obtained by
calling the function @ref reason.
@param cb The function object to call, which must be
invocable with this equivalent signature:
@code
void
callback(
@ -585,10 +589,9 @@ public:
string_view payload // The payload in the frame
);
@endcode
For close frames, the close reason code may be obtained by
calling the function @ref reason.
The implementation type-erases the callback without requiring
a dynamic allocation. For this reason, the callback object is
passed by a non-constant reference.
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.
@ -597,14 +600,26 @@ public:
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.
*/
template<class Callback>
void
control_callback(Callback& cb)
{
// Callback may not be constant, caller is responsible for
// managing the lifetime of the callback. Copies are not made.
BOOST_STATIC_ASSERT(! std::is_const<Callback>::value);
@param cb The callback to set.
ctrl_cb_ = std::ref(cb);
}
/** Reset the control frame callback.
This function removes any previously set control frame callback.
*/
void
control_callback(
std::function<void(frame_type, string_view)> cb)
control_callback()
{
ctrl_cb_ = std::move(cb);
ctrl_cb_ = {};
}
/** Set the maximum incoming message size option.

View File

@ -189,12 +189,13 @@ boost::asio::ip::tcp::socket sock{ios};
{
stream<boost::asio::ip::tcp::socket> ws{ios};
//[ws_snippet_17
ws.control_callback(
auto cb =
[](frame_type kind, string_view payload)
{
// Do something with the payload
boost::ignore_unused(kind, payload);
});
};
ws.control_callback(cb);
//]
//[ws_snippet_18

View File

@ -1675,50 +1675,56 @@ public:
bool once;
// send ping and message
once = false;
ws.control_callback(
[&](frame_type kind, string_view s)
{
BEAST_EXPECT(kind == frame_type::pong);
BEAST_EXPECT(! once);
once = true;
BEAST_EXPECT(s == "");
});
c.ping(ws, "");
ws.binary(true);
c.write(ws, sbuf("Hello"));
{
// receive echoed message
multi_buffer db;
c.read(ws, db);
BEAST_EXPECT(once);
BEAST_EXPECT(ws.got_binary());
BEAST_EXPECT(to_string(db.data()) == "Hello");
once = false;
auto cb =
[&](frame_type kind, string_view s)
{
BEAST_EXPECT(kind == frame_type::pong);
BEAST_EXPECT(! once);
once = true;
BEAST_EXPECT(s == "");
};
ws.control_callback(cb);
c.ping(ws, "");
ws.binary(true);
c.write(ws, sbuf("Hello"));
{
// receive echoed message
multi_buffer db;
c.read(ws, db);
BEAST_EXPECT(once);
BEAST_EXPECT(ws.got_binary());
BEAST_EXPECT(to_string(db.data()) == "Hello");
}
ws.control_callback();
}
ws.control_callback({});
// send ping and fragmented message
once = false;
ws.control_callback(
[&](frame_type kind, string_view s)
{
BEAST_EXPECT(kind == frame_type::pong);
BEAST_EXPECT(! once);
once = true;
BEAST_EXPECT(s == "payload");
});
ws.ping("payload");
c.write_some(ws, false, sbuf("Hello, "));
c.write_some(ws, false, sbuf(""));
c.write_some(ws, true, sbuf("World!"));
{
// receive echoed message
multi_buffer db;
c.read(ws, db);
BEAST_EXPECT(once);
BEAST_EXPECT(to_string(db.data()) == "Hello, World!");
once = false;
auto cb =
[&](frame_type kind, string_view s)
{
BEAST_EXPECT(kind == frame_type::pong);
BEAST_EXPECT(! once);
once = true;
BEAST_EXPECT(s == "payload");
};
ws.control_callback(cb);
ws.ping("payload");
c.write_some(ws, false, sbuf("Hello, "));
c.write_some(ws, false, sbuf(""));
c.write_some(ws, true, sbuf("World!"));
{
// receive echoed message
multi_buffer db;
c.read(ws, db);
BEAST_EXPECT(once);
BEAST_EXPECT(to_string(db.data()) == "Hello, World!");
}
ws.control_callback();
}
ws.control_callback({});
// send pong
c.pong(ws, "");