2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 // Official repository: https://github.com/boostorg/beast
10 #ifndef BOOST_BEAST_WEBSOCKET_IMPL_PING_HPP
11 #define BOOST_BEAST_WEBSOCKET_IMPL_PING_HPP
13 #include <boost/beast/core/async_base.hpp>
14 #include <boost/beast/core/bind_handler.hpp>
15 #include <boost/beast/core/stream_traits.hpp>
16 #include <boost/beast/core/detail/bind_continuation.hpp>
17 #include <boost/beast/websocket/detail/frame.hpp>
18 #include <boost/beast/websocket/impl/stream_impl.hpp>
19 #include <boost/asio/coroutine.hpp>
20 #include <boost/asio/post.hpp>
21 #include <boost/throw_exception.hpp>
29 This composed operation handles sending ping and pong frames.
30 It only sends the frames it does not make attempts to read
33 template<class NextLayer, bool deflateSupported>
34 template<class Handler>
35 class stream<NextLayer, deflateSupported>::ping_op
36 : public beast::stable_async_base<
37 Handler, beast::executor_type<stream>>
38 , public asio::coroutine
40 boost::weak_ptr<impl_type> wp_;
41 detail::frame_buffer& fb_;
44 static constexpr int id = 3; // for soft_mutex
46 template<class Handler_>
49 boost::shared_ptr<impl_type> const& sp,
51 ping_data const& payload)
52 : stable_async_base<Handler,
53 beast::executor_type<stream>>(
54 std::forward<Handler_>(h),
55 sp->stream().get_executor())
57 , fb_(beast::allocate_stable<
58 detail::frame_buffer>(*this))
60 // Serialize the ping or pong frame
61 sp->template write_ping<
62 flat_static_buffer_base>(fb_, op, payload);
63 (*this)({}, 0, false);
68 std::size_t bytes_transferred = 0,
71 boost::ignore_unused(bytes_transferred);
75 ec = net::error::operation_aborted;
76 return this->complete(cont, ec);
79 BOOST_ASIO_CORO_REENTER(*this)
81 // Acquire the write lock
82 if(! impl.wr_block.try_lock(this))
85 impl.op_ping.emplace(std::move(*this));
86 impl.wr_block.lock(this);
88 net::post(std::move(*this));
89 BOOST_ASSERT(impl.wr_block.is_locked(this));
91 if(impl.check_stop_now(ec))
96 net::async_write(impl.stream(), fb_.data(),
97 beast::detail::bind_continuation(std::move(*this)));
98 if(impl.check_stop_now(ec))
102 impl.wr_block.unlock(this);
103 impl.op_close.maybe_invoke()
104 || impl.op_idle_ping.maybe_invoke()
105 || impl.op_rd.maybe_invoke()
106 || impl.op_wr.maybe_invoke();
107 this->complete(cont, ec);
112 //------------------------------------------------------------------------------
114 // sends the idle ping
115 template<class NextLayer, bool deflateSupported>
116 template<class Executor>
117 class stream<NextLayer, deflateSupported>::idle_ping_op
118 : public asio::coroutine
119 , public boost::empty_value<Executor>
121 boost::weak_ptr<impl_type> wp_;
122 std::unique_ptr<detail::frame_buffer> fb_;
125 static constexpr int id = 4; // for soft_mutex
127 using executor_type = Executor;
130 get_executor() const noexcept
136 boost::shared_ptr<impl_type> const& sp,
138 : boost::empty_value<Executor>(
139 boost::empty_init_t{}, ex)
141 , fb_(new detail::frame_buffer)
143 if(! sp->idle_pinging)
145 // Create the ping frame
146 ping_data payload; // empty for now
147 sp->template write_ping<
148 flat_static_buffer_base>(*fb_,
149 detail::opcode::ping, payload);
151 sp->idle_pinging = true;
156 // if we are already in the middle of sending
157 // an idle ping, don't bother sending another.
163 std::size_t bytes_transferred = 0)
165 boost::ignore_unused(bytes_transferred);
166 auto sp = wp_.lock();
170 BOOST_ASIO_CORO_REENTER(*this)
172 // Acquire the write lock
173 if(! impl.wr_block.try_lock(this))
175 BOOST_ASIO_CORO_YIELD
176 impl.op_idle_ping.emplace(std::move(*this));
177 impl.wr_block.lock(this);
178 BOOST_ASIO_CORO_YIELD
180 this->get_executor(), std::move(*this));
181 BOOST_ASSERT(impl.wr_block.is_locked(this));
183 if(impl.check_stop_now(ec))
187 BOOST_ASIO_CORO_YIELD
188 net::async_write(impl.stream(), fb_->data(),
189 //beast::detail::bind_continuation(std::move(*this)));
191 if(impl.check_stop_now(ec))
195 BOOST_ASSERT(sp->idle_pinging);
196 sp->idle_pinging = false;
197 impl.wr_block.unlock(this);
198 impl.op_close.maybe_invoke()
199 || impl.op_ping.maybe_invoke()
200 || impl.op_rd.maybe_invoke()
201 || impl.op_wr.maybe_invoke();
206 template<class NextLayer, bool deflateSupported>
207 struct stream<NextLayer, deflateSupported>::
210 template<class WriteHandler>
214 boost::shared_ptr<impl_type> const& sp,
218 // If you get an error on the following line it means
219 // that your handler does not meet the documented type
220 // requirements for the handler.
223 beast::detail::is_invocable<WriteHandler,
224 void(error_code)>::value,
225 "WriteHandler type requirements not met");
228 typename std::decay<WriteHandler>::type>(
229 std::forward<WriteHandler>(h),
236 //------------------------------------------------------------------------------
238 template<class NextLayer, bool deflateSupported>
240 stream<NextLayer, deflateSupported>::
241 ping(ping_data const& payload)
246 BOOST_THROW_EXCEPTION(system_error{ec});
249 template<class NextLayer, bool deflateSupported>
251 stream<NextLayer, deflateSupported>::
252 ping(ping_data const& payload, error_code& ec)
254 if(impl_->check_stop_now(ec))
256 detail::frame_buffer fb;
257 impl_->template write_ping<flat_static_buffer_base>(
258 fb, detail::opcode::ping, payload);
259 net::write(impl_->stream(), fb.data(), ec);
260 if(impl_->check_stop_now(ec))
264 template<class NextLayer, bool deflateSupported>
266 stream<NextLayer, deflateSupported>::
267 pong(ping_data const& payload)
272 BOOST_THROW_EXCEPTION(system_error{ec});
275 template<class NextLayer, bool deflateSupported>
277 stream<NextLayer, deflateSupported>::
278 pong(ping_data const& payload, error_code& ec)
280 if(impl_->check_stop_now(ec))
282 detail::frame_buffer fb;
283 impl_->template write_ping<flat_static_buffer_base>(
284 fb, detail::opcode::pong, payload);
285 net::write(impl_->stream(), fb.data(), ec);
286 if(impl_->check_stop_now(ec))
290 template<class NextLayer, bool deflateSupported>
291 template<class WriteHandler>
292 BOOST_BEAST_ASYNC_RESULT1(WriteHandler)
293 stream<NextLayer, deflateSupported>::
294 async_ping(ping_data const& payload, WriteHandler&& handler)
296 static_assert(is_async_stream<next_layer_type>::value,
297 "AsyncStream type requirements not met");
298 return net::async_initiate<
304 detail::opcode::ping,
308 template<class NextLayer, bool deflateSupported>
309 template<class WriteHandler>
310 BOOST_BEAST_ASYNC_RESULT1(WriteHandler)
311 stream<NextLayer, deflateSupported>::
312 async_pong(ping_data const& payload, WriteHandler&& handler)
314 static_assert(is_async_stream<next_layer_type>::value,
315 "AsyncStream type requirements not met");
316 return net::async_initiate<
322 detail::opcode::pong,