// // Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Official repository: https://github.com/boostorg/beast // #ifndef BOOST_BEAST_WEBSOCKET_IMPL_PING_IPP #define BOOST_BEAST_WEBSOCKET_IMPL_PING_IPP #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace beast { namespace websocket { /* This composed operation handles sending ping and pong frames. It only sends the frames it does not make attempts to read any frame data. */ template template class stream::ping_op : public boost::asio::coroutine { struct state { stream& ws; detail::frame_buffer fb; token tok; state( Handler&, stream& ws_, detail::opcode op, ping_data const& payload) : ws(ws_) , tok(ws.t_.unique()) { // Serialize the control frame ws.template write_ping< flat_static_buffer_base>( fb, op, payload); } }; handler_ptr d_; public: ping_op(ping_op&&) = default; ping_op(ping_op const&) = default; template ping_op( DeducedHandler&& h, stream& ws, detail::opcode op, ping_data const& payload) : d_(std::forward(h), ws, op, payload) { } void operator()( error_code ec = {}, std::size_t bytes_transferred = 0); friend void* asio_handler_allocate( std::size_t size, ping_op* op) { using boost::asio::asio_handler_allocate; return asio_handler_allocate( size, std::addressof(op->d_.handler())); } friend void asio_handler_deallocate( void* p, std::size_t size, ping_op* op) { using boost::asio::asio_handler_deallocate; asio_handler_deallocate( p, size, std::addressof(op->d_.handler())); } friend bool asio_handler_is_continuation(ping_op* op) { using boost::asio::asio_handler_is_continuation; return asio_handler_is_continuation( std::addressof(op->d_.handler())); } template friend void asio_handler_invoke(Function&& f, ping_op* op) { using boost::asio::asio_handler_invoke; asio_handler_invoke( f, std::addressof(op->d_.handler())); } }; template template void stream:: ping_op:: operator()(error_code ec, std::size_t) { auto& d = *d_; BOOST_ASIO_CORO_REENTER(*this) { // Maybe suspend if(! d.ws.wr_block_) { // Acquire the write block d.ws.wr_block_ = d.tok; // Make sure the stream is open if(d.ws.failed_) { BOOST_ASIO_CORO_YIELD d.ws.get_io_service().post( bind_handler(std::move(*this), boost::asio::error::operation_aborted)); goto upcall; } } else { // Suspend BOOST_ASSERT(d.ws.wr_block_ != d.tok); BOOST_ASIO_CORO_YIELD d.ws.ping_op_.emplace(std::move(*this)); // Acquire the write block BOOST_ASSERT(! d.ws.wr_block_); d.ws.wr_block_ = d.tok; // Resume BOOST_ASIO_CORO_YIELD d.ws.get_io_service().post(std::move(*this)); BOOST_ASSERT(d.ws.wr_block_ == d.tok); // Make sure the stream is open if(d.ws.failed_) { ec = boost::asio::error::operation_aborted; goto upcall; } } // Send ping frame BOOST_ASIO_CORO_YIELD boost::asio::async_write(d.ws.stream_, d.fb.data(), std::move(*this)); if(ec) d.ws.failed_ = true; upcall: BOOST_ASSERT(d.ws.wr_block_ == d.tok); d.ws.wr_block_.reset(); d.ws.close_op_.maybe_invoke() || d.ws.rd_op_.maybe_invoke() || d.ws.wr_op_.maybe_invoke(); d_.invoke(ec); } } //------------------------------------------------------------------------------ template void stream:: ping(ping_data const& payload) { error_code ec; ping(payload, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template void stream:: ping(ping_data const& payload, error_code& ec) { // Make sure the stream is open if(failed_) { ec = boost::asio::error::operation_aborted; return; } detail::frame_buffer fb; write_ping( fb, detail::opcode::ping, payload); boost::asio::write(stream_, fb.data(), ec); } template void stream:: pong(ping_data const& payload) { error_code ec; pong(payload, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template void stream:: pong(ping_data const& payload, error_code& ec) { // Make sure the stream is open if(failed_) { ec = boost::asio::error::operation_aborted; return; } detail::frame_buffer fb; write_ping( fb, detail::opcode::pong, payload); boost::asio::write(stream_, fb.data(), ec); } template template async_return_type< WriteHandler, void(error_code)> stream:: async_ping(ping_data const& payload, WriteHandler&& handler) { static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); async_completion init{handler}; ping_op>{ init.completion_handler, *this, detail::opcode::ping, payload}(); return init.result.get(); } template template async_return_type< WriteHandler, void(error_code)> stream:: async_pong(ping_data const& payload, WriteHandler&& handler) { static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); async_completion init{handler}; ping_op>{ init.completion_handler, *this, detail::opcode::pong, payload}(); return init.result.get(); } } // websocket } // beast } // boost #endif