From e15448a83adb16da6ed440542ee96a7bd9d9adfc Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Fri, 28 Jul 2017 12:04:55 -0700 Subject: [PATCH] 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. --- CHANGELOG.md | 10 +++ include/boost/beast/websocket/stream.hpp | 35 +++++++--- test/websocket/doc_snippets.cpp | 5 +- test/websocket/stream.cpp | 82 +++++++++++++----------- 4 files changed, 82 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11c066d8..b086e768 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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: diff --git a/include/boost/beast/websocket/stream.hpp b/include/boost/beast/websocket/stream.hpp index ea572cc5..bbfc03a9 100644 --- a/include/boost/beast/websocket/stream.hpp +++ b/include/boost/beast/websocket/stream.hpp @@ -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 + 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::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 cb) + control_callback() { - ctrl_cb_ = std::move(cb); + ctrl_cb_ = {}; } /** Set the maximum incoming message size option. diff --git a/test/websocket/doc_snippets.cpp b/test/websocket/doc_snippets.cpp index 14406093..751d099d 100644 --- a/test/websocket/doc_snippets.cpp +++ b/test/websocket/doc_snippets.cpp @@ -189,12 +189,13 @@ boost::asio::ip::tcp::socket sock{ios}; { stream 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 diff --git a/test/websocket/stream.cpp b/test/websocket/stream.cpp index cbe830f9..c429ee8b 100644 --- a/test/websocket/stream.cpp +++ b/test/websocket/stream.cpp @@ -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, "");