diff --git a/CHANGELOG.md b/CHANGELOG.md index 0324ef55..1866596c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Version 71: WebSockets: * Fine tune websocket op asserts +* Refactor websocket composed ops -------------------------------------------------------------------------------- diff --git a/include/beast/websocket/impl/close.ipp b/include/beast/websocket/impl/close.ipp index 807a8e29..1b1c8461 100644 --- a/include/beast/websocket/impl/close.ipp +++ b/include/beast/websocket/impl/close.ipp @@ -29,23 +29,18 @@ 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; + detail::frame_streambuf fb; int state = 0; - data(Handler& handler, stream& ws_, + data(Handler&, stream& ws_, close_reason const& cr_) : ws(ws_) , cr(cr_) { - using boost::asio::asio_handler_is_continuation; - cont = asio_handler_is_continuation(std::addressof(handler)); ws.template write_close< static_buffer>(fb, cr); } @@ -63,19 +58,16 @@ public: : d_(std::forward(h), ws, std::forward(args)...) { - (*this)(error_code{}, false); } void operator()() { - (*this)(error_code{}); + (*this)({}); } void - operator()(error_code ec, std::size_t); - - void - operator()(error_code ec, bool again = true); + operator()(error_code ec, + std::size_t bytes_transferred = 0); friend void* asio_handler_allocate( @@ -98,7 +90,9 @@ public: friend bool asio_handler_is_continuation(close_op* op) { - return op->d_->cont; + using boost::asio::asio_handler_is_continuation; + return asio_handler_is_continuation( + std::addressof(op->d_.handler())); } template @@ -119,85 +113,72 @@ 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) + BOOST_ASSERT(d.ws.wr_block_ == &d); + d.ws.failed_ = true; + goto upcall; + } + switch(d.state) + { + case 0: + if(d.ws.wr_block_) { - case 0: - if(d.ws.wr_block_) - { - // suspend - d.state = 2; - d.ws.wr_op_.emplace(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; - BEAST_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; - } + // suspend d.state = 1; - break; + d.ws.wr_op_.emplace(std::move(*this)); + return; + } + d.ws.wr_block_ = &d; + 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; + } - case 99: + do_write: + // send close frame + BOOST_ASSERT(d.ws.wr_block_ == &d); + d.state = 3; + d.ws.wr_close_ = true; + boost::asio::async_write(d.ws.stream_, + d.fb.data(), std::move(*this)); + return; + + case 1: + BOOST_ASSERT(! d.ws.wr_block_); + d.ws.wr_block_ = &d; + d.state = 2; + // 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 2: + 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; } + goto do_write; + + case 3: + break; } upcall: - if(d.ws.wr_block_ == &d) - d.ws.wr_block_ = nullptr; + BOOST_ASSERT(d.ws.wr_block_ == &d); + d.ws.wr_block_ = nullptr; d.ws.rd_op_.maybe_invoke() || - d.ws.ping_op_.maybe_invoke(); + d.ws.ping_op_.maybe_invoke() || + d.ws.wr_op_.maybe_invoke(); d_.invoke(ec); } @@ -214,7 +195,7 @@ async_close(close_reason const& cr, CloseHandler&& handler) void(error_code)> init{handler}; close_op>{ - init.completion_handler, *this, cr}; + init.completion_handler, *this, cr}({}); return init.result.get(); } @@ -239,6 +220,11 @@ close(close_reason const& cr, error_code& ec) static_assert(is_sync_stream::value, "SyncStream requirements not met"); BOOST_ASSERT(! wr_close_); + if(wr_close_) + { + ec = boost::asio::error::operation_aborted; + return; + } wr_close_ = true; detail::frame_streambuf fb; write_close(fb, cr); diff --git a/include/beast/websocket/impl/ping.ipp b/include/beast/websocket/impl/ping.ipp index 8b4f3d11..1edd2b1d 100644 --- a/include/beast/websocket/impl/ping.ipp +++ b/include/beast/websocket/impl/ping.ipp @@ -32,17 +32,14 @@ class stream::ping_op { struct data : op { - bool cont; stream& ws; detail::frame_streambuf fb; int state = 0; - data(Handler& handler, stream& ws_, + data(Handler&, stream& ws_, detail::opcode op_, ping_data const& payload) : ws(ws_) { - using boost::asio::asio_handler_is_continuation; - cont = asio_handler_is_continuation(std::addressof(handler)); using boost::asio::buffer; using boost::asio::buffer_copy; ws.template write_ping< @@ -62,17 +59,15 @@ public: : d_(std::forward(h), ws, std::forward(args)...) { - (*this)(error_code{}, false); } void operator()() { - (*this)(error_code{}); + (*this)({}); } - void operator()(error_code ec, std::size_t); - - void operator()(error_code ec, bool again = true); + void operator()(error_code ec, + std::size_t bytes_transferred = 0); friend void* asio_handler_allocate( @@ -95,7 +90,9 @@ public: friend bool asio_handler_is_continuation(ping_op* op) { - return op->d_->cont; + using boost::asio::asio_handler_is_continuation; + return asio_handler_is_continuation( + std::addressof(op->d_.handler())); } template @@ -111,85 +108,70 @@ public: template template void -stream::ping_op:: +stream:: +ping_op:: operator()(error_code ec, std::size_t) { auto& d = *d_; if(ec) - d.ws.failed_ = true; - (*this)(ec); -} - -template -template -void -stream:: -ping_op:: -operator()(error_code ec, bool again) -{ - auto& d = *d_; - d.cont = d.cont || again; - if(ec) - goto upcall; - for(;;) { - switch(d.state) + BOOST_ASSERT(d.ws.wr_block_ == &d); + d.ws.failed_ = true; + goto upcall; + } + switch(d.state) + { + case 0: + if(d.ws.wr_block_) { - case 0: - if(d.ws.wr_block_) - { - // suspend - d.state = 2; - d.ws.ping_op_.emplace(std::move(*this)); - return; - } - if(d.ws.failed_ || d.ws.wr_close_) - { - // call handler - d.state = 99; - d.ws.get_io_service().post( - bind_handler(std::move(*this), - boost::asio::error::operation_aborted)); - return; - } - d.ws.wr_block_ = &d; - BEAST_FALLTHROUGH; - - case 1: - // send ping frame - BOOST_ASSERT(d.ws.wr_block_ == &d); - d.state = 99; - 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; - } + // suspend d.state = 1; - break; + d.ws.ping_op_.emplace(std::move(*this)); + return; + } + d.ws.wr_block_ = &d; + 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; + } - case 99: + do_write: + // send ping frame + BOOST_ASSERT(d.ws.wr_block_ == &d); + d.state = 3; + boost::asio::async_write(d.ws.stream_, + d.fb.data(), std::move(*this)); + return; + + case 1: + BOOST_ASSERT(! d.ws.wr_block_); + d.ws.wr_block_ = &d; + d.state = 2; + // 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 2: + 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; } + goto do_write; + + case 3: + break; } upcall: BOOST_ASSERT(d.ws.wr_block_ == &d); @@ -213,7 +195,7 @@ async_ping(ping_data const& payload, WriteHandler&& handler) ping_op>{ init.completion_handler, *this, - detail::opcode::ping, payload}; + detail::opcode::ping, payload}({}); return init.result.get(); } @@ -231,7 +213,7 @@ async_pong(ping_data const& payload, WriteHandler&& handler) ping_op>{ init.completion_handler, *this, - detail::opcode::pong, payload}; + detail::opcode::pong, payload}({}); return init.result.get(); }