From f904759877305d1bf521fb09f3856130502ecbc6 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Thu, 3 Nov 2016 17:53:32 -0400 Subject: [PATCH] Add websocket::stream pong and async_pong --- CHANGELOG.md | 1 + doc/websocket.qbk | 2 +- include/beast/http/message.hpp | 2 +- .../beast/websocket/detail/stream_base.hpp | 1 - include/beast/websocket/impl/ping.ipp | 57 +++++++++-- include/beast/websocket/impl/write.ipp | 4 +- include/beast/websocket/stream.hpp | 99 ++++++++++++++++++- test/websocket/stream.cpp | 6 ++ 8 files changed, 156 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 539372a7..9e691d5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ WebSocket * Write buffer option does not change capacity * Close connection during async_read on close frame +* Add pong, async pong to stream Core diff --git a/doc/websocket.qbk b/doc/websocket.qbk index 8834d134..49681e52 100644 --- a/doc/websocket.qbk +++ b/doc/websocket.qbk @@ -329,7 +329,7 @@ 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.websocket__stream.ping `ping`] and - [link beast.ref.websocket__stream.ping `pong`]. + [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 diff --git a/include/beast/http/message.hpp b/include/beast/http/message.hpp index 8d503dcf..406e8c1b 100644 --- a/include/beast/http/message.hpp +++ b/include/beast/http/message.hpp @@ -226,7 +226,7 @@ struct message_headers The Reason-Phrase is obsolete as of rfc7230. - @note This field is present only if `isRequest == false`. + @note This field is present only if `isRequest == false`. */ std::string reason; diff --git a/include/beast/websocket/detail/stream_base.hpp b/include/beast/websocket/detail/stream_base.hpp index ec1c7fba..d62375e2 100644 --- a/include/beast/websocket/detail/stream_base.hpp +++ b/include/beast/websocket/detail/stream_base.hpp @@ -104,7 +104,6 @@ protected: // The write buffer. // The buffer is allocated or reallocated at the beginning of // sending a message. - // std::unique_ptr buf; void diff --git a/include/beast/websocket/impl/ping.ipp b/include/beast/websocket/impl/ping.ipp index 8fc6b0f8..cfe91277 100644 --- a/include/beast/websocket/impl/ping.ipp +++ b/include/beast/websocket/impl/ping.ipp @@ -38,7 +38,7 @@ class stream::ping_op template data(DeducedHandler&& h_, stream& ws_, - ping_data const& payload) + opcode op_, ping_data const& payload) : ws(ws_) , h(std::forward(h_)) , cont(boost_asio_handler_cont_helpers:: @@ -46,8 +46,8 @@ class stream::ping_op { using boost::asio::buffer; using boost::asio::buffer_copy; - ws.template write_ping( - fb, opcode::ping, payload); + ws.template write_ping< + static_streambuf>(fb, op_, payload); } }; @@ -191,19 +191,38 @@ upcall: } template -template +template typename async_completion< - PingHandler, void(error_code)>::result_type + WriteHandler, void(error_code)>::result_type stream:: -async_ping(ping_data const& payload, PingHandler&& handler) +async_ping(ping_data const& payload, WriteHandler&& handler) { static_assert(is_AsyncStream::value, "AsyncStream requirements requirements not met"); beast::async_completion< - PingHandler, void(error_code) + WriteHandler, void(error_code) > completion(handler); ping_op{ - completion.handler, *this, payload}; + completion.handler, *this, + opcode::ping, payload}; + return completion.result.get(); +} + +template +template +typename async_completion< + WriteHandler, void(error_code)>::result_type +stream:: +async_pong(ping_data const& payload, WriteHandler&& handler) +{ + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); + beast::async_completion< + WriteHandler, void(error_code) + > completion(handler); + ping_op{ + completion.handler, *this, + opcode::pong, payload}; return completion.result.get(); } @@ -229,6 +248,28 @@ ping(ping_data const& payload, error_code& ec) boost::asio::write(stream_, db.data(), ec); } +template +void +stream:: +pong(ping_data const& payload) +{ + error_code ec; + pong(payload, ec); + if(ec) + throw system_error{ec}; +} + +template +void +stream:: +pong(ping_data const& payload, error_code& ec) +{ + detail::frame_streambuf db; + write_ping( + db, opcode::pong, payload); + boost::asio::write(stream_, db.data(), ec); +} + //------------------------------------------------------------------------------ } // websocket diff --git a/include/beast/websocket/impl/write.ipp b/include/beast/websocket/impl/write.ipp index d1cffa05..0f84692b 100644 --- a/include/beast/websocket/impl/write.ipp +++ b/include/beast/websocket/impl/write.ipp @@ -40,7 +40,7 @@ namespace websocket { system call, by concatenating the frame header and the payload. In the client role, this will send a single frame in one system - calls, using the write buffer to calculate masked data. + call, using the write buffer to calculate masked data. 2. autofragment: true compression: false @@ -215,7 +215,7 @@ public: template template -void +void stream:: write_frame_op:: operator()(error_code ec, std::size_t) diff --git a/include/beast/websocket/stream.hpp b/include/beast/websocket/stream.hpp index 85d3fc4a..a5bedaf3 100644 --- a/include/beast/websocket/stream.hpp +++ b/include/beast/websocket/stream.hpp @@ -987,14 +987,107 @@ public: this function. Invocation of the handler will be performed in a manner equivalent to using `boost::asio::io_service::post`. */ - template + template #if GENERATING_DOCS void_or_deduced #else typename async_completion< - PingHandler, void(error_code)>::result_type + WriteHandler, void(error_code)>::result_type #endif - async_ping(ping_data const& payload, PingHandler&& handler); + async_ping(ping_data const& payload, WriteHandler&& handler); + + /** Send a WebSocket pong frame. + + This function is used to synchronously send a pong frame on + the stream. The call blocks until one of the following is true: + + @li The pong frame finishes sending. + + @li An error occurs on the stream. + + This function is implemented in terms of one or more calls to the + next layer's `write_some` functions. + + The WebSocket protocol allows pong frames to be sent from either + end at any time. It is not necessary to first receive a ping in + order to send a pong. The remote peer may use the receipt of a + pong frame as an indication that the connection is not dead. + + @param payload The payload of the pong message, which may be empty. + + @throws system_error Thrown on failure. + */ + void + pong(ping_data const& payload); + + /** Send a WebSocket pong frame. + + This function is used to synchronously send a pong frame on + the stream. The call blocks until one of the following is true: + + @li The pong frame finishes sending. + + @li An error occurs on the stream. + + This function is implemented in terms of one or more calls to the + next layer's `write_some` functions. + + The WebSocket protocol allows pong frames to be sent from either + end at any time. It is not necessary to first receive a ping in + order to send a pong. The remote peer may use the receipt of a + pong frame as an indication that the connection is not dead. + + @param payload The payload of the pong message, which may be empty. + + @param ec Set to indicate what error occurred, if any. + */ + void + pong(ping_data const& payload, error_code& ec); + + /** Start an asynchronous operation to send a WebSocket pong frame. + + This function is used to asynchronously send a pong frame to + the stream. The function call always returns immediately. The + asynchronous operation will continue until one of the following + is true: + + @li The entire pong frame is sent. + + @li An error occurs on the stream. + + This operation is implemented in terms of one or more calls to the + next layer's `async_write_some` functions, and is known as a + composed operation. The program must ensure that the + stream performs no other writes until this operation completes. + + The WebSocket protocol allows pong frames to be sent from either + end at any time. It is not necessary to first receive a ping in + order to send a pong. The remote peer may use the receipt of a + pong frame as an indication that the connection is not dead. + + @param payload The payload of the pong message, which may be empty. + + @param handler The handler to be called when the read operation + completes. Copies will be made of the handler as required. The + function signature of the handler must be: + @code + void handler( + error_code const& error // Result of operation + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `boost::asio::io_service::post`. + */ + template +#if GENERATING_DOCS + void_or_deduced +#else + typename async_completion< + WriteHandler, void(error_code)>::result_type +#endif + async_pong(ping_data const& payload, WriteHandler&& handler); /** Read a message from the stream. diff --git a/test/websocket/stream.cpp b/test/websocket/stream.cpp index f35e19f4..8c396b2d 100644 --- a/test/websocket/stream.cpp +++ b/test/websocket/stream.cpp @@ -910,6 +910,9 @@ public: } ws.set_option(pong_callback{}); + // send pong + ws.pong(""); + // send auto fragmented message ws.set_option(auto_fragment{true}); ws.set_option(write_buffer_size{8}); @@ -1172,6 +1175,9 @@ public: ws.set_option(pong_callback{}); } + // send pong + ws.async_pong("", do_yield[ec]); + // send auto fragmented message ws.set_option(auto_fragment{true}); ws.set_option(write_buffer_size{8});