// // Copyright (c) 2013-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) // #ifndef BEAST_WEBSOCKET_IMPL_CLOSE_IPP #define BEAST_WEBSOCKET_IMPL_CLOSE_IPP #include #include #include #include #include namespace beast { namespace websocket { //------------------------------------------------------------------------------ // send the close message and wait for the response // template template class stream::close_op { using fb_type = detail::frame_streambuf; struct data : op { bool cont; stream& ws; close_reason cr; fb_type fb; int state = 0; data(Handler& handler, stream& ws_, close_reason const& cr_) : cont(beast_asio_helpers:: is_continuation(handler)) , ws(ws_) , cr(cr_) { ws.template write_close< static_streambuf>(fb, cr); } }; handler_ptr d_; public: close_op(close_op&&) = default; close_op(close_op const&) = default; template close_op(DeducedHandler&& h, stream& ws, Args&&... args) : d_(std::forward(h), ws, std::forward(args)...) { (*this)(error_code{}, false); } void operator()() { (*this)(error_code{}); } void operator()(error_code ec, std::size_t); void operator()(error_code ec, bool again = true); friend void* asio_handler_allocate( std::size_t size, close_op* op) { return beast_asio_helpers:: allocate(size, op->d_.handler()); } friend void asio_handler_deallocate( void* p, std::size_t size, close_op* op) { return beast_asio_helpers:: deallocate(p, size, op->d_.handler()); } friend bool asio_handler_is_continuation(close_op* op) { return op->d_->cont; } template friend void asio_handler_invoke(Function&& f, close_op* op) { return beast_asio_helpers:: invoke(f, op->d_.handler()); } }; template template void stream::close_op:: operator()(error_code ec, std::size_t) { auto& d = *d_; if(ec) d.ws.failed_ = true; (*this)(ec); } template template void stream::close_op:: operator()(error_code ec, bool again) { auto& d = *d_; d.cont = d.cont || again; if(ec) goto upcall; for(;;) { switch(d.state) { case 0: if(d.ws.wr_block_) { // suspend d.state = 2; d.ws.wr_op_.template emplace< close_op>(std::move(*this)); return; } if(d.ws.failed_ || d.ws.wr_close_) { // call handler d.ws.get_io_service().post( bind_handler(std::move(*this), boost::asio::error::operation_aborted)); return; } d.ws.wr_block_ = &d; // [[fallthrough]] case 1: // send close frame BOOST_ASSERT(d.ws.wr_block_ == &d); d.state = 99; d.ws.wr_close_ = true; boost::asio::async_write(d.ws.stream_, d.fb.data(), std::move(*this)); return; case 2: BOOST_ASSERT(! d.ws.wr_block_); d.ws.wr_block_ = &d; d.state = 3; // The current context is safe but might not be // the same as the one for this operation (since // we are being called from a write operation). // Call post to make sure we are invoked the same // way as the final handler for this operation. d.ws.get_io_service().post( bind_handler(std::move(*this), ec)); return; case 3: BOOST_ASSERT(d.ws.wr_block_ == &d); if(d.ws.failed_ || d.ws.wr_close_) { // call handler ec = boost::asio::error::operation_aborted; goto upcall; } d.state = 1; break; case 99: goto upcall; } } upcall: if(d.ws.wr_block_ == &d) d.ws.wr_block_ = nullptr; d.ws.rd_op_.maybe_invoke() || d.ws.ping_op_.maybe_invoke(); d_.invoke(ec); } template template typename async_completion< CloseHandler, void(error_code)>::result_type stream:: async_close(close_reason const& cr, CloseHandler&& handler) { static_assert(is_AsyncStream::value, "AsyncStream requirements not met"); beast::async_completion< CloseHandler, void(error_code) > completion{handler}; close_op{ completion.handler, *this, cr}; return completion.result.get(); } template void stream:: close(close_reason const& cr) { static_assert(is_SyncStream::value, "SyncStream requirements not met"); error_code ec; close(cr, ec); if(ec) throw system_error{ec}; } template void stream:: close(close_reason const& cr, error_code& ec) { static_assert(is_SyncStream::value, "SyncStream requirements not met"); BOOST_ASSERT(! wr_close_); wr_close_ = true; detail::frame_streambuf fb; write_close(fb, cr); boost::asio::write(stream_, fb.data(), ec); failed_ = ec != 0; } //------------------------------------------------------------------------------ } // websocket } // beast #endif