// // Copyright (c) 2016-2019 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_HPP #define BOOST_BEAST_WEBSOCKET_IMPL_PING_HPP #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 beast::stable_async_base< Handler, beast::executor_type> , public asio::coroutine { boost::weak_ptr wp_; detail::frame_buffer& fb_; public: static constexpr int id = 3; // for soft_mutex template ping_op( Handler_&& h, boost::shared_ptr const& sp, detail::opcode op, ping_data const& payload) : stable_async_base>( std::forward(h), sp->stream().get_executor()) , wp_(sp) , fb_(beast::allocate_stable< detail::frame_buffer>(*this)) { // Serialize the ping or pong frame sp->template write_ping< flat_static_buffer_base>(fb_, op, payload); (*this)({}, 0, false); } void operator()( error_code ec = {}, std::size_t bytes_transferred = 0, bool cont = true) { boost::ignore_unused(bytes_transferred); auto sp = wp_.lock(); if(! sp) { ec = net::error::operation_aborted; return this->complete(cont, ec); } auto& impl = *sp; BOOST_ASIO_CORO_REENTER(*this) { // Acquire the write lock if(! impl.wr_block.try_lock(this)) { BOOST_ASIO_CORO_YIELD impl.op_ping.emplace(std::move(*this)); impl.wr_block.lock(this); BOOST_ASIO_CORO_YIELD net::post(std::move(*this)); BOOST_ASSERT(impl.wr_block.is_locked(this)); } if(impl.check_stop_now(ec)) goto upcall; // Send ping frame BOOST_ASIO_CORO_YIELD net::async_write(impl.stream(), fb_.data(), beast::detail::bind_continuation(std::move(*this))); if(impl.check_stop_now(ec)) goto upcall; upcall: impl.wr_block.unlock(this); impl.op_close.maybe_invoke() || impl.op_idle_ping.maybe_invoke() || impl.op_rd.maybe_invoke() || impl.op_wr.maybe_invoke(); this->complete(cont, ec); } } }; //------------------------------------------------------------------------------ // sends the idle ping template template class stream::idle_ping_op : public asio::coroutine , public boost::empty_value { boost::weak_ptr wp_; std::unique_ptr fb_; public: static constexpr int id = 4; // for soft_mutex using executor_type = Executor; executor_type get_executor() const noexcept { return this->get(); } idle_ping_op( boost::shared_ptr const& sp, Executor const& ex) : boost::empty_value( boost::empty_init_t{}, ex) , wp_(sp) , fb_(new detail::frame_buffer) { if(! sp->idle_pinging) { // Create the ping frame ping_data payload; // empty for now sp->template write_ping< flat_static_buffer_base>(*fb_, detail::opcode::ping, payload); sp->idle_pinging = true; (*this)({}, 0); } else { // if we are already in the middle of sending // an idle ping, don't bother sending another. } } void operator()( error_code ec = {}, std::size_t bytes_transferred = 0) { boost::ignore_unused(bytes_transferred); auto sp = wp_.lock(); if(! sp) return; auto& impl = *sp; BOOST_ASIO_CORO_REENTER(*this) { // Acquire the write lock if(! impl.wr_block.try_lock(this)) { BOOST_ASIO_CORO_YIELD impl.op_idle_ping.emplace(std::move(*this)); impl.wr_block.lock(this); BOOST_ASIO_CORO_YIELD net::post( this->get_executor(), std::move(*this)); BOOST_ASSERT(impl.wr_block.is_locked(this)); } if(impl.check_stop_now(ec)) goto upcall; // Send ping frame BOOST_ASIO_CORO_YIELD net::async_write(impl.stream(), fb_->data(), //beast::detail::bind_continuation(std::move(*this))); std::move(*this)); if(impl.check_stop_now(ec)) goto upcall; upcall: BOOST_ASSERT(sp->idle_pinging); sp->idle_pinging = false; impl.wr_block.unlock(this); impl.op_close.maybe_invoke() || impl.op_ping.maybe_invoke() || impl.op_rd.maybe_invoke() || impl.op_wr.maybe_invoke(); } } }; template struct stream:: run_ping_op { template void operator()( WriteHandler&& h, boost::shared_ptr const& sp, detail::opcode op, ping_data const& p) { // If you get an error on the following line it means // that your handler does not meet the documented type // requirements for the handler. static_assert( beast::detail::is_invocable::value, "WriteHandler type requirements not met"); ping_op< typename std::decay::type>( std::forward(h), sp, op, p); } }; //------------------------------------------------------------------------------ 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) { if(impl_->check_stop_now(ec)) return; detail::frame_buffer fb; impl_->template write_ping( fb, detail::opcode::ping, payload); net::write(impl_->stream(), fb.data(), ec); if(impl_->check_stop_now(ec)) return; } 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) { if(impl_->check_stop_now(ec)) return; detail::frame_buffer fb; impl_->template write_ping( fb, detail::opcode::pong, payload); net::write(impl_->stream(), fb.data(), ec); if(impl_->check_stop_now(ec)) return; } template template BOOST_BEAST_ASYNC_RESULT1(WriteHandler) stream:: async_ping(ping_data const& payload, WriteHandler&& handler) { static_assert(is_async_stream::value, "AsyncStream type requirements not met"); return net::async_initiate< WriteHandler, void(error_code)>( run_ping_op{}, handler, impl_, detail::opcode::ping, payload); } template template BOOST_BEAST_ASYNC_RESULT1(WriteHandler) stream:: async_pong(ping_data const& payload, WriteHandler&& handler) { static_assert(is_async_stream::value, "AsyncStream type requirements not met"); return net::async_initiate< WriteHandler, void(error_code)>( run_ping_op{}, handler, impl_, detail::opcode::pong, payload); } } // websocket } // beast } // boost #endif