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_CLOSE_HPP
11 #define BOOST_BEAST_WEBSOCKET_IMPL_CLOSE_HPP
13 #include <boost/beast/websocket/teardown.hpp>
14 #include <boost/beast/websocket/detail/mask.hpp>
15 #include <boost/beast/websocket/impl/stream_impl.hpp>
16 #include <boost/beast/core/async_base.hpp>
17 #include <boost/beast/core/flat_static_buffer.hpp>
18 #include <boost/beast/core/stream_traits.hpp>
19 #include <boost/beast/core/detail/bind_continuation.hpp>
20 #include <boost/asio/coroutine.hpp>
21 #include <boost/asio/post.hpp>
22 #include <boost/throw_exception.hpp>
29 /* Close the WebSocket Connection
31 This composed operation sends the close frame if it hasn't already
32 been sent, then reads and discards frames until receiving a close
33 frame. Finally it invokes the teardown operation to shut down the
34 underlying connection.
36 template<class NextLayer, bool deflateSupported>
37 template<class Handler>
38 class stream<NextLayer, deflateSupported>::close_op
39 : public beast::stable_async_base<
40 Handler, beast::executor_type<stream>>
41 , public asio::coroutine
43 boost::weak_ptr<impl_type> wp_;
45 detail::frame_buffer& fb_;
48 static constexpr int id = 5; // for soft_mutex
50 template<class Handler_>
53 boost::shared_ptr<impl_type> const& sp,
54 close_reason const& cr)
55 : stable_async_base<Handler,
56 beast::executor_type<stream>>(
57 std::forward<Handler_>(h),
58 sp->stream().get_executor())
60 , fb_(beast::allocate_stable<
61 detail::frame_buffer>(*this))
63 // Serialize the close frame
64 sp->template write_close<
65 flat_static_buffer_base>(fb_, cr);
66 (*this)({}, 0, false);
72 std::size_t bytes_transferred = 0,
75 using beast::detail::clamp;
79 ec = net::error::operation_aborted;
80 return this->complete(cont, ec);
83 BOOST_ASIO_CORO_REENTER(*this)
85 // Acquire the write lock
86 if(! impl.wr_block.try_lock(this))
89 impl.op_close.emplace(std::move(*this));
90 impl.wr_block.lock(this);
92 net::post(std::move(*this));
93 BOOST_ASSERT(impl.wr_block.is_locked(this));
95 if(impl.check_stop_now(ec))
98 // Can't call close twice
99 // TODO return a custom error code
100 BOOST_ASSERT(! impl.wr_close);
103 impl.wr_close = true;
104 impl.change_status(status::closing);
105 impl.update_timer(this->get_executor());
106 BOOST_ASIO_CORO_YIELD
107 net::async_write(impl.stream(), fb_.data(),
108 beast::detail::bind_continuation(std::move(*this)));
109 if(impl.check_stop_now(ec))
114 // This happens when the read_op gets a close frame
115 // at the same time close_op is sending the close frame.
116 // The read_op will be suspended on the write block.
120 // Acquire the read lock
121 if(! impl.rd_block.try_lock(this))
123 BOOST_ASIO_CORO_YIELD
124 impl.op_r_close.emplace(std::move(*this));
125 impl.rd_block.lock(this);
126 BOOST_ASIO_CORO_YIELD
127 net::post(std::move(*this));
128 BOOST_ASSERT(impl.rd_block.is_locked(this));
129 if(impl.check_stop_now(ec))
131 BOOST_ASSERT(! impl.rd_close);
134 // Read until a receiving a close frame
135 // TODO There should be a timeout on this
136 if(impl.rd_remain > 0)
141 while(! impl.parse_fh(
142 impl.rd_fh, impl.rd_buf, ev_))
146 BOOST_ASIO_CORO_YIELD
147 impl.stream().async_read_some(
148 impl.rd_buf.prepare(read_size(
149 impl.rd_buf, impl.rd_buf.max_size())),
150 beast::detail::bind_continuation(std::move(*this)));
151 impl.rd_buf.commit(bytes_transferred);
152 if(impl.check_stop_now(ec))
155 if(detail::is_control(impl.rd_fh.op))
157 // Discard ping or pong frame
158 if(impl.rd_fh.op != detail::opcode::close)
160 impl.rd_buf.consume(clamp(impl.rd_fh.len));
164 // Process close frame
165 // TODO Should we invoke the control callback?
166 BOOST_ASSERT(! impl.rd_close);
167 impl.rd_close = true;
168 auto const mb = buffers_prefix(
169 clamp(impl.rd_fh.len),
171 if(impl.rd_fh.len > 0 && impl.rd_fh.mask)
172 detail::mask_inplace(mb, impl.rd_key);
173 detail::read_close(impl.cr, mb, ev_);
176 impl.rd_buf.consume(clamp(impl.rd_fh.len));
181 // Discard message frame
182 while(impl.rd_buf.size() < impl.rd_remain)
184 impl.rd_remain -= impl.rd_buf.size();
185 impl.rd_buf.consume(impl.rd_buf.size());
186 BOOST_ASIO_CORO_YIELD
187 impl.stream().async_read_some(
188 impl.rd_buf.prepare(read_size(
189 impl.rd_buf, impl.rd_buf.max_size())),
190 beast::detail::bind_continuation(std::move(*this)));
191 impl.rd_buf.commit(bytes_transferred);
192 if(impl.check_stop_now(ec))
195 BOOST_ASSERT(impl.rd_buf.size() >= impl.rd_remain);
196 impl.rd_buf.consume(clamp(impl.rd_remain));
202 BOOST_ASSERT(impl.wr_block.is_locked(this));
203 using beast::websocket::async_teardown;
204 BOOST_ASIO_CORO_YIELD
205 async_teardown(impl.role, impl.stream(),
206 beast::detail::bind_continuation(std::move(*this)));
207 BOOST_ASSERT(impl.wr_block.is_locked(this));
208 if(ec == net::error::eof)
211 // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
217 impl.change_status(status::failed);
219 impl.change_status(status::closed);
223 impl.wr_block.unlock(this);
224 impl.rd_block.try_unlock(this)
225 && impl.op_r_rd.maybe_invoke();
226 impl.op_rd.maybe_invoke()
227 || impl.op_idle_ping.maybe_invoke()
228 || impl.op_ping.maybe_invoke()
229 || impl.op_wr.maybe_invoke();
230 this->complete(cont, ec);
235 template<class NextLayer, bool deflateSupported>
236 struct stream<NextLayer, deflateSupported>::
239 template<class CloseHandler>
243 boost::shared_ptr<impl_type> const& sp,
244 close_reason const& cr)
246 // If you get an error on the following line it means
247 // that your handler does not meet the documented type
248 // requirements for the handler.
251 beast::detail::is_invocable<CloseHandler,
252 void(error_code)>::value,
253 "CloseHandler type requirements not met");
256 typename std::decay<CloseHandler>::type>(
257 std::forward<CloseHandler>(h),
263 //------------------------------------------------------------------------------
265 template<class NextLayer, bool deflateSupported>
267 stream<NextLayer, deflateSupported>::
268 close(close_reason const& cr)
270 static_assert(is_sync_stream<next_layer_type>::value,
271 "SyncStream type requirements not met");
275 BOOST_THROW_EXCEPTION(system_error{ec});
278 template<class NextLayer, bool deflateSupported>
280 stream<NextLayer, deflateSupported>::
281 close(close_reason const& cr, error_code& ec)
283 static_assert(is_sync_stream<next_layer_type>::value,
284 "SyncStream type requirements not met");
285 using beast::detail::clamp;
288 if(impl.check_stop_now(ec))
290 BOOST_ASSERT(! impl.rd_close);
292 // Can't call close twice
293 // TODO return a custom error code
294 BOOST_ASSERT(! impl.wr_close);
298 impl.wr_close = true;
299 impl.change_status(status::closing);
300 detail::frame_buffer fb;
301 impl.template write_close<flat_static_buffer_base>(fb, cr);
302 net::write(impl.stream(), fb.data(), ec);
303 if(impl.check_stop_now(ec))
307 // Read until a receiving a close frame
309 if(impl.rd_remain > 0)
314 while(! impl.parse_fh(
315 impl.rd_fh, impl.rd_buf, ev))
319 // Protocol violation
320 return do_fail(close_code::none, ev, ec);
322 impl.rd_buf.commit(impl.stream().read_some(
323 impl.rd_buf.prepare(read_size(
324 impl.rd_buf, impl.rd_buf.max_size())), ec));
325 if(impl.check_stop_now(ec))
329 if(detail::is_control(impl.rd_fh.op))
331 // Discard ping/pong frame
332 if(impl.rd_fh.op != detail::opcode::close)
334 impl.rd_buf.consume(clamp(impl.rd_fh.len));
338 // Handle close frame
339 // TODO Should we invoke the control callback?
340 BOOST_ASSERT(! impl.rd_close);
341 impl.rd_close = true;
342 auto const mb = buffers_prefix(
343 clamp(impl.rd_fh.len),
345 if(impl.rd_fh.len > 0 && impl.rd_fh.mask)
346 detail::mask_inplace(mb, impl.rd_key);
347 detail::read_close(impl.cr, mb, ev);
350 // Protocol violation
351 return do_fail(close_code::none, ev, ec);
353 impl.rd_buf.consume(clamp(impl.rd_fh.len));
358 // Discard message frame
359 while(impl.rd_buf.size() < impl.rd_remain)
361 impl.rd_remain -= impl.rd_buf.size();
362 impl.rd_buf.consume(impl.rd_buf.size());
364 impl.stream().read_some(
368 impl.rd_buf.max_size())),
370 if(impl.check_stop_now(ec))
374 impl.rd_buf.size() >= impl.rd_remain);
375 impl.rd_buf.consume(clamp(impl.rd_remain));
378 // _Close the WebSocket Connection_
379 do_fail(close_code::none, error::closed, ec);
380 if(ec == error::closed)
384 template<class NextLayer, bool deflateSupported>
385 template<class CloseHandler>
386 BOOST_BEAST_ASYNC_RESULT1(CloseHandler)
387 stream<NextLayer, deflateSupported>::
388 async_close(close_reason const& cr, CloseHandler&& handler)
390 static_assert(is_async_stream<next_layer_type>::value,
391 "AsyncStream type requirements not met");
392 return net::async_initiate<