From 9a8e22950ff5aa0d92d70860f00b84bc042d5324 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sat, 19 Jan 2019 07:24:00 -0800 Subject: [PATCH] websocket stream uses shared_ptr --- CHANGELOG.md | 1 + doc/qbk/03_core/1_refresher.qbk | 2 +- example/cppcon2018/websocket_session.cpp | 1 + include/boost/beast/websocket/detail/prng.hpp | 17 - .../beast/websocket/detail/stream_base.hpp | 146 +- include/boost/beast/websocket/error.hpp | 2 +- .../websocket/impl/{accept.ipp => accept.hpp} | 138 +- .../websocket/impl/{close.ipp => close.hpp} | 209 +-- .../websocket/impl/{error.ipp => error.hpp} | 18 +- .../impl/{handshake.ipp => handshake.hpp} | 20 +- .../websocket/impl/{ping.ipp => ping.hpp} | 40 +- include/boost/beast/websocket/impl/read.hpp | 1234 ++++++++++++++++ include/boost/beast/websocket/impl/read.ipp | 1274 ----------------- .../impl/{rfc6455.ipp => rfc6455.hpp} | 4 +- .../beast/websocket/impl/{ssl.ipp => ssl.hpp} | 4 +- .../websocket/impl/{stream.ipp => stream.hpp} | 337 +++-- .../beast/websocket/impl/stream_impl.hpp | 199 +++ .../impl/{teardown.ipp => teardown.hpp} | 4 +- .../websocket/impl/{write.ipp => write.hpp} | 379 ++--- include/boost/beast/websocket/rfc6455.hpp | 2 +- include/boost/beast/websocket/ssl.hpp | 2 +- include/boost/beast/websocket/stream.hpp | 254 +--- include/boost/beast/websocket/teardown.hpp | 2 +- test/beast/websocket/CMakeLists.txt | 2 + test/beast/websocket/Jamfile | 1 + test/beast/websocket/_detail_stream_base.cpp | 11 + test/beast/websocket/close.cpp | 14 +- test/beast/websocket/ping.cpp | 16 +- test/beast/websocket/read2.cpp | 16 +- test/beast/websocket/stream.cpp | 8 +- test/beast/websocket/write.cpp | 16 +- 31 files changed, 2243 insertions(+), 2130 deletions(-) rename include/boost/beast/websocket/impl/{accept.ipp => accept.hpp} (88%) rename include/boost/beast/websocket/impl/{close.ipp => close.hpp} (57%) rename include/boost/beast/websocket/impl/{error.ipp => error.hpp} (96%) rename include/boost/beast/websocket/impl/{handshake.ipp => handshake.hpp} (96%) rename include/boost/beast/websocket/impl/{ping.ipp => ping.hpp} (85%) create mode 100644 include/boost/beast/websocket/impl/read.hpp delete mode 100644 include/boost/beast/websocket/impl/read.ipp rename include/boost/beast/websocket/impl/{rfc6455.ipp => rfc6455.hpp} (90%) rename include/boost/beast/websocket/impl/{ssl.ipp => ssl.hpp} (92%) rename include/boost/beast/websocket/impl/{stream.ipp => stream.hpp} (73%) create mode 100644 include/boost/beast/websocket/impl/stream_impl.hpp rename include/boost/beast/websocket/impl/{teardown.ipp => teardown.hpp} (98%) rename include/boost/beast/websocket/impl/{write.ipp => write.hpp} (67%) create mode 100644 test/beast/websocket/_detail_stream_base.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index c3dc721a..585207f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ Version 206 * Clear error codes idiomatically +* websocket stream uses shared_ptr -------------------------------------------------------------------------------- diff --git a/doc/qbk/03_core/1_refresher.qbk b/doc/qbk/03_core/1_refresher.qbk index e5868a42..ce5efd9e 100644 --- a/doc/qbk/03_core/1_refresher.qbk +++ b/doc/qbk/03_core/1_refresher.qbk @@ -82,7 +82,7 @@ these memory regions as type-safe pointer/size pairs, as shown below: pointer-to-byte. The operating system doesn't care about this, but if a user wants to send and receive an array of some other type, presenting it as an array of bytes which supports bitwise operations is unnecessary. - Custom buffer types also permit networking implmentations to provide + Custom buffer types also enable networking implementations to provide targeted features such as [@boost:/doc/html/boost_asio/overview/core/buffers.html#boost_asio.overview.core.buffers.buffer_debugging ['buffer debugging]] without changing the more general vocabulary types. diff --git a/example/cppcon2018/websocket_session.cpp b/example/cppcon2018/websocket_session.cpp index e929c057..2f87ff96 100644 --- a/example/cppcon2018/websocket_session.cpp +++ b/example/cppcon2018/websocket_session.cpp @@ -8,6 +8,7 @@ // #include "websocket_session.hpp" +#include websocket_session:: websocket_session( diff --git a/include/boost/beast/websocket/detail/prng.hpp b/include/boost/beast/websocket/detail/prng.hpp index e34ebb7a..0a316e59 100644 --- a/include/boost/beast/websocket/detail/prng.hpp +++ b/include/boost/beast/websocket/detail/prng.hpp @@ -111,23 +111,6 @@ BOOST_BEAST_DECL prng::ref make_prng(bool secure); -//------------------------------------------------------------------------------ - -struct stream_prng -{ - bool secure_prng_ = true; - - std::uint32_t - create_mask() - { - auto g = make_prng(secure_prng_); - for(;;) - if(auto key = g()) - return key; - } - -}; - } // detail } // websocket } // beast diff --git a/include/boost/beast/websocket/detail/stream_base.hpp b/include/boost/beast/websocket/detail/stream_base.hpp index cc2f800d..93091df5 100644 --- a/include/boost/beast/websocket/detail/stream_base.hpp +++ b/include/boost/beast/websocket/detail/stream_base.hpp @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -24,14 +27,20 @@ #include #include #include +#include namespace boost { namespace beast { namespace websocket { namespace detail { +//------------------------------------------------------------------------------ + template -struct stream_base : stream_prng +struct impl_base; + +template<> +struct impl_base { // State information for the permessage-deflate extension struct pmd_type @@ -67,6 +76,9 @@ struct stream_base : stream_prng return ! rsv1; // pmd not negotiated } + // Compress a buffer sequence + // Returns: `true` if more calls are needed + // template bool deflate( @@ -74,19 +86,105 @@ struct stream_base : stream_prng buffers_suffix& cb, bool fin, std::size_t& total_in, - error_code& ec); + error_code& ec) + { + using net::buffer; + BOOST_ASSERT(out.size() >= 6); + auto& zo = this->pmd_->zo; + zlib::z_params zs; + zs.avail_in = 0; + zs.next_in = nullptr; + zs.avail_out = out.size(); + zs.next_out = out.data(); + for(auto in : beast::buffers_range_ref(cb)) + { + zs.avail_in = in.size(); + if(zs.avail_in == 0) + continue; + zs.next_in = in.data(); + zo.write(zs, zlib::Flush::none, ec); + if(ec) + { + if(ec != zlib::error::need_buffers) + return false; + BOOST_ASSERT(zs.avail_out == 0); + BOOST_ASSERT(zs.total_out == out.size()); + ec = {}; + break; + } + if(zs.avail_out == 0) + { + BOOST_ASSERT(zs.total_out == out.size()); + break; + } + BOOST_ASSERT(zs.avail_in == 0); + } + total_in = zs.total_in; + cb.consume(zs.total_in); + if(zs.avail_out > 0 && fin) + { + auto const remain = net::buffer_size(cb); + if(remain == 0) + { + // Inspired by Mark Adler + // https://github.com/madler/zlib/issues/149 + // + // VFALCO We could do this flush twice depending + // on how much space is in the output. + zo.write(zs, zlib::Flush::block, ec); + BOOST_ASSERT(! ec || ec == zlib::error::need_buffers); + if(ec == zlib::error::need_buffers) + ec = {}; + if(ec) + return false; + if(zs.avail_out >= 6) + { + zo.write(zs, zlib::Flush::full, ec); + BOOST_ASSERT(! ec); + // remove flush marker + zs.total_out -= 4; + out = buffer(out.data(), zs.total_out); + return false; + } + } + } + ec = {}; + out = buffer(out.data(), zs.total_out); + return true; + } void - do_context_takeover_write(role_type role); + do_context_takeover_write(role_type role) + { + if((role == role_type::client && + this->pmd_config_.client_no_context_takeover) || + (role == role_type::server && + this->pmd_config_.server_no_context_takeover)) + { + this->pmd_->zo.reset(); + } + } void inflate( zlib::z_params& zs, zlib::Flush flush, - error_code& ec); + error_code& ec) + { + pmd_->zi.write(zs, flush, ec); + } void - do_context_takeover_read(role_type role); + do_context_takeover_read(role_type role) + { + if((role == role_type::client && + pmd_config_.server_no_context_takeover) || + (role == role_type::server && + pmd_config_.client_no_context_takeover)) + { + pmd_->zi.clear(); + } + } template void @@ -95,14 +193,15 @@ struct stream_base : stream_prng http::request> const& req) { - detail::pmd_offer offer; - detail::pmd_offer unused; - detail::pmd_read(offer, req); - detail::pmd_negotiate(res, unused, offer, pmd_opts_); + pmd_offer offer; + pmd_offer unused; + pmd_read(offer, req); + pmd_negotiate(res, unused, offer, pmd_opts_); } void - on_response_pmd(http::response const& res) + on_response_pmd( + http::response const& res) { detail::pmd_offer offer; detail::pmd_read(offer, res); @@ -177,8 +276,7 @@ struct stream_base : stream_prng pmd_config_.accept) { detail::pmd_normalize(pmd_config_); - pmd_.reset(new typename - detail::stream_base::pmd_type); + pmd_.reset(::new pmd_type); if(role == role_type::client) { pmd_->zi.reset( @@ -248,8 +346,10 @@ struct stream_base : stream_prng } }; +//------------------------------------------------------------------------------ + template<> -struct stream_base : stream_prng +struct impl_base { // These stubs are for avoiding linking in the zlib // code when permessage-deflate is not enabled. @@ -362,7 +462,7 @@ struct stream_base : stream_prng std::size_t initial_size, bool rd_done, std::uint64_t rd_remain, - detail::frame_header const& rd_fh) const + frame_header const& rd_fh) const { using beast::detail::clamp; std::size_t result; @@ -389,6 +489,24 @@ struct stream_base : stream_prng } }; +//------------------------------------------------------------------------------ + +struct stream_base +{ +protected: + bool secure_prng_ = true; + + std::uint32_t + create_mask() + { + auto g = make_prng(secure_prng_); + for(;;) + if(auto key = g()) + return key; + } + +}; + } // detail } // websocket } // beast diff --git a/include/boost/beast/websocket/error.hpp b/include/boost/beast/websocket/error.hpp index d4e51971..fa025ec0 100644 --- a/include/boost/beast/websocket/error.hpp +++ b/include/boost/beast/websocket/error.hpp @@ -249,6 +249,6 @@ enum class condition } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/websocket/impl/accept.ipp b/include/boost/beast/websocket/impl/accept.hpp similarity index 88% rename from include/boost/beast/websocket/impl/accept.ipp rename to include/boost/beast/websocket/impl/accept.hpp index 2f8fada6..727179c5 100644 --- a/include/boost/beast/websocket/impl/accept.ipp +++ b/include/boost/beast/websocket/impl/accept.hpp @@ -32,7 +32,8 @@ namespace boost { namespace beast { namespace websocket { -// Respond to an upgrade HTTP request +/** Respond to an HTTP request +*/ template template class stream::response_op @@ -40,39 +41,26 @@ class stream::response_op Handler, beast::detail::get_executor_type> , public net::coroutine { - struct data - { - stream& ws; - error_code result; - response_type res; - - template - data( - stream& ws_, - http::request> const& req, - Decorator const& decorator) - : ws(ws_) - , res(ws_.build_response(req, decorator, result)) - { - } - }; - - data& d_; + stream& ws_; + error_code result_; // must come before res_ + response_type& res_; public: template< class Handler_, - class... Args> + class Body, class Allocator, + class Decorator> response_op( Handler_&& h, stream& ws, - Args&&... args) + http::request> const& req, + Decorator const& decorator) : stable_async_op_base< Handler, beast::detail::get_executor_type>( std::forward(h), ws.get_executor()) - , d_(beast::allocate_stable( - *this, ws, std::forward(args)...)) + , ws_(ws) + , res_(beast::allocate_stable(*this, + ws.build_response(req, decorator, result_))) { } @@ -81,19 +69,18 @@ public: std::size_t bytes_transferred = 0) { boost::ignore_unused(bytes_transferred); - BOOST_ASIO_CORO_REENTER(*this) { // Send response BOOST_ASIO_CORO_YIELD - http::async_write(d_.ws.next_layer(), - d_.res, std::move(*this)); + http::async_write( + ws_.next_layer(), res_, std::move(*this)); if(! ec) - ec = d_.result; + ec = result_; if(! ec) { - d_.ws.do_pmd_config(d_.res); - d_.ws.open(role_type::server); + ws_.impl_->do_pmd_config(res_); + ws_.impl_->open(role_type::server); } this->invoke(ec); } @@ -111,36 +98,23 @@ class stream::accept_op Handler, beast::detail::get_executor_type> , public net::coroutine { - struct data - { - stream& ws; - Decorator decorator; - http::request_parser p; - - data( - stream& ws_, - Decorator const& decorator_) - : ws(ws_) - , decorator(decorator_) - { - } - }; - - data& d_; + stream& ws_; + http::request_parser& p_; + Decorator d_; public: - template< - class Handler_, - class... Args> + template accept_op( Handler_&& h, stream& ws, - Args&&... args) + Decorator const& decorator) : stable_async_op_base< Handler, beast::detail::get_executor_type>( std::forward(h), ws.get_executor()) - , d_(beast::allocate_stable( - *this, ws, std::forward(args)...)) + , ws_(ws) + , p_(beast::allocate_stable< + http::request_parser>(*this)) + , d_(decorator) { } @@ -151,19 +125,19 @@ public: using net::buffer_size; error_code ec; auto const mb = beast::detail::dynamic_buffer_prepare( - d_.ws.rd_buf_, buffer_size(buffers), ec, + ws_.impl_->rd_buf, buffer_size(buffers), ec, error::buffer_overflow); if(ec) return (*this)(ec); - d_.ws.rd_buf_.commit(buffer_copy(*mb, buffers)); + ws_.impl_->rd_buf.commit(buffer_copy(*mb, buffers)); (*this)(ec); } void operator()( error_code ec = {}, - std::size_t bytes_used = 0) + std::size_t bytes_transferred = 0) { - boost::ignore_unused(bytes_used); + boost::ignore_unused(bytes_transferred); BOOST_ASIO_CORO_REENTER(*this) { @@ -171,15 +145,15 @@ public: { BOOST_ASIO_CORO_YIELD net::post( - d_.ws.get_executor(), + ws_.get_executor(), beast::bind_front_handler(std::move(*this), ec)); } else { BOOST_ASIO_CORO_YIELD http::async_read( - d_.ws.next_layer(), d_.ws.rd_buf_, - d_.p, std::move(*this)); + ws_.next_layer(), ws_.impl_->rd_buf, + p_, std::move(*this)); if(ec == http::error::end_of_stream) ec = error::closed; if(! ec) @@ -187,9 +161,9 @@ public: // Arguments from our state must be // moved to the stack before releasing // the handler. - auto& ws = d_.ws; - auto const req = d_.p.release(); - auto const decorator = d_.decorator; + auto& ws = ws_; + auto const req = p_.release(); + auto const decorator = d_; #if 1 return response_op{ this->release_handler(), @@ -247,7 +221,7 @@ accept(error_code& ec) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); - reset(); + impl_->reset(); do_accept(&default_decorate_res, ec); } @@ -262,7 +236,7 @@ accept_ex(ResponseDecorator const& decorator, error_code& ec) static_assert(detail::is_response_decorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); - reset(); + impl_->reset(); do_accept(decorator, ec); } @@ -324,13 +298,13 @@ accept( "ConstBufferSequence requirements not met"); using net::buffer_copy; using net::buffer_size; - reset(); + impl_->reset(); auto const mb = beast::detail::dynamic_buffer_prepare( - rd_buf_, buffer_size(buffers), ec, + impl_->rd_buf, buffer_size(buffers), ec, error::buffer_overflow); if(ec) return; - rd_buf_.commit(buffer_copy(*mb, buffers)); + impl_->rd_buf.commit(buffer_copy(*mb, buffers)); do_accept(&default_decorate_res, ec); } @@ -356,13 +330,13 @@ accept_ex( "ConstBufferSequence requirements not met"); using net::buffer_copy; using net::buffer_size; - reset(); + impl_->reset(); auto const mb = beast::detail::dynamic_buffer_prepare( - rd_buf_, buffer_size(buffers), ec, + impl_->rd_buf, buffer_size(buffers), ec, error::buffer_overflow); if(ec) return; - rd_buf_.commit(buffer_copy(*mb, buffers)); + impl_->rd_buf.commit(buffer_copy(*mb, buffers)); do_accept(decorator, ec); } @@ -415,7 +389,7 @@ accept( { static_assert(is_sync_stream::value, "SyncStream requirements not met"); - reset(); + impl_->reset(); do_accept(req, &default_decorate_res, ec); } @@ -436,7 +410,7 @@ accept_ex( static_assert(detail::is_response_decorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); - reset(); + impl_->reset(); do_accept(req, decorator, ec); } @@ -455,7 +429,7 @@ async_accept( "AsyncStream requirements not met"); BOOST_BEAST_HANDLER_INIT( AcceptHandler, void(error_code)); - reset(); + impl_->reset(); accept_op< decltype(&default_decorate_res), BOOST_ASIO_HANDLER_TYPE( @@ -484,7 +458,7 @@ async_accept_ex( "ResponseDecorator requirements not met"); BOOST_BEAST_HANDLER_INIT( AcceptHandler, void(error_code)); - reset(); + impl_->reset(); accept_op< ResponseDecorator, BOOST_ASIO_HANDLER_TYPE( @@ -515,7 +489,7 @@ async_accept( "ConstBufferSequence requirements not met"); BOOST_BEAST_HANDLER_INIT( AcceptHandler, void(error_code)); - reset(); + impl_->reset(); accept_op< decltype(&default_decorate_res), BOOST_ASIO_HANDLER_TYPE( @@ -551,7 +525,7 @@ async_accept_ex( "ResponseDecorator requirements not met"); BOOST_BEAST_HANDLER_INIT( AcceptHandler, void(error_code)); - reset(); + impl_->reset(); accept_op< ResponseDecorator, BOOST_ASIO_HANDLER_TYPE( @@ -577,7 +551,7 @@ async_accept( "AsyncStream requirements not met"); BOOST_BEAST_HANDLER_INIT( AcceptHandler, void(error_code)); - reset(); + impl_->reset(); using net::asio_handler_is_continuation; response_op< BOOST_ASIO_HANDLER_TYPE( @@ -609,7 +583,7 @@ async_accept_ex( "ResponseDecorator requirements not met"); BOOST_BEAST_HANDLER_INIT( AcceptHandler, void(error_code)); - reset(); + impl_->reset(); using net::asio_handler_is_continuation; response_op< BOOST_ASIO_HANDLER_TYPE( @@ -632,7 +606,7 @@ do_accept( error_code& ec) { http::request_parser p; - http::read(next_layer(), rd_buf_, p, ec); + http::read(next_layer(), impl_->rd_buf, p, ec); if(ec == http::error::end_of_stream) ec = error::closed; if(ec) @@ -653,7 +627,7 @@ do_accept( { error_code result; auto const res = build_response(req, decorator, result); - http::write(stream_, res, ec); + http::write(impl_->stream, res, ec); if(ec) return; ec = result; @@ -663,8 +637,8 @@ do_accept( // teardown if Connection: close. return; } - this->do_pmd_config(res); - open(role_type::server); + impl_->do_pmd_config(res); + impl_->open(role_type::server); } } // websocket diff --git a/include/boost/beast/websocket/impl/close.ipp b/include/boost/beast/websocket/impl/close.hpp similarity index 57% rename from include/boost/beast/websocket/impl/close.ipp rename to include/boost/beast/websocket/impl/close.hpp index 8e4d927d..e5161cdc 100644 --- a/include/boost/beast/websocket/impl/close.ipp +++ b/include/boost/beast/websocket/impl/close.hpp @@ -7,10 +7,11 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_WEBSOCKET_IMPL_CLOSE_IPP -#define BOOST_BEAST_WEBSOCKET_IMPL_CLOSE_IPP +#ifndef BOOST_BEAST_WEBSOCKET_IMPL_CLOSE_HPP +#define BOOST_BEAST_WEBSOCKET_IMPL_CLOSE_HPP #include +#include #include #include #include @@ -86,42 +87,42 @@ public: BOOST_ASIO_CORO_REENTER(*this) { // Attempt to acquire write block - if(! d_.ws.wr_block_.try_lock(this)) + if(! d_.ws.impl_->wr_block.try_lock(this)) { // Suspend BOOST_ASIO_CORO_YIELD - d_.ws.paused_close_.emplace(std::move(*this)); + d_.ws.impl_->paused_close.emplace(std::move(*this)); // Acquire the write block - d_.ws.wr_block_.lock(this); + d_.ws.impl_->wr_block.lock(this); // Resume BOOST_ASIO_CORO_YIELD net::post( d_.ws.get_executor(), std::move(*this)); - BOOST_ASSERT(d_.ws.wr_block_.is_locked(this)); + BOOST_ASSERT(d_.ws.impl_->wr_block.is_locked(this)); } // Make sure the stream is open - if(! d_.ws.check_open(ec)) + if(! d_.ws.impl_->check_open(ec)) goto upcall; // Can't call close twice - BOOST_ASSERT(! d_.ws.wr_close_); + BOOST_ASSERT(! d_.ws.impl_->wr_close); // Change status to closing - BOOST_ASSERT(d_.ws.status_ == status::open); - d_.ws.status_ = status::closing; + BOOST_ASSERT(d_.ws.impl_->status_ == status::open); + d_.ws.impl_->status_ = status::closing; // Send close frame - d_.ws.wr_close_ = true; + d_.ws.impl_->wr_close = true; BOOST_ASIO_CORO_YIELD - net::async_write(d_.ws.stream_, + net::async_write(d_.ws.impl_->stream, d_.fb.data(), std::move(*this)); - if(! d_.ws.check_ok(ec)) + if(! d_.ws.impl_->check_ok(ec)) goto upcall; - if(d_.ws.rd_close_) + if(d_.ws.impl_->rd_close) { // This happens when the read_op gets a close frame // at the same time close_op is sending the close frame. @@ -130,100 +131,100 @@ public: } // Maybe suspend - if(! d_.ws.rd_block_.try_lock(this)) + if(! d_.ws.impl_->rd_block.try_lock(this)) { // Suspend BOOST_ASIO_CORO_YIELD - d_.ws.paused_r_close_.emplace(std::move(*this)); + d_.ws.impl_->paused_r_close.emplace(std::move(*this)); // Acquire the read block - d_.ws.rd_block_.lock(this); + d_.ws.impl_->rd_block.lock(this); // Resume BOOST_ASIO_CORO_YIELD net::post( d_.ws.get_executor(), std::move(*this)); - BOOST_ASSERT(d_.ws.rd_block_.is_locked(this)); + BOOST_ASSERT(d_.ws.impl_->rd_block.is_locked(this)); // Make sure the stream is open - BOOST_ASSERT(d_.ws.status_ != status::open); - BOOST_ASSERT(d_.ws.status_ != status::closed); - if( d_.ws.status_ == status::failed) + BOOST_ASSERT(d_.ws.impl_->status_ != status::open); + BOOST_ASSERT(d_.ws.impl_->status_ != status::closed); + if( d_.ws.impl_->status_ == status::failed) goto upcall; - BOOST_ASSERT(! d_.ws.rd_close_); + BOOST_ASSERT(! d_.ws.impl_->rd_close); } // Drain - if(d_.ws.rd_remain_ > 0) + if(d_.ws.impl_->rd_remain > 0) goto read_payload; for(;;) { // Read frame header while(! d_.ws.parse_fh( - d_.ws.rd_fh_, d_.ws.rd_buf_, d_.ev)) + d_.ws.impl_->rd_fh, d_.ws.impl_->rd_buf, d_.ev)) { if(d_.ev) goto teardown; BOOST_ASIO_CORO_YIELD - d_.ws.stream_.async_read_some( - d_.ws.rd_buf_.prepare(read_size(d_.ws.rd_buf_, - d_.ws.rd_buf_.max_size())), + d_.ws.impl_->stream.async_read_some( + d_.ws.impl_->rd_buf.prepare(read_size(d_.ws.impl_->rd_buf, + d_.ws.impl_->rd_buf.max_size())), std::move(*this)); - if(! d_.ws.check_ok(ec)) + if(! d_.ws.impl_->check_ok(ec)) goto upcall; - d_.ws.rd_buf_.commit(bytes_transferred); + d_.ws.impl_->rd_buf.commit(bytes_transferred); } - if(detail::is_control(d_.ws.rd_fh_.op)) + if(detail::is_control(d_.ws.impl_->rd_fh.op)) { // Process control frame - if(d_.ws.rd_fh_.op == detail::opcode::close) + if(d_.ws.impl_->rd_fh.op == detail::opcode::close) { - BOOST_ASSERT(! d_.ws.rd_close_); - d_.ws.rd_close_ = true; + BOOST_ASSERT(! d_.ws.impl_->rd_close); + d_.ws.impl_->rd_close = true; auto const mb = buffers_prefix( - clamp(d_.ws.rd_fh_.len), - d_.ws.rd_buf_.data()); - if(d_.ws.rd_fh_.len > 0 && d_.ws.rd_fh_.mask) - detail::mask_inplace(mb, d_.ws.rd_key_); - detail::read_close(d_.ws.cr_, mb, d_.ev); + clamp(d_.ws.impl_->rd_fh.len), + d_.ws.impl_->rd_buf.data()); + if(d_.ws.impl_->rd_fh.len > 0 && d_.ws.impl_->rd_fh.mask) + detail::mask_inplace(mb, d_.ws.impl_->rd_key); + detail::read_close(d_.ws.impl_->cr, mb, d_.ev); if(d_.ev) goto teardown; - d_.ws.rd_buf_.consume(clamp(d_.ws.rd_fh_.len)); + d_.ws.impl_->rd_buf.consume(clamp(d_.ws.impl_->rd_fh.len)); goto teardown; } - d_.ws.rd_buf_.consume(clamp(d_.ws.rd_fh_.len)); + d_.ws.impl_->rd_buf.consume(clamp(d_.ws.impl_->rd_fh.len)); } else { read_payload: - while(d_.ws.rd_buf_.size() < d_.ws.rd_remain_) + while(d_.ws.impl_->rd_buf.size() < d_.ws.impl_->rd_remain) { - d_.ws.rd_remain_ -= d_.ws.rd_buf_.size(); - d_.ws.rd_buf_.consume(d_.ws.rd_buf_.size()); + d_.ws.impl_->rd_remain -= d_.ws.impl_->rd_buf.size(); + d_.ws.impl_->rd_buf.consume(d_.ws.impl_->rd_buf.size()); BOOST_ASIO_CORO_YIELD - d_.ws.stream_.async_read_some( - d_.ws.rd_buf_.prepare(read_size(d_.ws.rd_buf_, - d_.ws.rd_buf_.max_size())), + d_.ws.impl_->stream.async_read_some( + d_.ws.impl_->rd_buf.prepare(read_size(d_.ws.impl_->rd_buf, + d_.ws.impl_->rd_buf.max_size())), std::move(*this)); - if(! d_.ws.check_ok(ec)) + if(! d_.ws.impl_->check_ok(ec)) goto upcall; - d_.ws.rd_buf_.commit(bytes_transferred); + d_.ws.impl_->rd_buf.commit(bytes_transferred); } - BOOST_ASSERT(d_.ws.rd_buf_.size() >= d_.ws.rd_remain_); - d_.ws.rd_buf_.consume(clamp(d_.ws.rd_remain_)); - d_.ws.rd_remain_ = 0; + BOOST_ASSERT(d_.ws.impl_->rd_buf.size() >= d_.ws.impl_->rd_remain); + d_.ws.impl_->rd_buf.consume(clamp(d_.ws.impl_->rd_remain)); + d_.ws.impl_->rd_remain = 0; } } teardown: // Teardown - BOOST_ASSERT(d_.ws.wr_block_.is_locked(this)); + BOOST_ASSERT(d_.ws.impl_->wr_block.is_locked(this)); using beast::websocket::async_teardown; BOOST_ASIO_CORO_YIELD - async_teardown(d_.ws.role_, - d_.ws.stream_, std::move(*this)); - BOOST_ASSERT(d_.ws.wr_block_.is_locked(this)); + async_teardown(d_.ws.impl_->role, + d_.ws.impl_->stream, std::move(*this)); + BOOST_ASSERT(d_.ws.impl_->wr_block.is_locked(this)); if(ec == net::error::eof) { // Rationale: @@ -233,19 +234,19 @@ public: if(! ec) ec = d_.ev; if(ec) - d_.ws.status_ = status::failed; + d_.ws.impl_->status_ = status::failed; else - d_.ws.status_ = status::closed; - d_.ws.close(); + d_.ws.impl_->status_ = status::closed; + d_.ws.impl_->close(); upcall: - BOOST_ASSERT(d_.ws.wr_block_.is_locked(this)); - d_.ws.wr_block_.unlock(this); - if(d_.ws.rd_block_.try_unlock(this)) - d_.ws.paused_r_rd_.maybe_invoke(); - d_.ws.paused_rd_.maybe_invoke() || - d_.ws.paused_ping_.maybe_invoke() || - d_.ws.paused_wr_.maybe_invoke(); + BOOST_ASSERT(d_.ws.impl_->wr_block.is_locked(this)); + d_.ws.impl_->wr_block.unlock(this); + if(d_.ws.impl_->rd_block.try_unlock(this)) + d_.ws.impl_->paused_r_rd.maybe_invoke(); + d_.ws.impl_->paused_rd.maybe_invoke() || + d_.ws.impl_->paused_ping.maybe_invoke() || + d_.ws.impl_->paused_wr.maybe_invoke(); if(! d_.cont) { BOOST_ASIO_CORO_YIELD @@ -281,90 +282,92 @@ close(close_reason const& cr, error_code& ec) static_assert(is_sync_stream::value, "SyncStream requirements not met"); using beast::detail::clamp; - ec.assign(0, ec.category()); + ec = {}; // Make sure the stream is open - if(! check_open(ec)) + if(! impl_->check_open(ec)) return; // If rd_close_ is set then we already sent a close - BOOST_ASSERT(! rd_close_); - BOOST_ASSERT(! wr_close_); - wr_close_ = true; + BOOST_ASSERT(! impl_->rd_close); + BOOST_ASSERT(! impl_->wr_close); + impl_->wr_close = true; { detail::frame_buffer fb; write_close(fb, cr); - net::write(stream_, fb.data(), ec); + net::write(impl_->stream, fb.data(), ec); } - if(! check_ok(ec)) + if(! impl_->check_ok(ec)) return; - status_ = status::closing; + impl_->status_ = status::closing; error_code result; // Drain the connection - if(rd_remain_ > 0) + if(impl_->rd_remain > 0) goto read_payload; for(;;) { // Read frame header - while(! parse_fh(rd_fh_, rd_buf_, result)) + while(! parse_fh( + impl_->rd_fh, impl_->rd_buf, result)) { if(result) return do_fail( close_code::none, result, ec); auto const bytes_transferred = - stream_.read_some( - rd_buf_.prepare(read_size(rd_buf_, - rd_buf_.max_size())), ec); - if(! check_ok(ec)) + impl_->stream.read_some( + impl_->rd_buf.prepare(read_size(impl_->rd_buf, + impl_->rd_buf.max_size())), ec); + if(! impl_->check_ok(ec)) return; - rd_buf_.commit(bytes_transferred); + impl_->rd_buf.commit(bytes_transferred); } - if(detail::is_control(rd_fh_.op)) + if(detail::is_control(impl_->rd_fh.op)) { // Process control frame - if(rd_fh_.op == detail::opcode::close) + if(impl_->rd_fh.op == detail::opcode::close) { - BOOST_ASSERT(! rd_close_); - rd_close_ = true; + BOOST_ASSERT(! impl_->rd_close); + impl_->rd_close = true; auto const mb = buffers_prefix( - clamp(rd_fh_.len), - rd_buf_.data()); - if(rd_fh_.len > 0 && rd_fh_.mask) - detail::mask_inplace(mb, rd_key_); - detail::read_close(cr_, mb, result); + clamp(impl_->rd_fh.len), + impl_->rd_buf.data()); + if(impl_->rd_fh.len > 0 && impl_->rd_fh.mask) + detail::mask_inplace(mb, impl_->rd_key); + detail::read_close(impl_->cr, mb, result); if(result) { // Protocol violation return do_fail( close_code::none, result, ec); } - rd_buf_.consume(clamp(rd_fh_.len)); + impl_->rd_buf.consume(clamp(impl_->rd_fh.len)); break; } - rd_buf_.consume(clamp(rd_fh_.len)); + impl_->rd_buf.consume(clamp(impl_->rd_fh.len)); } else { read_payload: - while(rd_buf_.size() < rd_remain_) + while(impl_->rd_buf.size() < impl_->rd_remain) { - rd_remain_ -= rd_buf_.size(); - rd_buf_.consume(rd_buf_.size()); + impl_->rd_remain -= impl_->rd_buf.size(); + impl_->rd_buf.consume(impl_->rd_buf.size()); auto const bytes_transferred = - stream_.read_some( - rd_buf_.prepare(read_size(rd_buf_, - rd_buf_.max_size())), ec); - if(! check_ok(ec)) + impl_->stream.read_some( + impl_->rd_buf.prepare(read_size(impl_->rd_buf, + impl_->rd_buf.max_size())), ec); + if(! impl_->check_ok(ec)) return; - rd_buf_.commit(bytes_transferred); + impl_->rd_buf.commit(bytes_transferred); } - BOOST_ASSERT(rd_buf_.size() >= rd_remain_); - rd_buf_.consume(clamp(rd_remain_)); - rd_remain_ = 0; + BOOST_ASSERT( + impl_->rd_buf.size() >= impl_->rd_remain); + impl_->rd_buf.consume(clamp(impl_->rd_remain)); + impl_->rd_remain = 0; } } // _Close the WebSocket Connection_ do_fail(close_code::none, error::closed, ec); if(ec == error::closed) - ec.assign(0, ec.category()); + ec = {}; } template diff --git a/include/boost/beast/websocket/impl/error.ipp b/include/boost/beast/websocket/impl/error.hpp similarity index 96% rename from include/boost/beast/websocket/impl/error.ipp rename to include/boost/beast/websocket/impl/error.hpp index 56a20e25..edccdb0d 100644 --- a/include/boost/beast/websocket/impl/error.ipp +++ b/include/boost/beast/websocket/impl/error.hpp @@ -7,15 +7,15 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_WEBSOCKET_IMPL_ERROR_IPP -#define BOOST_BEAST_WEBSOCKET_IMPL_ERROR_IPP +#ifndef BOOST_BEAST_WEBSOCKET_IMPL_ERROR_HPP +#define BOOST_BEAST_WEBSOCKET_IMPL_ERROR_HPP namespace boost { namespace beast { namespace websocket { namespace detail { -inline +BOOST_BEAST_DECL const char* error_codes:: name() const noexcept @@ -23,7 +23,7 @@ name() const noexcept return "boost.beast.websocket"; } -inline +BOOST_BEAST_DECL std::string error_codes:: message(int ev) const @@ -67,7 +67,7 @@ message(int ev) const } } -inline +BOOST_BEAST_DECL error_condition error_codes:: default_error_condition(int ev) const noexcept @@ -114,7 +114,7 @@ default_error_condition(int ev) const noexcept } } -inline +BOOST_BEAST_DECL const char* error_conditions:: name() const noexcept @@ -122,7 +122,7 @@ name() const noexcept return "boost.beast.websocket"; } -inline +BOOST_BEAST_DECL std::string error_conditions:: message(int cv) const @@ -137,7 +137,7 @@ message(int cv) const } // detail -inline +BOOST_BEAST_DECL error_code make_error_code(error e) { @@ -146,7 +146,7 @@ make_error_code(error e) std::underlying_type::type>(e), cat}; } -inline +BOOST_BEAST_DECL error_condition make_error_condition(condition c) { diff --git a/include/boost/beast/websocket/impl/handshake.ipp b/include/boost/beast/websocket/impl/handshake.hpp similarity index 96% rename from include/boost/beast/websocket/impl/handshake.ipp rename to include/boost/beast/websocket/impl/handshake.hpp index 884e1107..68e74d34 100644 --- a/include/boost/beast/websocket/impl/handshake.ipp +++ b/include/boost/beast/websocket/impl/handshake.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP -#define BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP +#ifndef BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_HPP +#define BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_HPP #include #include @@ -58,7 +58,7 @@ class stream::handshake_op , req(ws.build_request(key, host, target, decorator)) { - ws.reset(); + ws.impl_->reset(); } }; @@ -89,9 +89,9 @@ public: BOOST_ASIO_CORO_REENTER(*this) { // Send HTTP Upgrade - d_.ws.do_pmd_config(d_.req); + d_.ws.impl_->do_pmd_config(d_.req); BOOST_ASIO_CORO_YIELD - http::async_write(d_.ws.stream_, + http::async_write(d_.ws.impl_->stream, d_.req, std::move(*this)); if(ec) goto upcall; @@ -105,7 +105,7 @@ public: // Read HTTP response BOOST_ASIO_CORO_YIELD http::async_read(d_.ws.next_layer(), - d_.ws.rd_buf_, d_.res, + d_.ws.impl_->rd_buf, d_.res, std::move(*this)); if(ec) goto upcall; @@ -354,17 +354,17 @@ do_handshake( error_code& ec) { response_type res; - reset(); + impl_->reset(); detail::sec_ws_key_type key; { auto const req = build_request( key, host, target, decorator); - this->do_pmd_config(req); - http::write(stream_, req, ec); + this->impl_->do_pmd_config(req); + http::write(impl_->stream, req, ec); } if(ec) return; - http::read(next_layer(), rd_buf_, res, ec); + http::read(next_layer(), impl_->rd_buf, res, ec); if(ec) return; on_response(res, key, ec); diff --git a/include/boost/beast/websocket/impl/ping.ipp b/include/boost/beast/websocket/impl/ping.hpp similarity index 85% rename from include/boost/beast/websocket/impl/ping.ipp rename to include/boost/beast/websocket/impl/ping.hpp index 1a9a5f64..1092ffec 100644 --- a/include/boost/beast/websocket/impl/ping.ipp +++ b/include/boost/beast/websocket/impl/ping.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_WEBSOCKET_IMPL_PING_IPP -#define BOOST_BEAST_WEBSOCKET_IMPL_PING_IPP +#ifndef BOOST_BEAST_WEBSOCKET_IMPL_PING_HPP +#define BOOST_BEAST_WEBSOCKET_IMPL_PING_HPP #include #include @@ -83,10 +83,10 @@ public: BOOST_ASIO_CORO_REENTER(*this) { // Maybe suspend - if(d_.ws.wr_block_.try_lock(this)) + if(d_.ws.impl_->wr_block.try_lock(this)) { // Make sure the stream is open - if(! d_.ws.check_open(ec)) + if(! d_.ws.impl_->check_open(ec)) { BOOST_ASIO_CORO_YIELD net::post( @@ -99,34 +99,34 @@ public: { // Suspend BOOST_ASIO_CORO_YIELD - d_.ws.paused_ping_.emplace(std::move(*this)); + d_.ws.impl_->paused_ping.emplace(std::move(*this)); // Acquire the write block - d_.ws.wr_block_.lock(this); + d_.ws.impl_->wr_block.lock(this); // Resume BOOST_ASIO_CORO_YIELD net::post( d_.ws.get_executor(), std::move(*this)); - BOOST_ASSERT(d_.ws.wr_block_.is_locked(this)); + BOOST_ASSERT(d_.ws.impl_->wr_block.is_locked(this)); // Make sure the stream is open - if(! d_.ws.check_open(ec)) + if(! d_.ws.impl_->check_open(ec)) goto upcall; } // Send ping frame BOOST_ASIO_CORO_YIELD - net::async_write(d_.ws.stream_, + net::async_write(d_.ws.impl_->stream, d_.fb.data(), std::move(*this)); - if(! d_.ws.check_ok(ec)) + if(! d_.ws.impl_->check_ok(ec)) goto upcall; upcall: - d_.ws.wr_block_.unlock(this); - d_.ws.paused_close_.maybe_invoke() || - d_.ws.paused_rd_.maybe_invoke() || - d_.ws.paused_wr_.maybe_invoke(); + d_.ws.impl_->wr_block.unlock(this); + d_.ws.impl_->paused_close.maybe_invoke() || + d_.ws.impl_->paused_rd.maybe_invoke() || + d_.ws.impl_->paused_wr.maybe_invoke(); this->invoke(ec); } } @@ -151,13 +151,13 @@ stream:: ping(ping_data const& payload, error_code& ec) { // Make sure the stream is open - if(! check_open(ec)) + if(! impl_->check_open(ec)) return; detail::frame_buffer fb; write_ping( fb, detail::opcode::ping, payload); - net::write(stream_, fb.data(), ec); - if(! check_ok(ec)) + net::write(impl_->stream, fb.data(), ec); + if(! impl_->check_ok(ec)) return; } @@ -178,13 +178,13 @@ stream:: pong(ping_data const& payload, error_code& ec) { // Make sure the stream is open - if(! check_open(ec)) + if(! impl_->check_open(ec)) return; detail::frame_buffer fb; write_ping( fb, detail::opcode::pong, payload); - net::write(stream_, fb.data(), ec); - if(! check_ok(ec)) + net::write(impl_->stream, fb.data(), ec); + if(! impl_->check_ok(ec)) return; } diff --git a/include/boost/beast/websocket/impl/read.hpp b/include/boost/beast/websocket/impl/read.hpp new file mode 100644 index 00000000..f6742826 --- /dev/null +++ b/include/boost/beast/websocket/impl/read.hpp @@ -0,0 +1,1234 @@ +// +// Copyright (c) 2016-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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_WEBSOCKET_IMPL_READ_HPP +#define BOOST_BEAST_WEBSOCKET_IMPL_READ_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace websocket { + +/* Read some message frame data. + + Also reads and handles control frames. +*/ +template +template< + class MutableBufferSequence, + class Handler> +class stream::read_some_op + : public beast::async_op_base< + Handler, beast::detail::get_executor_type> + , public net::coroutine +{ + stream& ws_; + MutableBufferSequence bs_; + buffers_suffix cb_; + std::size_t bytes_written_ = 0; + error_code result_; + close_code code_; + bool did_read_ = false; + bool cont_ = false; + +public: + static constexpr int id = 1; // for soft_mutex + + template + read_some_op( + Handler_&& h, + stream& ws, + MutableBufferSequence const& bs) + : async_op_base< + Handler, beast::detail::get_executor_type>( + std::forward(h), ws.get_executor()) + , ws_(ws) + , bs_(bs) + , cb_(bs) + , code_(close_code::none) + { + } + + void operator()( + error_code ec = {}, + std::size_t bytes_transferred = 0, + bool cont = true) + { + using beast::detail::clamp; + using net::buffer; + using net::buffer_size; + auto& impl = *ws_.impl_; + cont_ = cont; + BOOST_ASIO_CORO_REENTER(*this) + { + // Maybe suspend + do_maybe_suspend: + if(impl.rd_block.try_lock(this)) + { + // Make sure the stream is not closed + if( impl.status_ == status::closed || + impl.status_ == status::failed) + { + ec = net::error::operation_aborted; + goto upcall; + } + } + else + { + do_suspend: + // Suspend + BOOST_ASIO_CORO_YIELD + impl.paused_r_rd.emplace(std::move(*this)); + + // Acquire the read block + impl.rd_block.lock(this); + + // Resume + BOOST_ASIO_CORO_YIELD + net::post( + ws_.get_executor(), std::move(*this)); + BOOST_ASSERT(impl.rd_block.is_locked(this)); + + // The only way to get read blocked is if + // a `close_op` wrote a close frame + BOOST_ASSERT(impl.wr_close); + BOOST_ASSERT(impl.status_ != status::open); + ec = net::error::operation_aborted; + goto upcall; + } + + // if status_ == status::closing, we want to suspend + // the read operation until the close completes, + // then finish the read with operation_aborted. + + loop: + BOOST_ASSERT(impl.rd_block.is_locked(this)); + // See if we need to read a frame header. This + // condition is structured to give the decompressor + // a chance to emit the final empty deflate block + // + if(impl.rd_remain == 0 && + (! impl.rd_fh.fin || impl.rd_done)) + { + // Read frame header + while(! ws_.parse_fh( + impl.rd_fh, impl.rd_buf, result_)) + { + if(result_) + { + // _Fail the WebSocket Connection_ + if(result_ == error::message_too_big) + code_ = close_code::too_big; + else + code_ = close_code::protocol_error; + goto close; + } + BOOST_ASSERT(impl.rd_block.is_locked(this)); + BOOST_ASIO_CORO_YIELD + impl.stream.async_read_some( + impl.rd_buf.prepare(read_size( + impl.rd_buf, impl.rd_buf.max_size())), + std::move(*this)); + BOOST_ASSERT(impl.rd_block.is_locked(this)); + if(! impl.check_ok(ec)) + goto upcall; + impl.rd_buf.commit(bytes_transferred); + + // Allow a close operation + // to acquire the read block + impl.rd_block.unlock(this); + if( impl.paused_r_close.maybe_invoke()) + { + // Suspend + BOOST_ASSERT(impl.rd_block.is_locked()); + goto do_suspend; + } + // Acquire read block + impl.rd_block.lock(this); + } + // Immediately apply the mask to the portion + // of the buffer holding payload data. + if(impl.rd_fh.len > 0 && impl.rd_fh.mask) + detail::mask_inplace(buffers_prefix( + clamp(impl.rd_fh.len), + impl.rd_buf.data()), + impl.rd_key); + if(detail::is_control(impl.rd_fh.op)) + { + // Clear this otherwise the next + // frame will be considered final. + impl.rd_fh.fin = false; + + // Handle ping frame + if(impl.rd_fh.op == detail::opcode::ping) + { + if(impl.ctrl_cb) + { + if(! cont_) + { + BOOST_ASIO_CORO_YIELD + net::post( + ws_.get_executor(), + std::move(*this)); + BOOST_ASSERT(cont_); + } + } + { + auto const b = buffers_prefix( + clamp(impl.rd_fh.len), + impl.rd_buf.data()); + auto const len = buffer_size(b); + BOOST_ASSERT(len == impl.rd_fh.len); + ping_data payload; + detail::read_ping(payload, b); + impl.rd_buf.consume(len); + // Ignore ping when closing + if(impl.status_ == status::closing) + goto loop; + if(impl.ctrl_cb) + impl.ctrl_cb( + frame_type::ping, payload); + impl.rd_fb.clear(); + ws_.template write_ping< + flat_static_buffer_base>(impl.rd_fb, + detail::opcode::pong, payload); + } + + // Allow a close operation + // to acquire the read block + impl.rd_block.unlock(this); + impl.paused_r_close.maybe_invoke(); + + // Maybe suspend + if(! impl.wr_block.try_lock(this)) + { + // Suspend + BOOST_ASIO_CORO_YIELD + impl.paused_rd.emplace(std::move(*this)); + + // Acquire the write block + impl.wr_block.lock(this); + + // Resume + BOOST_ASIO_CORO_YIELD + net::post( + ws_.get_executor(), std::move(*this)); + BOOST_ASSERT(impl.wr_block.is_locked(this)); + + // Make sure the stream is open + if(! impl.check_open(ec)) + goto upcall; + } + + // Send pong + BOOST_ASSERT(impl.wr_block.is_locked(this)); + BOOST_ASIO_CORO_YIELD + net::async_write(impl.stream, + impl.rd_fb.data(), std::move(*this)); + BOOST_ASSERT(impl.wr_block.is_locked(this)); + if(! impl.check_ok(ec)) + goto upcall; + impl.wr_block.unlock(this); + impl.paused_close.maybe_invoke() || + impl.paused_ping.maybe_invoke() || + impl.paused_wr.maybe_invoke(); + goto do_maybe_suspend; + } + // Handle pong frame + if(impl.rd_fh.op == detail::opcode::pong) + { + // Ignore pong when closing + if(! impl.wr_close && impl.ctrl_cb) + { + if(! cont_) + { + BOOST_ASIO_CORO_YIELD + net::post( + ws_.get_executor(), + std::move(*this)); + BOOST_ASSERT(cont_); + } + } + auto const cb = buffers_prefix(clamp( + impl.rd_fh.len), impl.rd_buf.data()); + auto const len = buffer_size(cb); + BOOST_ASSERT(len == impl.rd_fh.len); + ping_data payload; + detail::read_ping(payload, cb); + impl.rd_buf.consume(len); + // Ignore pong when closing + if(! impl.wr_close && impl.ctrl_cb) + impl.ctrl_cb(frame_type::pong, payload); + goto loop; + } + // Handle close frame + BOOST_ASSERT(impl.rd_fh.op == detail::opcode::close); + { + if(impl.ctrl_cb) + { + if(! cont_) + { + BOOST_ASIO_CORO_YIELD + net::post( + ws_.get_executor(), + std::move(*this)); + BOOST_ASSERT(cont_); + } + } + auto const cb = buffers_prefix(clamp( + impl.rd_fh.len), impl.rd_buf.data()); + auto const len = buffer_size(cb); + BOOST_ASSERT(len == impl.rd_fh.len); + BOOST_ASSERT(! impl.rd_close); + impl.rd_close = true; + close_reason cr; + detail::read_close(cr, cb, result_); + if(result_) + { + // _Fail the WebSocket Connection_ + code_ = close_code::protocol_error; + goto close; + } + impl.cr = cr; + impl.rd_buf.consume(len); + if(impl.ctrl_cb) + impl.ctrl_cb(frame_type::close, + impl.cr.reason); + // See if we are already closing + if(impl.status_ == status::closing) + { + // _Close the WebSocket Connection_ + BOOST_ASSERT(impl.wr_close); + code_ = close_code::none; + result_ = error::closed; + goto close; + } + // _Start the WebSocket Closing Handshake_ + code_ = cr.code == close_code::none ? + close_code::normal : + static_cast(cr.code); + result_ = error::closed; + goto close; + } + } + if(impl.rd_fh.len == 0 && ! impl.rd_fh.fin) + { + // Empty non-final frame + goto loop; + } + impl.rd_done = false; + } + if(! impl.rd_deflated()) + { + if(impl.rd_remain > 0) + { + if(impl.rd_buf.size() == 0 && impl.rd_buf.max_size() > + (std::min)(clamp(impl.rd_remain), + buffer_size(cb_))) + { + // Fill the read buffer first, otherwise we + // get fewer bytes at the cost of one I/O. + BOOST_ASIO_CORO_YIELD + impl.stream.async_read_some( + impl.rd_buf.prepare(read_size( + impl.rd_buf, impl.rd_buf.max_size())), + std::move(*this)); + if(! impl.check_ok(ec)) + goto upcall; + impl.rd_buf.commit(bytes_transferred); + if(impl.rd_fh.mask) + detail::mask_inplace(buffers_prefix(clamp( + impl.rd_remain), impl.rd_buf.data()), + impl.rd_key); + } + if(impl.rd_buf.size() > 0) + { + // Copy from the read buffer. + // The mask was already applied. + bytes_transferred = net::buffer_copy(cb_, + impl.rd_buf.data(), clamp(impl.rd_remain)); + auto const mb = buffers_prefix( + bytes_transferred, cb_); + impl.rd_remain -= bytes_transferred; + if(impl.rd_op == detail::opcode::text) + { + if(! impl.rd_utf8.write(mb) || + (impl.rd_remain == 0 && impl.rd_fh.fin && + ! impl.rd_utf8.finish())) + { + // _Fail the WebSocket Connection_ + code_ = close_code::bad_payload; + result_ = error::bad_frame_payload; + goto close; + } + } + bytes_written_ += bytes_transferred; + impl.rd_size += bytes_transferred; + impl.rd_buf.consume(bytes_transferred); + } + else + { + // Read into caller's buffer + BOOST_ASSERT(impl.rd_remain > 0); + BOOST_ASSERT(buffer_size(cb_) > 0); + BOOST_ASSERT(buffer_size(buffers_prefix( + clamp(impl.rd_remain), cb_)) > 0); + BOOST_ASIO_CORO_YIELD + impl.stream.async_read_some(buffers_prefix( + clamp(impl.rd_remain), cb_), std::move(*this)); + if(! impl.check_ok(ec)) + goto upcall; + BOOST_ASSERT(bytes_transferred > 0); + auto const mb = buffers_prefix( + bytes_transferred, cb_); + impl.rd_remain -= bytes_transferred; + if(impl.rd_fh.mask) + detail::mask_inplace(mb, impl.rd_key); + if(impl.rd_op == detail::opcode::text) + { + if(! impl.rd_utf8.write(mb) || + (impl.rd_remain == 0 && impl.rd_fh.fin && + ! impl.rd_utf8.finish())) + { + // _Fail the WebSocket Connection_ + code_ = close_code::bad_payload; + result_ = error::bad_frame_payload; + goto close; + } + } + bytes_written_ += bytes_transferred; + impl.rd_size += bytes_transferred; + } + } + impl.rd_done = impl.rd_remain == 0 && impl.rd_fh.fin; + } + else + { + // Read compressed message frame payload: + // inflate even if rd_fh_.len == 0, otherwise we + // never emit the end-of-stream deflate block. + while(buffer_size(cb_) > 0) + { + if( impl.rd_remain > 0 && + impl.rd_buf.size() == 0 && + ! did_read_) + { + // read new + BOOST_ASIO_CORO_YIELD + impl.stream.async_read_some( + impl.rd_buf.prepare(read_size( + impl.rd_buf, impl.rd_buf.max_size())), + std::move(*this)); + if(! impl.check_ok(ec)) + goto upcall; + BOOST_ASSERT(bytes_transferred > 0); + impl.rd_buf.commit(bytes_transferred); + if(impl.rd_fh.mask) + detail::mask_inplace( + buffers_prefix(clamp(impl.rd_remain), + impl.rd_buf.data()), impl.rd_key); + did_read_ = true; + } + zlib::z_params zs; + { + auto const out = buffers_front(cb_); + zs.next_out = out.data(); + zs.avail_out = out.size(); + BOOST_ASSERT(zs.avail_out > 0); + } + if(impl.rd_remain > 0) + { + if(impl.rd_buf.size() > 0) + { + // use what's there + auto const in = buffers_prefix( + clamp(impl.rd_remain), buffers_front( + impl.rd_buf.data())); + zs.avail_in = in.size(); + zs.next_in = in.data(); + } + else + { + break; + } + } + else if(impl.rd_fh.fin) + { + // append the empty block codes + static std::uint8_t constexpr + empty_block[4] = { + 0x00, 0x00, 0xff, 0xff }; + zs.next_in = empty_block; + zs.avail_in = sizeof(empty_block); + impl.inflate(zs, zlib::Flush::sync, ec); + if(! ec) + { + // https://github.com/madler/zlib/issues/280 + if(zs.total_out > 0) + ec = error::partial_deflate_block; + } + if(! impl.check_ok(ec)) + goto upcall; + impl.do_context_takeover_read(impl.role); + impl.rd_done = true; + break; + } + else + { + break; + } + impl.inflate(zs, zlib::Flush::sync, ec); + if(! impl.check_ok(ec)) + goto upcall; + if(impl.rd_msg_max && beast::detail::sum_exceeds( + impl.rd_size, zs.total_out, impl.rd_msg_max)) + { + // _Fail the WebSocket Connection_ + code_ = close_code::too_big; + result_ = error::message_too_big; + goto close; + } + cb_.consume(zs.total_out); + impl.rd_size += zs.total_out; + impl.rd_remain -= zs.total_in; + impl.rd_buf.consume(zs.total_in); + bytes_written_ += zs.total_out; + } + if(impl.rd_op == detail::opcode::text) + { + // check utf8 + if(! impl.rd_utf8.write( + buffers_prefix(bytes_written_, bs_)) || ( + impl.rd_done && ! impl.rd_utf8.finish())) + { + // _Fail the WebSocket Connection_ + code_ = close_code::bad_payload; + result_ = error::bad_frame_payload; + goto close; + } + } + } + goto upcall; + + close: + // Try to acquire the write block + if(! impl.wr_block.try_lock(this)) + { + // Suspend + BOOST_ASIO_CORO_YIELD + impl.paused_rd.emplace(std::move(*this)); + + // Acquire the write block + impl.wr_block.lock(this); + + // Resume + BOOST_ASIO_CORO_YIELD + net::post( + ws_.get_executor(), std::move(*this)); + BOOST_ASSERT(impl.wr_block.is_locked(this)); + + // Make sure the stream is open + if(! impl.check_open(ec)) + goto upcall; + } + + // Set the status + impl.status_ = status::closing; + + if(! impl.wr_close) + { + impl.wr_close = true; + + // Serialize close frame + impl.rd_fb.clear(); + ws_.template write_close< + flat_static_buffer_base>( + impl.rd_fb, code_); + + // Send close frame + BOOST_ASSERT(impl.wr_block.is_locked(this)); + BOOST_ASIO_CORO_YIELD + net::async_write( + impl.stream, impl.rd_fb.data(), + std::move(*this)); + BOOST_ASSERT(impl.wr_block.is_locked(this)); + if(! impl.check_ok(ec)) + goto upcall; + } + + // Teardown + using beast::websocket::async_teardown; + BOOST_ASSERT(impl.wr_block.is_locked(this)); + BOOST_ASIO_CORO_YIELD + async_teardown(impl.role, + impl.stream, std::move(*this)); + BOOST_ASSERT(impl.wr_block.is_locked(this)); + if(ec == net::error::eof) + { + // Rationale: + // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error + ec = {}; + } + if(! ec) + ec = result_; + if(ec && ec != error::closed) + impl.status_ = status::failed; + else + impl.status_ = status::closed; + impl.close(); + + upcall: + impl.rd_block.try_unlock(this); + impl.paused_r_close.maybe_invoke(); + if(impl.wr_block.try_unlock(this)) + impl.paused_close.maybe_invoke() || + impl.paused_ping.maybe_invoke() || + impl.paused_wr.maybe_invoke(); + if(! cont_) + { + BOOST_ASIO_CORO_YIELD + net::post( + ws_.get_executor(), + beast::bind_front_handler(std::move(*this), + ec, bytes_written_)); + } + this->invoke(ec, bytes_written_); + } + } +}; + +//------------------------------------------------------------------------------ + +template +template< + class DynamicBuffer, + class Handler> +class stream::read_op + : public beast::async_op_base< + Handler, beast::detail::get_executor_type> + , public net::coroutine +{ + stream& ws_; + DynamicBuffer& b_; + std::size_t limit_; + std::size_t bytes_written_ = 0; + bool some_; + +public: + template + read_op( + Handler_&& h, + stream& ws, + DynamicBuffer& b, + std::size_t limit, + bool some) + : async_op_base< + Handler, beast::detail::get_executor_type>( + std::forward(h), ws.get_executor()) + , ws_(ws) + , b_(b) + , limit_(limit ? limit : ( + std::numeric_limits::max)()) + , some_(some) + { + } + + void operator()( + error_code ec = {}, + std::size_t bytes_transferred = 0) + { + using beast::detail::clamp; + BOOST_ASIO_CORO_REENTER(*this) + { + do + { + BOOST_ASIO_CORO_YIELD + { + auto mb = beast::detail::dynamic_buffer_prepare(b_, + clamp(ws_.read_size_hint(b_), limit_), + ec, error::buffer_overflow); + if(ec) + net::post( + ws_.get_executor(), + beast::bind_front_handler( + std::move(*this), ec, 0)); + else + read_some_op(std::move(*this), ws_, *mb)( + {}, 0, false); + } + if(ec) + goto upcall; + b_.commit(bytes_transferred); + bytes_written_ += bytes_transferred; + } + while(! some_ && ! ws_.is_message_done()); + upcall: + this->invoke(ec, bytes_written_); + } + } +}; + +//------------------------------------------------------------------------------ + +template +template +std::size_t +stream:: +read(DynamicBuffer& buffer) +{ + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer requirements not met"); + error_code ec; + auto const bytes_written = read(buffer, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_written; +} + +template +template +std::size_t +stream:: +read(DynamicBuffer& buffer, error_code& ec) +{ + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer requirements not met"); + std::size_t bytes_written = 0; + do + { + bytes_written += read_some(buffer, 0, ec); + if(ec) + return bytes_written; + } + while(! is_message_done()); + return bytes_written; +} + +template +template +BOOST_ASIO_INITFN_RESULT_TYPE( + ReadHandler, void(error_code, std::size_t)) +stream:: +async_read(DynamicBuffer& buffer, ReadHandler&& handler) +{ + static_assert(is_async_stream::value, + "AsyncStream requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer requirements not met"); + BOOST_BEAST_HANDLER_INIT( + ReadHandler, void(error_code, std::size_t)); + read_op< + DynamicBuffer, + BOOST_ASIO_HANDLER_TYPE( + ReadHandler, void(error_code, std::size_t))>{ + std::move(init.completion_handler), + *this, + buffer, + 0, + false}(); + return init.result.get(); +} + +//------------------------------------------------------------------------------ + +template +template +std::size_t +stream:: +read_some( + DynamicBuffer& buffer, + std::size_t limit) +{ + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer requirements not met"); + error_code ec; + auto const bytes_written = + read_some(buffer, limit, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_written; +} + +template +template +std::size_t +stream:: +read_some( + DynamicBuffer& buffer, + std::size_t limit, + error_code& ec) +{ + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer requirements not met"); + using beast::detail::clamp; + if(! limit) + limit = (std::numeric_limits::max)(); + auto const size = + clamp(read_size_hint(buffer), limit); + BOOST_ASSERT(size > 0); + auto mb = beast::detail::dynamic_buffer_prepare( + buffer, size, ec, error::buffer_overflow); + if(ec) + return 0; + auto const bytes_written = read_some(*mb, ec); + buffer.commit(bytes_written); + return bytes_written; +} + +template +template +BOOST_ASIO_INITFN_RESULT_TYPE( + ReadHandler, void(error_code, std::size_t)) +stream:: +async_read_some( + DynamicBuffer& buffer, + std::size_t limit, + ReadHandler&& handler) +{ + static_assert(is_async_stream::value, + "AsyncStream requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer requirements not met"); + BOOST_BEAST_HANDLER_INIT( + ReadHandler, void(error_code, std::size_t)); + read_op< + DynamicBuffer, + BOOST_ASIO_HANDLER_TYPE( + ReadHandler, void(error_code, std::size_t))>{ + std::move(init.completion_handler), + *this, + buffer, + limit, + true}({}, 0); + return init.result.get(); +} + +//------------------------------------------------------------------------------ + +template +template +std::size_t +stream:: +read_some( + MutableBufferSequence const& buffers) +{ + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + static_assert(net::is_mutable_buffer_sequence< + MutableBufferSequence>::value, + "MutableBufferSequence requirements not met"); + error_code ec; + auto const bytes_written = read_some(buffers, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_written; +} + +template +template +std::size_t +stream:: +read_some( + MutableBufferSequence const& buffers, + error_code& ec) +{ + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + static_assert(net::is_mutable_buffer_sequence< + MutableBufferSequence>::value, + "MutableBufferSequence requirements not met"); + using beast::detail::clamp; + using net::buffer; + using net::buffer_size; + close_code code{}; + std::size_t bytes_written = 0; + ec = {}; + // Make sure the stream is open + if(! impl_->check_open(ec)) + return 0; +loop: + // See if we need to read a frame header. This + // condition is structured to give the decompressor + // a chance to emit the final empty deflate block + // + if(impl_->rd_remain == 0 && ( + ! impl_->rd_fh.fin || impl_->rd_done)) + { + // Read frame header + error_code result; + while(! parse_fh(impl_->rd_fh, impl_->rd_buf, result)) + { + if(result) + { + // _Fail the WebSocket Connection_ + if(result == error::message_too_big) + code = close_code::too_big; + else + code = close_code::protocol_error; + do_fail(code, result, ec); + return bytes_written; + } + auto const bytes_transferred = + impl_->stream.read_some( + impl_->rd_buf.prepare(read_size( + impl_->rd_buf, impl_->rd_buf.max_size())), + ec); + if(! impl_->check_ok(ec)) + return bytes_written; + impl_->rd_buf.commit(bytes_transferred); + } + // Immediately apply the mask to the portion + // of the buffer holding payload data. + if(impl_->rd_fh.len > 0 && impl_->rd_fh.mask) + detail::mask_inplace(buffers_prefix( + clamp(impl_->rd_fh.len), impl_->rd_buf.data()), + impl_->rd_key); + if(detail::is_control(impl_->rd_fh.op)) + { + // Get control frame payload + auto const b = buffers_prefix( + clamp(impl_->rd_fh.len), impl_->rd_buf.data()); + auto const len = buffer_size(b); + BOOST_ASSERT(len == impl_->rd_fh.len); + + // Clear this otherwise the next + // frame will be considered final. + impl_->rd_fh.fin = false; + + // Handle ping frame + if(impl_->rd_fh.op == detail::opcode::ping) + { + ping_data payload; + detail::read_ping(payload, b); + impl_->rd_buf.consume(len); + if(impl_->wr_close) + { + // Ignore ping when closing + goto loop; + } + if(impl_->ctrl_cb) + impl_->ctrl_cb(frame_type::ping, payload); + detail::frame_buffer fb; + write_ping(fb, + detail::opcode::pong, payload); + net::write(impl_->stream, fb.data(), ec); + if(! impl_->check_ok(ec)) + return bytes_written; + goto loop; + } + // Handle pong frame + if(impl_->rd_fh.op == detail::opcode::pong) + { + ping_data payload; + detail::read_ping(payload, b); + impl_->rd_buf.consume(len); + if(impl_->ctrl_cb) + impl_->ctrl_cb(frame_type::pong, payload); + goto loop; + } + // Handle close frame + BOOST_ASSERT(impl_->rd_fh.op == detail::opcode::close); + { + BOOST_ASSERT(! impl_->rd_close); + impl_->rd_close = true; + close_reason cr; + detail::read_close(cr, b, result); + if(result) + { + // _Fail the WebSocket Connection_ + do_fail(close_code::protocol_error, + result, ec); + return bytes_written; + } + impl_->cr = cr; + impl_->rd_buf.consume(len); + if(impl_->ctrl_cb) + impl_->ctrl_cb(frame_type::close, impl_->cr.reason); + BOOST_ASSERT(! impl_->wr_close); + // _Start the WebSocket Closing Handshake_ + do_fail( + cr.code == close_code::none ? + close_code::normal : + static_cast(cr.code), + error::closed, ec); + return bytes_written; + } + } + if(impl_->rd_fh.len == 0 && ! impl_->rd_fh.fin) + { + // Empty non-final frame + goto loop; + } + impl_->rd_done = false; + } + else + { + ec = {}; + } + if(! impl_->rd_deflated()) + { + if(impl_->rd_remain > 0) + { + if(impl_->rd_buf.size() == 0 && impl_->rd_buf.max_size() > + (std::min)(clamp(impl_->rd_remain), + buffer_size(buffers))) + { + // Fill the read buffer first, otherwise we + // get fewer bytes at the cost of one I/O. + impl_->rd_buf.commit(impl_->stream.read_some( + impl_->rd_buf.prepare(read_size(impl_->rd_buf, + impl_->rd_buf.max_size())), ec)); + if(! impl_->check_ok(ec)) + return bytes_written; + if(impl_->rd_fh.mask) + detail::mask_inplace( + buffers_prefix(clamp(impl_->rd_remain), + impl_->rd_buf.data()), impl_->rd_key); + } + if(impl_->rd_buf.size() > 0) + { + // Copy from the read buffer. + // The mask was already applied. + auto const bytes_transferred = net::buffer_copy( + buffers, impl_->rd_buf.data(), + clamp(impl_->rd_remain)); + auto const mb = buffers_prefix( + bytes_transferred, buffers); + impl_->rd_remain -= bytes_transferred; + if(impl_->rd_op == detail::opcode::text) + { + if(! impl_->rd_utf8.write(mb) || + (impl_->rd_remain == 0 && impl_->rd_fh.fin && + ! impl_->rd_utf8.finish())) + { + // _Fail the WebSocket Connection_ + do_fail(close_code::bad_payload, + error::bad_frame_payload, ec); + return bytes_written; + } + } + bytes_written += bytes_transferred; + impl_->rd_size += bytes_transferred; + impl_->rd_buf.consume(bytes_transferred); + } + else + { + // Read into caller's buffer + BOOST_ASSERT(impl_->rd_remain > 0); + BOOST_ASSERT(buffer_size(buffers) > 0); + BOOST_ASSERT(buffer_size(buffers_prefix( + clamp(impl_->rd_remain), buffers)) > 0); + auto const bytes_transferred = + impl_->stream.read_some(buffers_prefix( + clamp(impl_->rd_remain), buffers), ec); + if(! impl_->check_ok(ec)) + return bytes_written; + BOOST_ASSERT(bytes_transferred > 0); + auto const mb = buffers_prefix( + bytes_transferred, buffers); + impl_->rd_remain -= bytes_transferred; + if(impl_->rd_fh.mask) + detail::mask_inplace(mb, impl_->rd_key); + if(impl_->rd_op == detail::opcode::text) + { + if(! impl_->rd_utf8.write(mb) || + (impl_->rd_remain == 0 && impl_->rd_fh.fin && + ! impl_->rd_utf8.finish())) + { + // _Fail the WebSocket Connection_ + do_fail(close_code::bad_payload, + error::bad_frame_payload, ec); + return bytes_written; + } + } + bytes_written += bytes_transferred; + impl_->rd_size += bytes_transferred; + } + } + impl_->rd_done = impl_->rd_remain == 0 && impl_->rd_fh.fin; + } + else + { + // Read compressed message frame payload: + // inflate even if rd_fh_.len == 0, otherwise we + // never emit the end-of-stream deflate block. + // + bool did_read = false; + buffers_suffix cb(buffers); + while(buffer_size(cb) > 0) + { + zlib::z_params zs; + { + auto const out = beast::buffers_front(cb); + zs.next_out = out.data(); + zs.avail_out = out.size(); + BOOST_ASSERT(zs.avail_out > 0); + } + if(impl_->rd_remain > 0) + { + if(impl_->rd_buf.size() > 0) + { + // use what's there + auto const in = buffers_prefix( + clamp(impl_->rd_remain), beast::buffers_front( + impl_->rd_buf.data())); + zs.avail_in = in.size(); + zs.next_in = in.data(); + } + else if(! did_read) + { + // read new + auto const bytes_transferred = + impl_->stream.read_some( + impl_->rd_buf.prepare(read_size( + impl_->rd_buf, impl_->rd_buf.max_size())), + ec); + if(! impl_->check_ok(ec)) + return bytes_written; + BOOST_ASSERT(bytes_transferred > 0); + impl_->rd_buf.commit(bytes_transferred); + if(impl_->rd_fh.mask) + detail::mask_inplace( + buffers_prefix(clamp(impl_->rd_remain), + impl_->rd_buf.data()), impl_->rd_key); + auto const in = buffers_prefix( + clamp(impl_->rd_remain), buffers_front( + impl_->rd_buf.data())); + zs.avail_in = in.size(); + zs.next_in = in.data(); + did_read = true; + } + else + { + break; + } + } + else if(impl_->rd_fh.fin) + { + // append the empty block codes + static std::uint8_t constexpr + empty_block[4] = { + 0x00, 0x00, 0xff, 0xff }; + zs.next_in = empty_block; + zs.avail_in = sizeof(empty_block); + impl_->inflate(zs, zlib::Flush::sync, ec); + if(! ec) + { + // https://github.com/madler/zlib/issues/280 + if(zs.total_out > 0) + ec = error::partial_deflate_block; + } + if(! impl_->check_ok(ec)) + return bytes_written; + impl_->do_context_takeover_read(impl_->role); + impl_->rd_done = true; + break; + } + else + { + break; + } + impl_->inflate(zs, zlib::Flush::sync, ec); + if(! impl_->check_ok(ec)) + return bytes_written; + if(impl_->rd_msg_max && beast::detail::sum_exceeds( + impl_->rd_size, zs.total_out, impl_->rd_msg_max)) + { + do_fail(close_code::too_big, + error::message_too_big, ec); + return bytes_written; + } + cb.consume(zs.total_out); + impl_->rd_size += zs.total_out; + impl_->rd_remain -= zs.total_in; + impl_->rd_buf.consume(zs.total_in); + bytes_written += zs.total_out; + } + if(impl_->rd_op == detail::opcode::text) + { + // check utf8 + if(! impl_->rd_utf8.write(beast::buffers_prefix( + bytes_written, buffers)) || ( + impl_->rd_done && ! impl_->rd_utf8.finish())) + { + // _Fail the WebSocket Connection_ + do_fail(close_code::bad_payload, + error::bad_frame_payload, ec); + return bytes_written; + } + } + } + return bytes_written; +} + +template +template +BOOST_ASIO_INITFN_RESULT_TYPE( + ReadHandler, void(error_code, std::size_t)) +stream:: +async_read_some( + MutableBufferSequence const& buffers, + ReadHandler&& handler) +{ + static_assert(is_async_stream::value, + "AsyncStream requirements not met"); + static_assert(net::is_mutable_buffer_sequence< + MutableBufferSequence>::value, + "MutableBufferSequence requirements not met"); + BOOST_BEAST_HANDLER_INIT( + ReadHandler, void(error_code, std::size_t)); + read_some_op{ + std::move(init.completion_handler), *this, buffers}( + {}, 0, false); + return init.result.get(); +} + +} // websocket +} // beast +} // boost + +#endif diff --git a/include/boost/beast/websocket/impl/read.ipp b/include/boost/beast/websocket/impl/read.ipp deleted file mode 100644 index fb2b3345..00000000 --- a/include/boost/beast/websocket/impl/read.ipp +++ /dev/null @@ -1,1274 +0,0 @@ -// -// Copyright (c) 2016-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) -// -// Official repository: https://github.com/boostorg/beast -// - -#ifndef BOOST_BEAST_WEBSOCKET_IMPL_READ_IPP -#define BOOST_BEAST_WEBSOCKET_IMPL_READ_IPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace beast { -namespace websocket { - -namespace detail { - -template<> -inline -void -stream_base:: -inflate( - zlib::z_params& zs, - zlib::Flush flush, - error_code& ec) -{ - this->pmd_->zi.write(zs, flush, ec); -} - -template<> -inline -void -stream_base:: -do_context_takeover_read(role_type role) -{ - if((role == role_type::client && - pmd_config_.server_no_context_takeover) || - (role == role_type::server && - pmd_config_.client_no_context_takeover)) - { - pmd_->zi.clear(); - } -} - -} // detail - -//------------------------------------------------------------------------------ - -/* Read some message frame data. - - Also reads and handles control frames. -*/ -template -template< - class MutableBufferSequence, - class Handler> -class stream::read_some_op - : public beast::async_op_base< - Handler, beast::detail::get_executor_type> - , public net::coroutine -{ - stream& ws_; - MutableBufferSequence bs_; - buffers_suffix cb_; - std::size_t bytes_written_ = 0; - error_code result_; - close_code code_; - bool did_read_ = false; - bool cont_ = false; - -public: - static constexpr int id = 1; // for soft_mutex - - template - read_some_op( - Handler_&& h, - stream& ws, - MutableBufferSequence const& bs) - : async_op_base< - Handler, beast::detail::get_executor_type>( - std::forward(h), ws.get_executor()) - , ws_(ws) - , bs_(bs) - , cb_(bs) - , code_(close_code::none) - { - } - - void operator()( - error_code ec = {}, - std::size_t bytes_transferred = 0, - bool cont = true); -}; - -template -template -void -stream:: -read_some_op:: -operator()( - error_code ec, - std::size_t bytes_transferred, - bool cont) -{ - using beast::detail::clamp; - using net::buffer; - using net::buffer_size; - cont_ = cont; - BOOST_ASIO_CORO_REENTER(*this) - { - // Maybe suspend - do_maybe_suspend: - if(ws_.rd_block_.try_lock(this)) - { - // Make sure the stream is not closed - if( ws_.status_ == status::closed || - ws_.status_ == status::failed) - { - ec = net::error::operation_aborted; - goto upcall; - } - } - else - { - do_suspend: - // Suspend - BOOST_ASIO_CORO_YIELD - ws_.paused_r_rd_.emplace(std::move(*this)); - - // Acquire the read block - ws_.rd_block_.lock(this); - - // Resume - BOOST_ASIO_CORO_YIELD - net::post( - ws_.get_executor(), std::move(*this)); - BOOST_ASSERT(ws_.rd_block_.is_locked(this)); - - // The only way to get read blocked is if - // a `close_op` wrote a close frame - BOOST_ASSERT(ws_.wr_close_); - BOOST_ASSERT(ws_.status_ != status::open); - ec = net::error::operation_aborted; - goto upcall; - } - - // if status_ == status::closing, we want to suspend - // the read operation until the close completes, - // then finish the read with operation_aborted. - - loop: - BOOST_ASSERT(ws_.rd_block_.is_locked(this)); - // See if we need to read a frame header. This - // condition is structured to give the decompressor - // a chance to emit the final empty deflate block - // - if(ws_.rd_remain_ == 0 && - (! ws_.rd_fh_.fin || ws_.rd_done_)) - { - // Read frame header - while(! ws_.parse_fh( - ws_.rd_fh_, ws_.rd_buf_, result_)) - { - if(result_) - { - // _Fail the WebSocket Connection_ - if(result_ == error::message_too_big) - code_ = close_code::too_big; - else - code_ = close_code::protocol_error; - goto close; - } - BOOST_ASSERT(ws_.rd_block_.is_locked(this)); - BOOST_ASIO_CORO_YIELD - ws_.stream_.async_read_some( - ws_.rd_buf_.prepare(read_size( - ws_.rd_buf_, ws_.rd_buf_.max_size())), - std::move(*this)); - BOOST_ASSERT(ws_.rd_block_.is_locked(this)); - if(! ws_.check_ok(ec)) - goto upcall; - ws_.rd_buf_.commit(bytes_transferred); - - // Allow a close operation - // to acquire the read block - ws_.rd_block_.unlock(this); - if( ws_.paused_r_close_.maybe_invoke()) - { - // Suspend - BOOST_ASSERT(ws_.rd_block_.is_locked()); - goto do_suspend; - } - // Acquire read block - ws_.rd_block_.lock(this); - } - // Immediately apply the mask to the portion - // of the buffer holding payload data. - if(ws_.rd_fh_.len > 0 && ws_.rd_fh_.mask) - detail::mask_inplace(buffers_prefix( - clamp(ws_.rd_fh_.len), - ws_.rd_buf_.data()), - ws_.rd_key_); - if(detail::is_control(ws_.rd_fh_.op)) - { - // Clear this otherwise the next - // frame will be considered final. - ws_.rd_fh_.fin = false; - - // Handle ping frame - if(ws_.rd_fh_.op == detail::opcode::ping) - { - if(ws_.ctrl_cb_) - { - if(! cont_) - { - BOOST_ASIO_CORO_YIELD - net::post( - ws_.get_executor(), - std::move(*this)); - BOOST_ASSERT(cont_); - } - } - { - auto const b = buffers_prefix( - clamp(ws_.rd_fh_.len), - ws_.rd_buf_.data()); - auto const len = buffer_size(b); - BOOST_ASSERT(len == ws_.rd_fh_.len); - ping_data payload; - detail::read_ping(payload, b); - ws_.rd_buf_.consume(len); - // Ignore ping when closing - if(ws_.status_ == status::closing) - goto loop; - if(ws_.ctrl_cb_) - ws_.ctrl_cb_( - frame_type::ping, payload); - ws_.rd_fb_.clear(); - ws_.template write_ping< - flat_static_buffer_base>(ws_.rd_fb_, - detail::opcode::pong, payload); - } - - // Allow a close operation - // to acquire the read block - ws_.rd_block_.unlock(this); - ws_.paused_r_close_.maybe_invoke(); - - // Maybe suspend - if(! ws_.wr_block_.try_lock(this)) - { - // Suspend - BOOST_ASIO_CORO_YIELD - ws_.paused_rd_.emplace(std::move(*this)); - - // Acquire the write block - ws_.wr_block_.lock(this); - - // Resume - BOOST_ASIO_CORO_YIELD - net::post( - ws_.get_executor(), std::move(*this)); - BOOST_ASSERT(ws_.wr_block_.is_locked(this)); - - // Make sure the stream is open - if(! ws_.check_open(ec)) - goto upcall; - } - - // Send pong - BOOST_ASSERT(ws_.wr_block_.is_locked(this)); - BOOST_ASIO_CORO_YIELD - net::async_write(ws_.stream_, - ws_.rd_fb_.data(), std::move(*this)); - BOOST_ASSERT(ws_.wr_block_.is_locked(this)); - if(! ws_.check_ok(ec)) - goto upcall; - ws_.wr_block_.unlock(this); - ws_.paused_close_.maybe_invoke() || - ws_.paused_ping_.maybe_invoke() || - ws_.paused_wr_.maybe_invoke(); - goto do_maybe_suspend; - } - // Handle pong frame - if(ws_.rd_fh_.op == detail::opcode::pong) - { - // Ignore pong when closing - if(! ws_.wr_close_ && ws_.ctrl_cb_) - { - if(! cont_) - { - BOOST_ASIO_CORO_YIELD - net::post( - ws_.get_executor(), - std::move(*this)); - BOOST_ASSERT(cont_); - } - } - auto const cb = buffers_prefix(clamp( - ws_.rd_fh_.len), ws_.rd_buf_.data()); - auto const len = buffer_size(cb); - BOOST_ASSERT(len == ws_.rd_fh_.len); - ping_data payload; - detail::read_ping(payload, cb); - ws_.rd_buf_.consume(len); - // Ignore pong when closing - if(! ws_.wr_close_ && ws_.ctrl_cb_) - ws_.ctrl_cb_(frame_type::pong, payload); - goto loop; - } - // Handle close frame - BOOST_ASSERT(ws_.rd_fh_.op == detail::opcode::close); - { - if(ws_.ctrl_cb_) - { - if(! cont_) - { - BOOST_ASIO_CORO_YIELD - net::post( - ws_.get_executor(), - std::move(*this)); - BOOST_ASSERT(cont_); - } - } - auto const cb = buffers_prefix(clamp( - ws_.rd_fh_.len), ws_.rd_buf_.data()); - auto const len = buffer_size(cb); - BOOST_ASSERT(len == ws_.rd_fh_.len); - BOOST_ASSERT(! ws_.rd_close_); - ws_.rd_close_ = true; - close_reason cr; - detail::read_close(cr, cb, result_); - if(result_) - { - // _Fail the WebSocket Connection_ - code_ = close_code::protocol_error; - goto close; - } - ws_.cr_ = cr; - ws_.rd_buf_.consume(len); - if(ws_.ctrl_cb_) - ws_.ctrl_cb_(frame_type::close, - ws_.cr_.reason); - // See if we are already closing - if(ws_.status_ == status::closing) - { - // _Close the WebSocket Connection_ - BOOST_ASSERT(ws_.wr_close_); - code_ = close_code::none; - result_ = error::closed; - goto close; - } - // _Start the WebSocket Closing Handshake_ - code_ = cr.code == close_code::none ? - close_code::normal : - static_cast(cr.code); - result_ = error::closed; - goto close; - } - } - if(ws_.rd_fh_.len == 0 && ! ws_.rd_fh_.fin) - { - // Empty non-final frame - goto loop; - } - ws_.rd_done_ = false; - } - if(! ws_.rd_deflated()) - { - if(ws_.rd_remain_ > 0) - { - if(ws_.rd_buf_.size() == 0 && ws_.rd_buf_.max_size() > - (std::min)(clamp(ws_.rd_remain_), - buffer_size(cb_))) - { - // Fill the read buffer first, otherwise we - // get fewer bytes at the cost of one I/O. - BOOST_ASIO_CORO_YIELD - ws_.stream_.async_read_some( - ws_.rd_buf_.prepare(read_size( - ws_.rd_buf_, ws_.rd_buf_.max_size())), - std::move(*this)); - if(! ws_.check_ok(ec)) - goto upcall; - ws_.rd_buf_.commit(bytes_transferred); - if(ws_.rd_fh_.mask) - detail::mask_inplace(buffers_prefix(clamp( - ws_.rd_remain_), ws_.rd_buf_.data()), - ws_.rd_key_); - } - if(ws_.rd_buf_.size() > 0) - { - // Copy from the read buffer. - // The mask was already applied. - bytes_transferred = net::buffer_copy(cb_, - ws_.rd_buf_.data(), clamp(ws_.rd_remain_)); - auto const mb = buffers_prefix( - bytes_transferred, cb_); - ws_.rd_remain_ -= bytes_transferred; - if(ws_.rd_op_ == detail::opcode::text) - { - if(! ws_.rd_utf8_.write(mb) || - (ws_.rd_remain_ == 0 && ws_.rd_fh_.fin && - ! ws_.rd_utf8_.finish())) - { - // _Fail the WebSocket Connection_ - code_ = close_code::bad_payload; - result_ = error::bad_frame_payload; - goto close; - } - } - bytes_written_ += bytes_transferred; - ws_.rd_size_ += bytes_transferred; - ws_.rd_buf_.consume(bytes_transferred); - } - else - { - // Read into caller's buffer - BOOST_ASSERT(ws_.rd_remain_ > 0); - BOOST_ASSERT(buffer_size(cb_) > 0); - BOOST_ASSERT(buffer_size(buffers_prefix( - clamp(ws_.rd_remain_), cb_)) > 0); - BOOST_ASIO_CORO_YIELD - ws_.stream_.async_read_some(buffers_prefix( - clamp(ws_.rd_remain_), cb_), std::move(*this)); - if(! ws_.check_ok(ec)) - goto upcall; - BOOST_ASSERT(bytes_transferred > 0); - auto const mb = buffers_prefix( - bytes_transferred, cb_); - ws_.rd_remain_ -= bytes_transferred; - if(ws_.rd_fh_.mask) - detail::mask_inplace(mb, ws_.rd_key_); - if(ws_.rd_op_ == detail::opcode::text) - { - if(! ws_.rd_utf8_.write(mb) || - (ws_.rd_remain_ == 0 && ws_.rd_fh_.fin && - ! ws_.rd_utf8_.finish())) - { - // _Fail the WebSocket Connection_ - code_ = close_code::bad_payload; - result_ = error::bad_frame_payload; - goto close; - } - } - bytes_written_ += bytes_transferred; - ws_.rd_size_ += bytes_transferred; - } - } - ws_.rd_done_ = ws_.rd_remain_ == 0 && ws_.rd_fh_.fin; - } - else - { - // Read compressed message frame payload: - // inflate even if rd_fh_.len == 0, otherwise we - // never emit the end-of-stream deflate block. - while(buffer_size(cb_) > 0) - { - if( ws_.rd_remain_ > 0 && - ws_.rd_buf_.size() == 0 && - ! did_read_) - { - // read new - BOOST_ASIO_CORO_YIELD - ws_.stream_.async_read_some( - ws_.rd_buf_.prepare(read_size( - ws_.rd_buf_, ws_.rd_buf_.max_size())), - std::move(*this)); - if(! ws_.check_ok(ec)) - goto upcall; - BOOST_ASSERT(bytes_transferred > 0); - ws_.rd_buf_.commit(bytes_transferred); - if(ws_.rd_fh_.mask) - detail::mask_inplace( - buffers_prefix(clamp(ws_.rd_remain_), - ws_.rd_buf_.data()), ws_.rd_key_); - did_read_ = true; - } - zlib::z_params zs; - { - auto const out = buffers_front(cb_); - zs.next_out = out.data(); - zs.avail_out = out.size(); - BOOST_ASSERT(zs.avail_out > 0); - } - if(ws_.rd_remain_ > 0) - { - if(ws_.rd_buf_.size() > 0) - { - // use what's there - auto const in = buffers_prefix( - clamp(ws_.rd_remain_), buffers_front( - ws_.rd_buf_.data())); - zs.avail_in = in.size(); - zs.next_in = in.data(); - } - else - { - break; - } - } - else if(ws_.rd_fh_.fin) - { - // append the empty block codes - static std::uint8_t constexpr - empty_block[4] = { - 0x00, 0x00, 0xff, 0xff }; - zs.next_in = empty_block; - zs.avail_in = sizeof(empty_block); - ws_.inflate(zs, zlib::Flush::sync, ec); - if(! ec) - { - // https://github.com/madler/zlib/issues/280 - if(zs.total_out > 0) - ec = error::partial_deflate_block; - } - if(! ws_.check_ok(ec)) - goto upcall; - ws_.do_context_takeover_read(ws_.role_); - ws_.rd_done_ = true; - break; - } - else - { - break; - } - ws_.inflate(zs, zlib::Flush::sync, ec); - if(! ws_.check_ok(ec)) - goto upcall; - if(ws_.rd_msg_max_ && beast::detail::sum_exceeds( - ws_.rd_size_, zs.total_out, ws_.rd_msg_max_)) - { - // _Fail the WebSocket Connection_ - code_ = close_code::too_big; - result_ = error::message_too_big; - goto close; - } - cb_.consume(zs.total_out); - ws_.rd_size_ += zs.total_out; - ws_.rd_remain_ -= zs.total_in; - ws_.rd_buf_.consume(zs.total_in); - bytes_written_ += zs.total_out; - } - if(ws_.rd_op_ == detail::opcode::text) - { - // check utf8 - if(! ws_.rd_utf8_.write( - buffers_prefix(bytes_written_, bs_)) || ( - ws_.rd_done_ && ! ws_.rd_utf8_.finish())) - { - // _Fail the WebSocket Connection_ - code_ = close_code::bad_payload; - result_ = error::bad_frame_payload; - goto close; - } - } - } - goto upcall; - - close: - // Try to acquire the write block - if(! ws_.wr_block_.try_lock(this)) - { - // Suspend - BOOST_ASIO_CORO_YIELD - ws_.paused_rd_.emplace(std::move(*this)); - - // Acquire the write block - ws_.wr_block_.lock(this); - - // Resume - BOOST_ASIO_CORO_YIELD - net::post( - ws_.get_executor(), std::move(*this)); - BOOST_ASSERT(ws_.wr_block_.is_locked(this)); - - // Make sure the stream is open - if(! ws_.check_open(ec)) - goto upcall; - } - - // Set the status - ws_.status_ = status::closing; - - if(! ws_.wr_close_) - { - ws_.wr_close_ = true; - - // Serialize close frame - ws_.rd_fb_.clear(); - ws_.template write_close< - flat_static_buffer_base>( - ws_.rd_fb_, code_); - - // Send close frame - BOOST_ASSERT(ws_.wr_block_.is_locked(this)); - BOOST_ASIO_CORO_YIELD - net::async_write( - ws_.stream_, ws_.rd_fb_.data(), - std::move(*this)); - BOOST_ASSERT(ws_.wr_block_.is_locked(this)); - if(! ws_.check_ok(ec)) - goto upcall; - } - - // Teardown - using beast::websocket::async_teardown; - BOOST_ASSERT(ws_.wr_block_.is_locked(this)); - BOOST_ASIO_CORO_YIELD - async_teardown(ws_.role_, - ws_.stream_, std::move(*this)); - BOOST_ASSERT(ws_.wr_block_.is_locked(this)); - if(ec == net::error::eof) - { - // Rationale: - // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error - ec.assign(0, ec.category()); - } - if(! ec) - ec = result_; - if(ec && ec != error::closed) - ws_.status_ = status::failed; - else - ws_.status_ = status::closed; - ws_.close(); - - upcall: - ws_.rd_block_.try_unlock(this); - ws_.paused_r_close_.maybe_invoke(); - if(ws_.wr_block_.try_unlock(this)) - ws_.paused_close_.maybe_invoke() || - ws_.paused_ping_.maybe_invoke() || - ws_.paused_wr_.maybe_invoke(); - if(! cont_) - { - BOOST_ASIO_CORO_YIELD - net::post( - ws_.get_executor(), - beast::bind_front_handler(std::move(*this), - ec, bytes_written_)); - } - this->invoke(ec, bytes_written_); - } -} - -//------------------------------------------------------------------------------ - -template -template< - class DynamicBuffer, - class Handler> -class stream::read_op - : public beast::async_op_base< - Handler, beast::detail::get_executor_type> - , public net::coroutine -{ - stream& ws_; - DynamicBuffer& b_; - std::size_t limit_; - std::size_t bytes_written_ = 0; - bool some_; - -public: - template - read_op( - Handler_&& h, - stream& ws, - DynamicBuffer& b, - std::size_t limit, - bool some) - : async_op_base< - Handler, beast::detail::get_executor_type>( - std::forward(h), ws.get_executor()) - , ws_(ws) - , b_(b) - , limit_(limit ? limit : ( - std::numeric_limits::max)()) - , some_(some) - { - } - - void operator()( - error_code ec = {}, - std::size_t bytes_transferred = 0) - { - using beast::detail::clamp; - BOOST_ASIO_CORO_REENTER(*this) - { - do - { - BOOST_ASIO_CORO_YIELD - { - auto mb = beast::detail::dynamic_buffer_prepare(b_, - clamp(ws_.read_size_hint(b_), limit_), - ec, error::buffer_overflow); - if(ec) - net::post( - ws_.get_executor(), - beast::bind_front_handler( - std::move(*this), ec, 0)); - else - read_some_op(std::move(*this), ws_, *mb)( - {}, 0, false); - } - if(ec) - goto upcall; - b_.commit(bytes_transferred); - bytes_written_ += bytes_transferred; - } - while(! some_ && ! ws_.is_message_done()); - upcall: - this->invoke(ec, bytes_written_); - } - } -}; - -//------------------------------------------------------------------------------ - -template -template -std::size_t -stream:: -read(DynamicBuffer& buffer) -{ - static_assert(is_sync_stream::value, - "SyncStream requirements not met"); - static_assert( - net::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - error_code ec; - auto const bytes_written = read(buffer, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); - return bytes_written; -} - -template -template -std::size_t -stream:: -read(DynamicBuffer& buffer, error_code& ec) -{ - static_assert(is_sync_stream::value, - "SyncStream requirements not met"); - static_assert( - net::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - std::size_t bytes_written = 0; - do - { - bytes_written += read_some(buffer, 0, ec); - if(ec) - return bytes_written; - } - while(! is_message_done()); - return bytes_written; -} - -template -template -BOOST_ASIO_INITFN_RESULT_TYPE( - ReadHandler, void(error_code, std::size_t)) -stream:: -async_read(DynamicBuffer& buffer, ReadHandler&& handler) -{ - static_assert(is_async_stream::value, - "AsyncStream requirements not met"); - static_assert( - net::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - BOOST_BEAST_HANDLER_INIT( - ReadHandler, void(error_code, std::size_t)); - read_op< - DynamicBuffer, - BOOST_ASIO_HANDLER_TYPE( - ReadHandler, void(error_code, std::size_t))>{ - std::move(init.completion_handler), - *this, - buffer, - 0, - false}(); - return init.result.get(); -} - -//------------------------------------------------------------------------------ - -template -template -std::size_t -stream:: -read_some( - DynamicBuffer& buffer, - std::size_t limit) -{ - static_assert(is_sync_stream::value, - "SyncStream requirements not met"); - static_assert( - net::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - error_code ec; - auto const bytes_written = - read_some(buffer, limit, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); - return bytes_written; -} - -template -template -std::size_t -stream:: -read_some( - DynamicBuffer& buffer, - std::size_t limit, - error_code& ec) -{ - static_assert(is_sync_stream::value, - "SyncStream requirements not met"); - static_assert( - net::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - using beast::detail::clamp; - if(! limit) - limit = (std::numeric_limits::max)(); - auto const size = - clamp(read_size_hint(buffer), limit); - BOOST_ASSERT(size > 0); - auto mb = beast::detail::dynamic_buffer_prepare( - buffer, size, ec, error::buffer_overflow); - if(ec) - return 0; - auto const bytes_written = read_some(*mb, ec); - buffer.commit(bytes_written); - return bytes_written; -} - -template -template -BOOST_ASIO_INITFN_RESULT_TYPE( - ReadHandler, void(error_code, std::size_t)) -stream:: -async_read_some( - DynamicBuffer& buffer, - std::size_t limit, - ReadHandler&& handler) -{ - static_assert(is_async_stream::value, - "AsyncStream requirements not met"); - static_assert( - net::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - BOOST_BEAST_HANDLER_INIT( - ReadHandler, void(error_code, std::size_t)); - read_op< - DynamicBuffer, - BOOST_ASIO_HANDLER_TYPE( - ReadHandler, void(error_code, std::size_t))>{ - std::move(init.completion_handler), - *this, - buffer, - limit, - true}({}, 0); - return init.result.get(); -} - -//------------------------------------------------------------------------------ - -template -template -std::size_t -stream:: -read_some( - MutableBufferSequence const& buffers) -{ - static_assert(is_sync_stream::value, - "SyncStream requirements not met"); - static_assert(net::is_mutable_buffer_sequence< - MutableBufferSequence>::value, - "MutableBufferSequence requirements not met"); - error_code ec; - auto const bytes_written = read_some(buffers, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); - return bytes_written; -} - -template -template -std::size_t -stream:: -read_some( - MutableBufferSequence const& buffers, - error_code& ec) -{ - static_assert(is_sync_stream::value, - "SyncStream requirements not met"); - static_assert(net::is_mutable_buffer_sequence< - MutableBufferSequence>::value, - "MutableBufferSequence requirements not met"); - using beast::detail::clamp; - using net::buffer; - using net::buffer_size; - close_code code{}; - std::size_t bytes_written = 0; - ec.assign(0, ec.category()); - // Make sure the stream is open - if(! check_open(ec)) - return 0; -loop: - // See if we need to read a frame header. This - // condition is structured to give the decompressor - // a chance to emit the final empty deflate block - // - if(rd_remain_ == 0 && (! rd_fh_.fin || rd_done_)) - { - // Read frame header - error_code result; - while(! parse_fh(rd_fh_, rd_buf_, result)) - { - if(result) - { - // _Fail the WebSocket Connection_ - if(result == error::message_too_big) - code = close_code::too_big; - else - code = close_code::protocol_error; - do_fail(code, result, ec); - return bytes_written; - } - auto const bytes_transferred = - stream_.read_some( - rd_buf_.prepare(read_size( - rd_buf_, rd_buf_.max_size())), - ec); - if(! check_ok(ec)) - return bytes_written; - rd_buf_.commit(bytes_transferred); - } - // Immediately apply the mask to the portion - // of the buffer holding payload data. - if(rd_fh_.len > 0 && rd_fh_.mask) - detail::mask_inplace(buffers_prefix( - clamp(rd_fh_.len), rd_buf_.data()), - rd_key_); - if(detail::is_control(rd_fh_.op)) - { - // Get control frame payload - auto const b = buffers_prefix( - clamp(rd_fh_.len), rd_buf_.data()); - auto const len = buffer_size(b); - BOOST_ASSERT(len == rd_fh_.len); - - // Clear this otherwise the next - // frame will be considered final. - rd_fh_.fin = false; - - // Handle ping frame - if(rd_fh_.op == detail::opcode::ping) - { - ping_data payload; - detail::read_ping(payload, b); - rd_buf_.consume(len); - if(wr_close_) - { - // Ignore ping when closing - goto loop; - } - if(ctrl_cb_) - ctrl_cb_(frame_type::ping, payload); - detail::frame_buffer fb; - write_ping(fb, - detail::opcode::pong, payload); - net::write(stream_, fb.data(), ec); - if(! check_ok(ec)) - return bytes_written; - goto loop; - } - // Handle pong frame - if(rd_fh_.op == detail::opcode::pong) - { - ping_data payload; - detail::read_ping(payload, b); - rd_buf_.consume(len); - if(ctrl_cb_) - ctrl_cb_(frame_type::pong, payload); - goto loop; - } - // Handle close frame - BOOST_ASSERT(rd_fh_.op == detail::opcode::close); - { - BOOST_ASSERT(! rd_close_); - rd_close_ = true; - close_reason cr; - detail::read_close(cr, b, result); - if(result) - { - // _Fail the WebSocket Connection_ - do_fail(close_code::protocol_error, - result, ec); - return bytes_written; - } - cr_ = cr; - rd_buf_.consume(len); - if(ctrl_cb_) - ctrl_cb_(frame_type::close, cr_.reason); - BOOST_ASSERT(! wr_close_); - // _Start the WebSocket Closing Handshake_ - do_fail( - cr.code == close_code::none ? - close_code::normal : - static_cast(cr.code), - error::closed, ec); - return bytes_written; - } - } - if(rd_fh_.len == 0 && ! rd_fh_.fin) - { - // Empty non-final frame - goto loop; - } - rd_done_ = false; - } - else - { - ec.assign(0, ec.category()); - } - if(! this->rd_deflated()) - { - if(rd_remain_ > 0) - { - if(rd_buf_.size() == 0 && rd_buf_.max_size() > - (std::min)(clamp(rd_remain_), - buffer_size(buffers))) - { - // Fill the read buffer first, otherwise we - // get fewer bytes at the cost of one I/O. - rd_buf_.commit(stream_.read_some( - rd_buf_.prepare(read_size(rd_buf_, - rd_buf_.max_size())), ec)); - if(! check_ok(ec)) - return bytes_written; - if(rd_fh_.mask) - detail::mask_inplace( - buffers_prefix(clamp(rd_remain_), - rd_buf_.data()), rd_key_); - } - if(rd_buf_.size() > 0) - { - // Copy from the read buffer. - // The mask was already applied. - auto const bytes_transferred = - net::buffer_copy(buffers, - rd_buf_.data(), clamp(rd_remain_)); - auto const mb = buffers_prefix( - bytes_transferred, buffers); - rd_remain_ -= bytes_transferred; - if(rd_op_ == detail::opcode::text) - { - if(! rd_utf8_.write(mb) || - (rd_remain_ == 0 && rd_fh_.fin && - ! rd_utf8_.finish())) - { - // _Fail the WebSocket Connection_ - do_fail(close_code::bad_payload, - error::bad_frame_payload, ec); - return bytes_written; - } - } - bytes_written += bytes_transferred; - rd_size_ += bytes_transferred; - rd_buf_.consume(bytes_transferred); - } - else - { - // Read into caller's buffer - BOOST_ASSERT(rd_remain_ > 0); - BOOST_ASSERT(buffer_size(buffers) > 0); - BOOST_ASSERT(buffer_size(buffers_prefix( - clamp(rd_remain_), buffers)) > 0); - auto const bytes_transferred = - stream_.read_some(buffers_prefix( - clamp(rd_remain_), buffers), ec); - if(! check_ok(ec)) - return bytes_written; - BOOST_ASSERT(bytes_transferred > 0); - auto const mb = buffers_prefix( - bytes_transferred, buffers); - rd_remain_ -= bytes_transferred; - if(rd_fh_.mask) - detail::mask_inplace(mb, rd_key_); - if(rd_op_ == detail::opcode::text) - { - if(! rd_utf8_.write(mb) || - (rd_remain_ == 0 && rd_fh_.fin && - ! rd_utf8_.finish())) - { - // _Fail the WebSocket Connection_ - do_fail(close_code::bad_payload, - error::bad_frame_payload, ec); - return bytes_written; - } - } - bytes_written += bytes_transferred; - rd_size_ += bytes_transferred; - } - } - rd_done_ = rd_remain_ == 0 && rd_fh_.fin; - } - else - { - // Read compressed message frame payload: - // inflate even if rd_fh_.len == 0, otherwise we - // never emit the end-of-stream deflate block. - // - bool did_read = false; - buffers_suffix cb{buffers}; - while(buffer_size(cb) > 0) - { - zlib::z_params zs; - { - auto const out = buffers_front(cb); - zs.next_out = out.data(); - zs.avail_out = out.size(); - BOOST_ASSERT(zs.avail_out > 0); - } - if(rd_remain_ > 0) - { - if(rd_buf_.size() > 0) - { - // use what's there - auto const in = buffers_prefix( - clamp(rd_remain_), buffers_front( - rd_buf_.data())); - zs.avail_in = in.size(); - zs.next_in = in.data(); - } - else if(! did_read) - { - // read new - auto const bytes_transferred = - stream_.read_some( - rd_buf_.prepare(read_size( - rd_buf_, rd_buf_.max_size())), - ec); - if(! check_ok(ec)) - return bytes_written; - BOOST_ASSERT(bytes_transferred > 0); - rd_buf_.commit(bytes_transferred); - if(rd_fh_.mask) - detail::mask_inplace( - buffers_prefix(clamp(rd_remain_), - rd_buf_.data()), rd_key_); - auto const in = buffers_prefix( - clamp(rd_remain_), buffers_front( - rd_buf_.data())); - zs.avail_in = in.size(); - zs.next_in = in.data(); - did_read = true; - } - else - { - break; - } - } - else if(rd_fh_.fin) - { - // append the empty block codes - static std::uint8_t constexpr - empty_block[4] = { - 0x00, 0x00, 0xff, 0xff }; - zs.next_in = empty_block; - zs.avail_in = sizeof(empty_block); - this->inflate(zs, zlib::Flush::sync, ec); - if(! ec) - { - // https://github.com/madler/zlib/issues/280 - if(zs.total_out > 0) - ec = error::partial_deflate_block; - } - if(! check_ok(ec)) - return bytes_written; - this->do_context_takeover_read(role_); - rd_done_ = true; - break; - } - else - { - break; - } - this->inflate(zs, zlib::Flush::sync, ec); - if(! check_ok(ec)) - return bytes_written; - if(rd_msg_max_ && beast::detail::sum_exceeds( - rd_size_, zs.total_out, rd_msg_max_)) - { - do_fail(close_code::too_big, - error::message_too_big, ec); - return bytes_written; - } - cb.consume(zs.total_out); - rd_size_ += zs.total_out; - rd_remain_ -= zs.total_in; - rd_buf_.consume(zs.total_in); - bytes_written += zs.total_out; - } - if(rd_op_ == detail::opcode::text) - { - // check utf8 - if(! rd_utf8_.write( - buffers_prefix(bytes_written, buffers)) || ( - rd_done_ && ! rd_utf8_.finish())) - { - // _Fail the WebSocket Connection_ - do_fail(close_code::bad_payload, - error::bad_frame_payload, ec); - return bytes_written; - } - } - } - return bytes_written; -} - -template -template -BOOST_ASIO_INITFN_RESULT_TYPE( - ReadHandler, void(error_code, std::size_t)) -stream:: -async_read_some( - MutableBufferSequence const& buffers, - ReadHandler&& handler) -{ - static_assert(is_async_stream::value, - "AsyncStream requirements not met"); - static_assert(net::is_mutable_buffer_sequence< - MutableBufferSequence>::value, - "MutableBufferSequence requirements not met"); - BOOST_BEAST_HANDLER_INIT( - ReadHandler, void(error_code, std::size_t)); - read_some_op{ - std::move(init.completion_handler), *this, buffers}( - {}, 0, false); - return init.result.get(); -} - -} // websocket -} // beast -} // boost - -#endif diff --git a/include/boost/beast/websocket/impl/rfc6455.ipp b/include/boost/beast/websocket/impl/rfc6455.hpp similarity index 90% rename from include/boost/beast/websocket/impl/rfc6455.ipp rename to include/boost/beast/websocket/impl/rfc6455.hpp index 07fdc306..b947cd4d 100644 --- a/include/boost/beast/websocket/impl/rfc6455.ipp +++ b/include/boost/beast/websocket/impl/rfc6455.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_WEBSOCKET_IMPL_RFC6455_IPP -#define BOOST_BEAST_WEBSOCKET_IMPL_RFC6455_IPP +#ifndef BOOST_BEAST_WEBSOCKET_IMPL_RFC6455_HPP +#define BOOST_BEAST_WEBSOCKET_IMPL_RFC6455_HPP #include #include diff --git a/include/boost/beast/websocket/impl/ssl.ipp b/include/boost/beast/websocket/impl/ssl.hpp similarity index 92% rename from include/boost/beast/websocket/impl/ssl.ipp rename to include/boost/beast/websocket/impl/ssl.hpp index 4338386e..7e786888 100644 --- a/include/boost/beast/websocket/impl/ssl.ipp +++ b/include/boost/beast/websocket/impl/ssl.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED -#define BOOST_BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED +#ifndef BOOST_BEAST_WEBSOCKET_IMPL_SSL_HPP +#define BOOST_BEAST_WEBSOCKET_IMPL_SSL_HPP #include diff --git a/include/boost/beast/websocket/impl/stream.ipp b/include/boost/beast/websocket/impl/stream.hpp similarity index 73% rename from include/boost/beast/websocket/impl/stream.ipp rename to include/boost/beast/websocket/impl/stream.hpp index f6584a9d..430d8921 100644 --- a/include/boost/beast/websocket/impl/stream.ipp +++ b/include/boost/beast/websocket/impl/stream.hpp @@ -7,13 +7,13 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_WEBSOCKET_IMPL_STREAM_IPP -#define BOOST_BEAST_WEBSOCKET_IMPL_STREAM_IPP +#ifndef BOOST_BEAST_WEBSOCKET_IMPL_STREAM_HPP +#define BOOST_BEAST_WEBSOCKET_IMPL_STREAM_HPP #include #include #include -#include +#include #include #include #include @@ -25,17 +25,18 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include #include #include -#include - namespace boost { namespace beast { namespace websocket { @@ -44,12 +45,101 @@ template template stream:: stream(Args&&... args) - : stream_(std::forward(args)...) + : impl_(std::make_shared( + std::forward(args)...)) { - BOOST_ASSERT(rd_buf_.max_size() >= + BOOST_ASSERT(impl_->rd_buf.max_size() >= max_control_frame_size); } +template +auto +stream:: +get_executor() const noexcept -> + executor_type +{ + return impl_->stream.get_executor(); +} + +template +auto +stream:: +next_layer() noexcept -> + next_layer_type& +{ + return impl_->stream; +} + +template +auto +stream:: +next_layer() const noexcept -> + next_layer_type const& +{ + return impl_->stream; +} + +template +auto +stream:: +lowest_layer() noexcept -> + lowest_layer_type& +{ + return impl_->stream.lowest_layer(); +} + +template +auto +stream:: +lowest_layer() const noexcept -> + lowest_layer_type const& +{ + return impl_->stream.lowest_layer(); +} + +template +bool +stream:: +is_open() const noexcept +{ + return impl_->status_ == status::open; +} + +template +bool +stream:: +got_binary() const noexcept +{ + return impl_->rd_op == detail::opcode::binary; +} + +template +bool +stream:: +is_message_done() const noexcept +{ + return impl_->rd_done; +} + +template +close_reason const& +stream:: +reason() const noexcept +{ + return impl_->cr; +} + +template +std::size_t +stream:: +read_size_hint( + std::size_t initial_size) const +{ + return impl_->read_size_hint_pmd( + initial_size, impl_->rd_done, + impl_->rd_remain, impl_->rd_fh); +} + template template std::size_t @@ -72,88 +162,129 @@ read_size_hint(DynamicBuffer& buffer) const template void stream:: -open(role_type role) +set_option(permessage_deflate const& o) { - // VFALCO TODO analyze and remove dupe code in reset() - role_ = role; - status_ = status::open; - rd_remain_ = 0; - rd_cont_ = false; - rd_done_ = true; - // Can't clear this because accept uses it - //rd_buf_.reset(); - rd_fh_.fin = false; - rd_close_ = false; - wr_close_ = false; - // These should not be necessary, because all completion - // handlers must be allowed to execute otherwise the - // stream exhibits undefined behavior. - wr_block_.reset(); - rd_block_.reset(); - cr_.code = close_code::none; - - wr_cont_ = false; - wr_buf_size_ = 0; - - this->open_pmd(role_); + impl_->set_option_pmd(o); } template void stream:: -close() +get_option(permessage_deflate& o) { - wr_buf_.reset(); - this->close_pmd(); + impl_->get_option_pmd(o); } template void stream:: -reset() +auto_fragment(bool value) { - BOOST_ASSERT(status_ != status::open); - rd_remain_ = 0; - rd_cont_ = false; - rd_done_ = true; - rd_buf_.consume(rd_buf_.size()); - rd_fh_.fin = false; - rd_close_ = false; - wr_close_ = false; - wr_cont_ = false; - // These should not be necessary, because all completion - // handlers must be allowed to execute otherwise the - // stream exhibits undefined behavior. - wr_block_.reset(); - rd_block_.reset(); - cr_.code = close_code::none; + impl_->wr_frag_opt = value; +} + +template +bool +stream:: +auto_fragment() const +{ + return impl_->wr_frag_opt; } -// Called before each write frame template -inline void stream:: -begin_msg() +binary(bool value) { - wr_frag_ = wr_frag_opt_; + impl_->wr_opcode = value ? + detail::opcode::binary : + detail::opcode::text; +} - // Maintain the write buffer - if( this->pmd_enabled() || - role_ == role_type::client) - { - if(! wr_buf_ || wr_buf_size_ != wr_buf_opt_) - { - wr_buf_size_ = wr_buf_opt_; - wr_buf_ = boost::make_unique_noinit< - std::uint8_t[]>(wr_buf_size_); - } - } - else - { - wr_buf_size_ = wr_buf_opt_; - wr_buf_.reset(); - } +template +bool +stream:: +binary() const +{ + return impl_->wr_opcode == detail::opcode::binary; +} + +template +void +stream:: +control_callback(std::function< + void(frame_type, string_view)> cb) +{ + impl_->ctrl_cb = std::move(cb); +} + +template +void +stream:: +control_callback() +{ + impl_->ctrl_cb = {}; +} + +template +void +stream:: +read_message_max(std::size_t amount) +{ + impl_->rd_msg_max = amount; +} + +template +std::size_t +stream:: +read_message_max() const +{ + return impl_->rd_msg_max; +} + +template +void +stream:: +secure_prng(bool value) +{ + this->secure_prng_ = value; +} + +template +void +stream:: +write_buffer_size(std::size_t amount) +{ + if(amount < 8) + BOOST_THROW_EXCEPTION(std::invalid_argument{ + "write buffer size underflow"}); + impl_->wr_buf_opt = amount; +} + +template +std::size_t +stream:: +write_buffer_size() const +{ + return impl_->wr_buf_opt; +} + +template +void +stream:: +text(bool value) +{ + impl_->wr_opcode = value ? + detail::opcode::text : + detail::opcode::binary; +} + +template +bool +stream:: +text() const +{ + return impl_->wr_opcode == detail::opcode::text; } //------------------------------------------------------------------------------ @@ -175,7 +306,7 @@ parse_fh( if(buffer_size(b.data()) < 2) { // need more bytes - ec.assign(0, ec.category()); + ec = {}; return false; } buffers_suffixrd_cont) { // new data frame when continuation expected ec = error::bad_data_frame; return false; } if(fh.rsv2 || fh.rsv3 || - ! this->rd_deflated(fh.rsv1)) + ! impl_->rd_deflated(fh.rsv1)) { // reserved bits not cleared ec = error::bad_reserved_bits; @@ -229,7 +360,7 @@ parse_fh( break; case detail::opcode::cont: - if(! rd_cont_) + if(! impl_->rd_cont) { // continuation without an active message ec = error::bad_continuation; @@ -270,13 +401,13 @@ parse_fh( } break; } - if(role_ == role_type::server && ! fh.mask) + if(impl_->role == role_type::server && ! fh.mask) { // unmasked frame from client ec = error::bad_unmasked_frame; return false; } - if(role_ == role_type::client && fh.mask) + if(impl_->role == role_type::client && fh.mask) { // masked frame from server ec = error::bad_masked_frame; @@ -326,7 +457,7 @@ parse_fh( BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp)); cb.consume(buffer_copy(buffer(tmp), cb)); fh.key = detail::little_uint32_to_native(&tmp[0]); - detail::prepare_key(rd_key_, fh.key); + detail::prepare_key(impl_->rd_key, fh.key); } else { @@ -337,12 +468,12 @@ parse_fh( { if(fh.op != detail::opcode::cont) { - rd_size_ = 0; - rd_op_ = fh.op; + impl_->rd_size = 0; + impl_->rd_op = fh.op; } else { - if(rd_size_ > (std::numeric_limits< + if(impl_->rd_size > (std::numeric_limits< std::uint64_t>::max)() - fh.len) { // message size exceeds configured limit @@ -350,21 +481,21 @@ parse_fh( return false; } } - if(! this->rd_deflated()) + if(! impl_->rd_deflated()) { - if(rd_msg_max_ && beast::detail::sum_exceeds( - rd_size_, fh.len, rd_msg_max_)) + if(impl_->rd_msg_max && beast::detail::sum_exceeds( + impl_->rd_size, fh.len, impl_->rd_msg_max)) { // message size exceeds configured limit ec = error::message_too_big; return false; } } - rd_cont_ = ! fh.fin; - rd_remain_ = fh.len; + impl_->rd_cont = ! fh.fin; + impl_->rd_remain = fh.len; } b.consume(b.size() - buffer_size(cb)); - ec.assign(0, ec.category()); + ec = {}; return true; } @@ -383,7 +514,7 @@ write_close(DynamicBuffer& db, close_reason const& cr) fh.rsv3 = false; fh.len = cr.code == close_code::none ? 0 : 2 + cr.reason.size(); - if(role_ == role_type::client) + if(impl_->role == role_type::client) { fh.mask = true; fh.key = this->create_mask(); @@ -436,7 +567,7 @@ write_ping(DynamicBuffer& db, fh.rsv2 = false; fh.rsv3 = false; fh.len = data.size(); - fh.mask = role_ == role_type::client; + fh.mask = impl_->role == role_type::client; if(fh.mask) fh.key = this->create_mask(); detail::write(db, fh); @@ -474,7 +605,7 @@ build_request(detail::sec_ws_key_type& key, detail::make_sec_ws_key(key); req.set(http::field::sec_websocket_key, key); req.set(http::field::sec_websocket_version, "13"); - this->build_request_pmd(req); + impl_->build_request_pmd(req); decorator(req); if(! req.count(http::field::user_agent)) req.set(http::field::user_agent, @@ -572,7 +703,7 @@ build_response( detail::make_sec_ws_accept(acc, key); res.set(http::field::sec_websocket_accept, acc); } - this->build_response_pmd(res, req); + impl_->build_response_pmd(res, req); decorate(res); result = {}; return res; @@ -620,9 +751,9 @@ on_response( return err(error::bad_sec_accept); } - ec.assign(0, ec.category()); - this->on_response_pmd(res); - open(role_type::client); + ec = {}; + impl_->on_response_pmd(res); + impl_->open(role_type::client); } // _Fail the WebSocket Connection_ @@ -635,32 +766,32 @@ do_fail( error_code& ec) // set to the error, else set to ev { BOOST_ASSERT(ev); - status_ = status::closing; - if(code != close_code::none && ! wr_close_) + impl_->status_ = status::closing; + if(code != close_code::none && ! impl_->wr_close) { - wr_close_ = true; + impl_->wr_close = true; detail::frame_buffer fb; write_close< flat_static_buffer_base>(fb, code); - net::write(stream_, fb.data(), ec); - if(! check_ok(ec)) + net::write(impl_->stream, fb.data(), ec); + if(! impl_->check_ok(ec)) return; } using beast::websocket::teardown; - teardown(role_, stream_, ec); + teardown(impl_->role, impl_->stream, ec); if(ec == net::error::eof) { // Rationale: // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error - ec.assign(0, ec.category()); + ec = {}; } if(! ec) ec = ev; if(ec && ec != error::closed) - status_ = status::failed; + impl_->status_ = status::failed; else - status_ = status::closed; - close(); + impl_->status_ = status::closed; + impl_->close(); } } // websocket diff --git a/include/boost/beast/websocket/impl/stream_impl.hpp b/include/boost/beast/websocket/impl/stream_impl.hpp new file mode 100644 index 00000000..096f4264 --- /dev/null +++ b/include/boost/beast/websocket/impl/stream_impl.hpp @@ -0,0 +1,199 @@ +// +// Copyright (c) 2016-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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_WEBSOCKET_IMPL_STREAM_IMPL_HPP +#define BOOST_BEAST_WEBSOCKET_IMPL_STREAM_IMPL_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace websocket { + +template< + class NextLayer, bool deflateSupported> +struct stream::impl_type + : std::enable_shared_from_this + , detail::impl_base +{ + using time_point = typename + std::chrono::steady_clock::time_point; + + static constexpr time_point never() + { + return (time_point::max)(); + } + + NextLayer stream; // The underlying stream + close_reason cr; // set from received close frame + control_cb_type ctrl_cb; // control callback + + std::size_t rd_msg_max /* max message size */ = 16 * 1024 * 1024; + std::uint64_t rd_size /* total size of current message so far */ = 0; + std::uint64_t rd_remain /* message frame bytes left in current frame */ = 0; + detail::frame_header rd_fh; // current frame header + detail::prepared_key rd_key; // current stateful mask key + detail::frame_buffer rd_fb; // to write control frames (during reads) + detail::utf8_checker rd_utf8; // to validate utf8 + static_buffer< + +tcp_frame_size> rd_buf; // buffer for reads + detail::opcode rd_op /* current message binary or text */ = detail::opcode::text; + bool rd_cont /* `true` if the next frame is a continuation */ = false; + bool rd_done /* set when a message is done */ = true; + bool rd_close /* did we read a close frame? */ = false; + detail::soft_mutex rd_block; // op currently reading + + role_type role /* server or client */ = role_type::client; + status status_ /* state of the object */ = status::closed; + + detail::soft_mutex wr_block; // op currently writing + bool wr_close /* did we write a close frame? */ = false; + bool wr_cont /* next write is a continuation */ = false; + bool wr_frag /* autofrag the current message */ = false; + bool wr_frag_opt /* autofrag option setting */ = true; + bool wr_compress /* compress current message */ = false; + detail::opcode wr_opcode /* message type */ = detail::opcode::text; + std::unique_ptr< + std::uint8_t[]> wr_buf; // write buffer + std::size_t wr_buf_size /* write buffer size (current message) */ = 0; + std::size_t wr_buf_opt /* write buffer size option setting */ = 4096; + detail::fh_buffer wr_fb; // header buffer used for writes + + saved_handler paused_rd; // paused read op + saved_handler paused_wr; // paused write op + saved_handler paused_ping; // paused ping op + saved_handler paused_close; // paused close op + saved_handler paused_r_rd; // paused read op (async read) + saved_handler paused_r_close; // paused close op (async read) + + enum + { + }; + + template + impl_type(Args&&... args) + : stream(std::forward(args)...) + { + } + + void + open(role_type role_) + { + // VFALCO TODO analyze and remove dupe code in reset() + role = role_; + status_ = status::open; + rd_remain = 0; + rd_cont = false; + rd_done = true; + // Can't clear this because accept uses it + //rd_buf.reset(); + rd_fh.fin = false; + rd_close = false; + wr_close = false; + // These should not be necessary, because all completion + // handlers must be allowed to execute otherwise the + // stream exhibits undefined behavior. + wr_block.reset(); + rd_block.reset(); + cr.code = close_code::none; + + wr_cont = false; + wr_buf_size = 0; + + this->open_pmd(role); + } + + void + close() + { + wr_buf.reset(); + this->close_pmd(); + } + + void + reset() + { + BOOST_ASSERT(status_ != status::open); + rd_remain = 0; + rd_cont = false; + rd_done = true; + rd_buf.consume(rd_buf.size()); + rd_fh.fin = false; + rd_close = false; + wr_close = false; + wr_cont = false; + // These should not be necessary, because all completion + // handlers must be allowed to execute otherwise the + // stream exhibits undefined behavior. + wr_block.reset(); + rd_block.reset(); + cr.code = close_code::none; + } + + // Called before each write frame + void + begin_msg() + { + wr_frag = wr_frag_opt; + + // Maintain the write buffer + if( this->pmd_enabled() || + role == role_type::client) + { + if(! wr_buf || + wr_buf_size != wr_buf_opt) + { + wr_buf_size = wr_buf_opt; + wr_buf = boost::make_unique_noinit< + std::uint8_t[]>(wr_buf_size); + } + } + else + { + wr_buf_size = wr_buf_opt; + wr_buf.reset(); + } + } + + bool + check_open(error_code& ec) + { + if(status_ != status::open) + { + ec = net::error::operation_aborted; + return false; + } + ec = {}; + return true; + } + + bool + check_ok(error_code& ec) + { + if(ec) + { + if(status_ != status::closed) + status_ = status::failed; + return false; + } + return true; + } +}; + +} // websocket +} // beast +} // boost + +#endif diff --git a/include/boost/beast/websocket/impl/teardown.ipp b/include/boost/beast/websocket/impl/teardown.hpp similarity index 98% rename from include/boost/beast/websocket/impl/teardown.ipp rename to include/boost/beast/websocket/impl/teardown.hpp index ff014169..6e627d4c 100644 --- a/include/boost/beast/websocket/impl/teardown.ipp +++ b/include/boost/beast/websocket/impl/teardown.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP -#define BOOST_BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP +#ifndef BOOST_BEAST_WEBSOCKET_IMPL_TEARDOWN_HPP +#define BOOST_BEAST_WEBSOCKET_IMPL_TEARDOWN_HPP #include #include diff --git a/include/boost/beast/websocket/impl/write.ipp b/include/boost/beast/websocket/impl/write.hpp similarity index 67% rename from include/boost/beast/websocket/impl/write.ipp rename to include/boost/beast/websocket/impl/write.hpp index b2750927..9e60fcf8 100644 --- a/include/boost/beast/websocket/impl/write.ipp +++ b/include/boost/beast/websocket/impl/write.hpp @@ -7,9 +7,10 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_WEBSOCKET_IMPL_WRITE_IPP -#define BOOST_BEAST_WEBSOCKET_IMPL_WRITE_IPP +#ifndef BOOST_BEAST_WEBSOCKET_IMPL_WRITE_HPP +#define BOOST_BEAST_WEBSOCKET_IMPL_WRITE_HPP +#include #include #include #include @@ -33,106 +34,6 @@ namespace boost { namespace beast { namespace websocket { -namespace detail { - -// Compress a buffer sequence -// Returns: `true` if more calls are needed -// -template<> -template -bool -stream_base:: -deflate( - net::mutable_buffer& out, - buffers_suffix& cb, - bool fin, - std::size_t& total_in, - error_code& ec) -{ - using net::buffer; - BOOST_ASSERT(out.size() >= 6); - auto& zo = this->pmd_->zo; - zlib::z_params zs; - zs.avail_in = 0; - zs.next_in = nullptr; - zs.avail_out = out.size(); - zs.next_out = out.data(); - for(auto in : beast::buffers_range_ref(cb)) - { - zs.avail_in = in.size(); - if(zs.avail_in == 0) - continue; - zs.next_in = in.data(); - zo.write(zs, zlib::Flush::none, ec); - if(ec) - { - if(ec != zlib::error::need_buffers) - return false; - BOOST_ASSERT(zs.avail_out == 0); - BOOST_ASSERT(zs.total_out == out.size()); - ec.assign(0, ec.category()); - break; - } - if(zs.avail_out == 0) - { - BOOST_ASSERT(zs.total_out == out.size()); - break; - } - BOOST_ASSERT(zs.avail_in == 0); - } - total_in = zs.total_in; - cb.consume(zs.total_in); - if(zs.avail_out > 0 && fin) - { - auto const remain = net::buffer_size(cb); - if(remain == 0) - { - // Inspired by Mark Adler - // https://github.com/madler/zlib/issues/149 - // - // VFALCO We could do this flush twice depending - // on how much space is in the output. - zo.write(zs, zlib::Flush::block, ec); - BOOST_ASSERT(! ec || ec == zlib::error::need_buffers); - if(ec == zlib::error::need_buffers) - ec.assign(0, ec.category()); - if(ec) - return false; - if(zs.avail_out >= 6) - { - zo.write(zs, zlib::Flush::full, ec); - BOOST_ASSERT(! ec); - // remove flush marker - zs.total_out -= 4; - out = buffer(out.data(), zs.total_out); - return false; - } - } - } - ec.assign(0, ec.category()); - out = buffer(out.data(), zs.total_out); - return true; -} - -template<> -inline -void -stream_base:: -do_context_takeover_write(role_type role) -{ - if((role == role_type::client && - this->pmd_config_.client_no_context_takeover) || - (role == role_type::server && - this->pmd_config_.server_no_context_takeover)) - { - this->pmd_->zo.reset(); - } -} - -} // detail - -//------------------------------------------------------------------------------ - template template class stream::write_some_op @@ -205,10 +106,10 @@ operator()( BOOST_ASIO_CORO_REENTER(*this) { // Set up the outgoing frame header - if(! ws_.wr_cont_) + if(! ws_.impl_->wr_cont) { - ws_.begin_msg(); - fh_.rsv1 = ws_.wr_compress_; + ws_.impl_->begin_msg(); + fh_.rsv1 = ws_.impl_->wr_compress; } else { @@ -216,27 +117,27 @@ operator()( } fh_.rsv2 = false; fh_.rsv3 = false; - fh_.op = ws_.wr_cont_ ? - detail::opcode::cont : ws_.wr_opcode_; + fh_.op = ws_.impl_->wr_cont ? + detail::opcode::cont : ws_.impl_->wr_opcode; fh_.mask = - ws_.role_ == role_type::client; + ws_.impl_->role == role_type::client; // Choose a write algorithm - if(ws_.wr_compress_) + if(ws_.impl_->wr_compress) { how_ = do_deflate; } else if(! fh_.mask) { - if(! ws_.wr_frag_) + if(! ws_.impl_->wr_frag) { how_ = do_nomask_nofrag; } else { - BOOST_ASSERT(ws_.wr_buf_size_ != 0); + BOOST_ASSERT(ws_.impl_->wr_buf_size != 0); remain_ = buffer_size(cb_); - if(remain_ > ws_.wr_buf_size_) + if(remain_ > ws_.impl_->wr_buf_size) how_ = do_nomask_frag; else how_ = do_nomask_nofrag; @@ -244,15 +145,15 @@ operator()( } else { - if(! ws_.wr_frag_) + if(! ws_.impl_->wr_frag) { how_ = do_mask_nofrag; } else { - BOOST_ASSERT(ws_.wr_buf_size_ != 0); + BOOST_ASSERT(ws_.impl_->wr_buf_size != 0); remain_ = buffer_size(cb_); - if(remain_ > ws_.wr_buf_size_) + if(remain_ > ws_.impl_->wr_buf_size) how_ = do_mask_frag; else how_ = do_mask_nofrag; @@ -260,10 +161,10 @@ operator()( } // Maybe suspend - if(ws_.wr_block_.try_lock(this)) + if(ws_.impl_->wr_block.try_lock(this)) { // Make sure the stream is open - if(! ws_.check_open(ec)) + if(! ws_.impl_->check_open(ec)) goto upcall; } else @@ -271,19 +172,19 @@ operator()( do_suspend: // Suspend BOOST_ASIO_CORO_YIELD - ws_.paused_wr_.emplace(std::move(*this)); + ws_.impl_->paused_wr.emplace(std::move(*this)); // Acquire the write block - ws_.wr_block_.lock(this); + ws_.impl_->wr_block.lock(this); // Resume BOOST_ASIO_CORO_YIELD net::post( ws_.get_executor(), std::move(*this)); - BOOST_ASSERT(ws_.wr_block_.is_locked(this)); + BOOST_ASSERT(ws_.impl_->wr_block.is_locked(this)); // Make sure the stream is open - if(! ws_.check_open(ec)) + if(! ws_.impl_->check_open(ec)) goto upcall; } @@ -293,16 +194,16 @@ operator()( { fh_.fin = fin_; fh_.len = buffer_size(cb_); - ws_.wr_fb_.clear(); + ws_.impl_->wr_fb.clear(); detail::write( - ws_.wr_fb_, fh_); - ws_.wr_cont_ = ! fin_; + ws_.impl_->wr_fb, fh_); + ws_.impl_->wr_cont = ! fin_; // Send frame BOOST_ASIO_CORO_YIELD - net::async_write(ws_.stream_, - buffers_cat(ws_.wr_fb_.data(), cb_), + net::async_write(ws_.impl_->stream, + buffers_cat(ws_.impl_->wr_fb.data(), cb_), std::move(*this)); - if(! ws_.check_ok(ec)) + if(! ws_.impl_->check_ok(ec)) goto upcall; bytes_transferred_ += clamp(fh_.len); goto upcall; @@ -314,22 +215,22 @@ operator()( { for(;;) { - n = clamp(remain_, ws_.wr_buf_size_); + n = clamp(remain_, ws_.impl_->wr_buf_size); fh_.len = n; remain_ -= n; fh_.fin = fin_ ? remain_ == 0 : false; - ws_.wr_fb_.clear(); + ws_.impl_->wr_fb.clear(); detail::write( - ws_.wr_fb_, fh_); - ws_.wr_cont_ = ! fin_; + ws_.impl_->wr_fb, fh_); + ws_.impl_->wr_cont = ! fin_; // Send frame BOOST_ASIO_CORO_YIELD net::async_write( - ws_.stream_, buffers_cat( - ws_.wr_fb_.data(), buffers_prefix( + ws_.impl_->stream, buffers_cat( + ws_.impl_->wr_fb.data(), buffers_prefix( clamp(fh_.len), cb_)), std::move(*this)); - if(! ws_.check_ok(ec)) + if(! ws_.impl_->check_ok(ec)) goto upcall; n = clamp(fh_.len); // because yield bytes_transferred_ += n; @@ -339,15 +240,15 @@ operator()( fh_.op = detail::opcode::cont; // Allow outgoing control frames to // be sent in between message frames - ws_.wr_block_.unlock(this); - if( ws_.paused_close_.maybe_invoke() || - ws_.paused_rd_.maybe_invoke() || - ws_.paused_ping_.maybe_invoke()) + ws_.impl_->wr_block.unlock(this); + if( ws_.impl_->paused_close.maybe_invoke() || + ws_.impl_->paused_rd.maybe_invoke() || + ws_.impl_->paused_ping.maybe_invoke()) { - BOOST_ASSERT(ws_.wr_block_.is_locked()); + BOOST_ASSERT(ws_.impl_->wr_block.is_locked()); goto do_suspend; } - ws_.wr_block_.lock(this); + ws_.impl_->wr_block.lock(this); } goto upcall; } @@ -361,41 +262,41 @@ operator()( fh_.len = remain_; fh_.key = ws_.create_mask(); detail::prepare_key(key_, fh_.key); - ws_.wr_fb_.clear(); + ws_.impl_->wr_fb.clear(); detail::write( - ws_.wr_fb_, fh_); - n = clamp(remain_, ws_.wr_buf_size_); + ws_.impl_->wr_fb, fh_); + n = clamp(remain_, ws_.impl_->wr_buf_size); buffer_copy(buffer( - ws_.wr_buf_.get(), n), cb_); + ws_.impl_->wr_buf.get(), n), cb_); detail::mask_inplace(buffer( - ws_.wr_buf_.get(), n), key_); + ws_.impl_->wr_buf.get(), n), key_); remain_ -= n; - ws_.wr_cont_ = ! fin_; + ws_.impl_->wr_cont = ! fin_; // Send frame header and partial payload BOOST_ASIO_CORO_YIELD net::async_write( - ws_.stream_, buffers_cat(ws_.wr_fb_.data(), - buffer(ws_.wr_buf_.get(), n)), + ws_.impl_->stream, buffers_cat(ws_.impl_->wr_fb.data(), + buffer(ws_.impl_->wr_buf.get(), n)), std::move(*this)); - if(! ws_.check_ok(ec)) + if(! ws_.impl_->check_ok(ec)) goto upcall; bytes_transferred_ += - bytes_transferred - ws_.wr_fb_.size(); + bytes_transferred - ws_.impl_->wr_fb.size(); while(remain_ > 0) { - cb_.consume(ws_.wr_buf_size_); - n = clamp(remain_, ws_.wr_buf_size_); + cb_.consume(ws_.impl_->wr_buf_size); + n = clamp(remain_, ws_.impl_->wr_buf_size); buffer_copy(buffer( - ws_.wr_buf_.get(), n), cb_); + ws_.impl_->wr_buf.get(), n), cb_); detail::mask_inplace(buffer( - ws_.wr_buf_.get(), n), key_); + ws_.impl_->wr_buf.get(), n), key_); remain_ -= n; // Send partial payload BOOST_ASIO_CORO_YIELD - net::async_write(ws_.stream_, - buffer(ws_.wr_buf_.get(), n), + net::async_write(ws_.impl_->stream, + buffer(ws_.impl_->wr_buf.get(), n), std::move(*this)); - if(! ws_.check_ok(ec)) + if(! ws_.impl_->check_ok(ec)) goto upcall; bytes_transferred_ += bytes_transferred; } @@ -408,29 +309,29 @@ operator()( { for(;;) { - n = clamp(remain_, ws_.wr_buf_size_); + n = clamp(remain_, ws_.impl_->wr_buf_size); remain_ -= n; fh_.len = n; fh_.key = ws_.create_mask(); fh_.fin = fin_ ? remain_ == 0 : false; detail::prepare_key(key_, fh_.key); buffer_copy(buffer( - ws_.wr_buf_.get(), n), cb_); + ws_.impl_->wr_buf.get(), n), cb_); detail::mask_inplace(buffer( - ws_.wr_buf_.get(), n), key_); - ws_.wr_fb_.clear(); + ws_.impl_->wr_buf.get(), n), key_); + ws_.impl_->wr_fb.clear(); detail::write( - ws_.wr_fb_, fh_); - ws_.wr_cont_ = ! fin_; + ws_.impl_->wr_fb, fh_); + ws_.impl_->wr_cont = ! fin_; // Send frame BOOST_ASIO_CORO_YIELD - net::async_write(ws_.stream_, - buffers_cat(ws_.wr_fb_.data(), - buffer(ws_.wr_buf_.get(), n)), + net::async_write(ws_.impl_->stream, + buffers_cat(ws_.impl_->wr_fb.data(), + buffer(ws_.impl_->wr_buf.get(), n)), std::move(*this)); - if(! ws_.check_ok(ec)) + if(! ws_.impl_->check_ok(ec)) goto upcall; - n = bytes_transferred - ws_.wr_fb_.size(); + n = bytes_transferred - ws_.impl_->wr_fb.size(); bytes_transferred_ += n; if(remain_ == 0) break; @@ -438,15 +339,15 @@ operator()( fh_.op = detail::opcode::cont; // Allow outgoing control frames to // be sent in between message frames: - ws_.wr_block_.unlock(this); - if( ws_.paused_close_.maybe_invoke() || - ws_.paused_rd_.maybe_invoke() || - ws_.paused_ping_.maybe_invoke()) + ws_.impl_->wr_block.unlock(this); + if( ws_.impl_->paused_close.maybe_invoke() || + ws_.impl_->paused_rd.maybe_invoke() || + ws_.impl_->paused_ping.maybe_invoke()) { - BOOST_ASSERT(ws_.wr_block_.is_locked()); + BOOST_ASSERT(ws_.impl_->wr_block.is_locked()); goto do_suspend; } - ws_.wr_block_.lock(this); + ws_.impl_->wr_block.lock(this); } goto upcall; } @@ -457,10 +358,10 @@ operator()( { for(;;) { - b = buffer(ws_.wr_buf_.get(), - ws_.wr_buf_size_); - more_ = ws_.deflate(b, cb_, fin_, in_, ec); - if(! ws_.check_ok(ec)) + b = buffer(ws_.impl_->wr_buf.get(), + ws_.impl_->wr_buf_size); + more_ = ws_.impl_->deflate(b, cb_, fin_, in_, ec); + if(! ws_.impl_->check_ok(ec)) goto upcall; n = buffer_size(b); if(n == 0) @@ -481,16 +382,16 @@ operator()( } fh_.fin = ! more_; fh_.len = n; - ws_.wr_fb_.clear(); + ws_.impl_->wr_fb.clear(); detail::write< - flat_static_buffer_base>(ws_.wr_fb_, fh_); - ws_.wr_cont_ = ! fin_; + flat_static_buffer_base>(ws_.impl_->wr_fb, fh_); + ws_.impl_->wr_cont = ! fin_; // Send frame BOOST_ASIO_CORO_YIELD - net::async_write(ws_.stream_, - buffers_cat(ws_.wr_fb_.data(), b), + net::async_write(ws_.impl_->stream, + buffers_cat(ws_.impl_->wr_fb.data(), b), std::move(*this)); - if(! ws_.check_ok(ec)) + if(! ws_.impl_->check_ok(ec)) goto upcall; bytes_transferred_ += in_; if(more_) @@ -499,20 +400,20 @@ operator()( fh_.rsv1 = false; // Allow outgoing control frames to // be sent in between message frames: - ws_.wr_block_.unlock(this); - if( ws_.paused_close_.maybe_invoke() || - ws_.paused_rd_.maybe_invoke() || - ws_.paused_ping_.maybe_invoke()) + ws_.impl_->wr_block.unlock(this); + if( ws_.impl_->paused_close.maybe_invoke() || + ws_.impl_->paused_rd.maybe_invoke() || + ws_.impl_->paused_ping.maybe_invoke()) { - BOOST_ASSERT(ws_.wr_block_.is_locked()); + BOOST_ASSERT(ws_.impl_->wr_block.is_locked()); goto do_suspend; } - ws_.wr_block_.lock(this); + ws_.impl_->wr_block.lock(this); } else { if(fh_.fin) - ws_.do_context_takeover_write(ws_.role_); + ws_.impl_->do_context_takeover_write(ws_.impl_->role); goto upcall; } } @@ -521,10 +422,10 @@ operator()( //-------------------------------------------------------------------------- upcall: - ws_.wr_block_.unlock(this); - ws_.paused_close_.maybe_invoke() || - ws_.paused_rd_.maybe_invoke() || - ws_.paused_ping_.maybe_invoke(); + ws_.impl_->wr_block.unlock(this); + ws_.impl_->paused_close.maybe_invoke() || + ws_.impl_->paused_rd.maybe_invoke() || + ws_.impl_->paused_ping.maybe_invoke(); if(! cont_) { BOOST_ASIO_CORO_YIELD @@ -576,15 +477,15 @@ write_some(bool fin, using net::buffer_copy; using net::buffer_size; std::size_t bytes_transferred = 0; - ec.assign(0, ec.category()); + ec = {}; // Make sure the stream is open - if(! check_open(ec)) + if(! impl_->check_open(ec)) return bytes_transferred; detail::frame_header fh; - if(! wr_cont_) + if(! impl_->wr_cont) { - begin_msg(); - fh.rsv1 = wr_compress_; + impl_->begin_msg(); + fh.rsv1 = impl_->wr_compress; } else { @@ -592,21 +493,21 @@ write_some(bool fin, } fh.rsv2 = false; fh.rsv3 = false; - fh.op = wr_cont_ ? - detail::opcode::cont : wr_opcode_; - fh.mask = role_ == role_type::client; + fh.op = impl_->wr_cont ? + detail::opcode::cont : impl_->wr_opcode; + fh.mask = impl_->role == role_type::client; auto remain = buffer_size(buffers); - if(wr_compress_) + if(impl_->wr_compress) { buffers_suffix< ConstBufferSequence> cb{buffers}; for(;;) { auto b = buffer( - wr_buf_.get(), wr_buf_size_); - auto const more = this->deflate( + impl_->wr_buf.get(), impl_->wr_buf_size); + auto const more = impl_->deflate( b, cb, fin, bytes_transferred, ec); - if(! check_ok(ec)) + if(! impl_->check_ok(ec)) return bytes_transferred; auto const n = buffer_size(b); if(n == 0) @@ -631,10 +532,10 @@ write_some(bool fin, detail::fh_buffer fh_buf; detail::write< flat_static_buffer_base>(fh_buf, fh); - wr_cont_ = ! fin; - net::write(stream_, + impl_->wr_cont = ! fin; + net::write(impl_->stream, buffers_cat(fh_buf.data(), b), ec); - if(! check_ok(ec)) + if(! impl_->check_ok(ec)) return bytes_transferred; if(! more) break; @@ -642,11 +543,11 @@ write_some(bool fin, fh.rsv1 = false; } if(fh.fin) - this->do_context_takeover_write(role_); + impl_->do_context_takeover_write(impl_->role); } else if(! fh.mask) { - if(! wr_frag_) + if(! impl_->wr_frag) { // no mask, no autofrag fh.fin = fin; @@ -654,33 +555,33 @@ write_some(bool fin, detail::fh_buffer fh_buf; detail::write< flat_static_buffer_base>(fh_buf, fh); - wr_cont_ = ! fin; - net::write(stream_, + impl_->wr_cont = ! fin; + net::write(impl_->stream, buffers_cat(fh_buf.data(), buffers), ec); - if(! check_ok(ec)) + if(! impl_->check_ok(ec)) return bytes_transferred; bytes_transferred += remain; } else { // no mask, autofrag - BOOST_ASSERT(wr_buf_size_ != 0); + BOOST_ASSERT(impl_->wr_buf_size != 0); buffers_suffix< ConstBufferSequence> cb{buffers}; for(;;) { - auto const n = clamp(remain, wr_buf_size_); + auto const n = clamp(remain, impl_->wr_buf_size); remain -= n; fh.len = n; fh.fin = fin ? remain == 0 : false; detail::fh_buffer fh_buf; detail::write< flat_static_buffer_base>(fh_buf, fh); - wr_cont_ = ! fin; - net::write(stream_, - buffers_cat(fh_buf.data(), - buffers_prefix(n, cb)), ec); - if(! check_ok(ec)) + impl_->wr_cont = ! fin; + net::write(impl_->stream, + beast::buffers_cat(fh_buf.data(), + beast::buffers_prefix(n, cb)), ec); + if(! impl_->check_ok(ec)) return bytes_transferred; bytes_transferred += n; if(remain == 0) @@ -690,7 +591,7 @@ write_some(bool fin, } } } - else if(! wr_frag_) + else if(! impl_->wr_frag) { // mask, no autofrag fh.fin = fin; @@ -704,29 +605,29 @@ write_some(bool fin, buffers_suffix< ConstBufferSequence> cb{buffers}; { - auto const n = clamp(remain, wr_buf_size_); - auto const b = buffer(wr_buf_.get(), n); + auto const n = clamp(remain, impl_->wr_buf_size); + auto const b = buffer(impl_->wr_buf.get(), n); buffer_copy(b, cb); cb.consume(n); remain -= n; detail::mask_inplace(b, key); - wr_cont_ = ! fin; - net::write(stream_, + impl_->wr_cont = ! fin; + net::write(impl_->stream, buffers_cat(fh_buf.data(), b), ec); - if(! check_ok(ec)) + if(! impl_->check_ok(ec)) return bytes_transferred; bytes_transferred += n; } while(remain > 0) { - auto const n = clamp(remain, wr_buf_size_); - auto const b = buffer(wr_buf_.get(), n); + auto const n = clamp(remain, impl_->wr_buf_size); + auto const b = buffer(impl_->wr_buf.get(), n); buffer_copy(b, cb); cb.consume(n); remain -= n; detail::mask_inplace(b, key); - net::write(stream_, b, ec); - if(! check_ok(ec)) + net::write(impl_->stream, b, ec); + if(! impl_->check_ok(ec)) return bytes_transferred; bytes_transferred += n; } @@ -734,28 +635,28 @@ write_some(bool fin, else { // mask, autofrag - BOOST_ASSERT(wr_buf_size_ != 0); + BOOST_ASSERT(impl_->wr_buf_size != 0); buffers_suffix< - ConstBufferSequence> cb{buffers}; + ConstBufferSequence> cb(buffers); for(;;) { fh.key = this->create_mask(); detail::prepared_key key; detail::prepare_key(key, fh.key); - auto const n = clamp(remain, wr_buf_size_); - auto const b = buffer(wr_buf_.get(), n); + auto const n = clamp(remain, impl_->wr_buf_size); + auto const b = buffer(impl_->wr_buf.get(), n); buffer_copy(b, cb); detail::mask_inplace(b, key); fh.len = n; remain -= n; fh.fin = fin ? remain == 0 : false; - wr_cont_ = ! fh.fin; + impl_->wr_cont = ! fh.fin; detail::fh_buffer fh_buf; detail::write< flat_static_buffer_base>(fh_buf, fh); - net::write(stream_, + net::write(impl_->stream, buffers_cat(fh_buf.data(), b), ec); - if(! check_ok(ec)) + if(! impl_->check_ok(ec)) return bytes_transferred; bytes_transferred += n; if(remain == 0) diff --git a/include/boost/beast/websocket/rfc6455.hpp b/include/boost/beast/websocket/rfc6455.hpp index fa6b2196..4cad2ecc 100644 --- a/include/boost/beast/websocket/rfc6455.hpp +++ b/include/boost/beast/websocket/rfc6455.hpp @@ -210,6 +210,6 @@ struct close_reason } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/websocket/ssl.hpp b/include/boost/beast/websocket/ssl.hpp index 3ff21008..03226a16 100644 --- a/include/boost/beast/websocket/ssl.hpp +++ b/include/boost/beast/websocket/ssl.hpp @@ -79,6 +79,6 @@ async_teardown( } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/websocket/stream.hpp b/include/boost/beast/websocket/stream.hpp index 35a46573..17247ef3 100644 --- a/include/boost/beast/websocket/stream.hpp +++ b/include/boost/beast/websocket/stream.hpp @@ -16,15 +16,9 @@ #include #include #include -#include -#include -#include #include -#include #include -#include -#include -#include +#include #include #include #include @@ -34,6 +28,7 @@ #include #include #include +#include #include namespace boost { @@ -124,7 +119,7 @@ template< bool deflateSupported> class stream #if ! BOOST_BEAST_DOXYGEN - : private detail::stream_base + : private detail::stream_base #endif { friend class close_test; @@ -153,64 +148,9 @@ class stream failed }; - NextLayer stream_; // the wrapped stream - close_reason cr_; // set from received close frame - control_cb_type ctrl_cb_; // control callback + struct impl_type; - std::size_t rd_msg_max_ // max message size - = 16 * 1024 * 1024; - std::uint64_t rd_size_ // total size of current message so far - = 0; - std::uint64_t rd_remain_ // message frame bytes left in current frame - = 0; - detail::frame_header rd_fh_; // current frame header - detail::prepared_key rd_key_; // current stateful mask key - detail::frame_buffer rd_fb_; // to write control frames (during reads) - detail::utf8_checker rd_utf8_; // to validate utf8 - static_buffer< - +tcp_frame_size> rd_buf_; // buffer for reads - detail::opcode rd_op_ // current message binary or text - = detail::opcode::text; - bool rd_cont_ // `true` if the next frame is a continuation - = false; - bool rd_done_ // set when a message is done - = true; - bool rd_close_ // did we read a close frame? - = false; - detail::soft_mutex rd_block_; // op currently reading - - role_type role_ // server or client - = role_type::client; - status status_ - = status::closed; - - detail::soft_mutex wr_block_; // op currently writing - bool wr_close_ // did we write a close frame? - = false; - bool wr_cont_ // next write is a continuation - = false; - bool wr_frag_ // autofrag the current message - = false; - bool wr_frag_opt_ // autofrag option setting - = true; - bool wr_compress_ // compress current message - = false; - detail::opcode wr_opcode_ // message type - = detail::opcode::text; - std::unique_ptr< - std::uint8_t[]> wr_buf_; // write buffer - std::size_t wr_buf_size_ // write buffer size (current message) - = 0; - std::size_t wr_buf_opt_ // write buffer size option setting - = 4096; - detail::fh_buffer wr_fb_; // header buffer used for writes - - saved_handler paused_rd_; // paused read op - saved_handler paused_wr_; // paused write op - saved_handler paused_ping_; // paused ping op - saved_handler paused_close_; // paused close op - saved_handler paused_r_rd_; // paused read op (async read) - saved_handler paused_r_close_;// paused close op (async read) + std::shared_ptr impl_; public: /// Indicates if the permessage-deflate extension is supported @@ -281,10 +221,7 @@ public: @return A copy of the executor that stream will use to dispatch handlers. */ executor_type - get_executor() noexcept - { - return stream_.get_executor(); - } + get_executor() const noexcept; /** Get a reference to the next layer @@ -295,10 +232,7 @@ public: stream layers. */ next_layer_type& - next_layer() - { - return stream_; - } + next_layer() noexcept; /** Get a reference to the next layer @@ -309,10 +243,7 @@ public: stream layers. */ next_layer_type const& - next_layer() const - { - return stream_; - } + next_layer() const noexcept; /** Get a reference to the lowest layer @@ -322,11 +253,9 @@ public: @return A reference to the lowest layer in the stack of stream layers. */ + // DEPRECATED lowest_layer_type& - lowest_layer() - { - return stream_.lowest_layer(); - } + lowest_layer() noexcept; /** Get a reference to the lowest layer @@ -336,11 +265,9 @@ public: @return A reference to the lowest layer in the stack of stream layers. Ownership is not transferred to the caller. */ + // DEPRECATED lowest_layer_type const& - lowest_layer() const - { - return stream_.lowest_layer(); - } + lowest_layer() const noexcept; //-------------------------------------------------------------------------- // @@ -354,10 +281,7 @@ public: no error has occurred. */ bool - is_open() const - { - return status_ == status::open; - } + is_open() const noexcept; /** Returns `true` if the latest message data indicates binary. @@ -369,10 +293,7 @@ public: undefined. */ bool - got_binary() const - { - return rd_op_ == detail::opcode::binary; - } + got_binary() const noexcept; /** Returns `true` if the latest message data indicates text. @@ -391,20 +312,14 @@ public: /// Returns `true` if the last completed read finished the current message. bool - is_message_done() const - { - return rd_done_; - } + is_message_done() const noexcept; /** Returns the close reason received from the peer. This is only valid after a read completes with error::closed. */ close_reason const& - reason() const - { - return cr_; - } + reason() const noexcept; /** Returns a suggested maximum buffer size for the next call to read. @@ -421,11 +336,7 @@ public: */ std::size_t read_size_hint( - std::size_t initial_size = +tcp_frame_size) const - { - return this->read_size_hint_pmd( - initial_size, rd_done_, rd_remain_, rd_fh_); - } + std::size_t initial_size = +tcp_frame_size) const; /** Returns a suggested maximum buffer size for the next call to read. @@ -462,17 +373,11 @@ public: `client_enable` or `server_enable` is `true`. */ void - set_option(permessage_deflate const& o) - { - this->set_option_pmd(o); - } + set_option(permessage_deflate const& o); /// Get the permessage-deflate extension options void - get_option(permessage_deflate& o) - { - this->get_option_pmd(o); - } + get_option(permessage_deflate& o); /** Set the automatic fragmentation option. @@ -494,17 +399,11 @@ public: @endcode */ void - auto_fragment(bool value) - { - wr_frag_opt_ = value; - } + auto_fragment(bool value); /// Returns `true` if the automatic fragmentation option is set. bool - auto_fragment() const - { - return wr_frag_opt_; - } + auto_fragment() const; /** Set the binary message write option. @@ -526,19 +425,11 @@ public: @endcode */ void - binary(bool value) - { - wr_opcode_ = value ? - detail::opcode::binary : - detail::opcode::text; - } + binary(bool value); /// Returns `true` if the binary message write option is set. bool - binary() const - { - return wr_opcode_ == detail::opcode::binary; - } + binary() const; /** Set a callback to be invoked on each incoming control frame. @@ -585,20 +476,14 @@ public: in undefined behavior. */ void - control_callback(std::function cb) - { - ctrl_cb_ = std::move(cb); - } + control_callback(std::function cb); /** Reset the control frame callback. This function removes any previously set control frame callback. */ void - control_callback() - { - ctrl_cb_ = {}; - } + control_callback(); /** Set the maximum incoming message size option. @@ -618,17 +503,11 @@ public: @param amount The limit on the size of incoming messages. */ void - read_message_max(std::size_t amount) - { - rd_msg_max_ = amount; - } + read_message_max(std::size_t amount); /// Returns the maximum incoming message size setting. std::size_t - read_message_max() const - { - return rd_msg_max_; - } + read_message_max() const; /** Set whether the PRNG is cryptographically secure @@ -656,10 +535,7 @@ public: cryptographically secure. */ void - secure_prng(bool value) - { - this->secure_prng_ = value; - } + secure_prng(bool value); /** Set the write buffer size option. @@ -687,20 +563,11 @@ public: @param amount The size of the write buffer in bytes. */ void - write_buffer_size(std::size_t amount) - { - if(amount < 8) - BOOST_THROW_EXCEPTION(std::invalid_argument{ - "write buffer size underflow"}); - wr_buf_opt_ = amount; - }; + write_buffer_size(std::size_t amount); /// Returns the size of the write buffer. std::size_t - write_buffer_size() const - { - return wr_buf_opt_; - } + write_buffer_size() const; /** Set the text message write option. @@ -722,19 +589,11 @@ public: @endcode */ void - text(bool value) - { - wr_opcode_ = value ? - detail::opcode::text : - detail::opcode::binary; - } + text(bool value); /// Returns `true` if the text message write option is set. bool - text() const - { - return wr_opcode_ == detail::opcode::text; - } + text() const; //-------------------------------------------------------------------------- // @@ -3372,38 +3231,6 @@ private: static void default_decorate_req(request_type&) {} static void default_decorate_res(response_type&) {} - void open(role_type role); - - void close(); - - void reset(); - - void begin_msg(); - - bool - check_open(error_code& ec) - { - if(status_ != status::open) - { - ec = net::error::operation_aborted; - return false; - } - ec = {}; - return true; - } - - bool - check_ok(error_code& ec) - { - if(ec) - { - if(status_ != status::closed) - status_ = status::failed; - return false; - } - return true; - } - template bool parse_fh( @@ -3507,7 +3334,7 @@ private: @see stream::secure_prng */ -inline +BOOST_BEAST_DECL void seed_prng(std::seed_seq& ss) { @@ -3518,12 +3345,13 @@ seed_prng(std::seed_seq& ss) } // beast } // boost -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #endif diff --git a/include/boost/beast/websocket/teardown.hpp b/include/boost/beast/websocket/teardown.hpp index 19a6f487..63d5b3e9 100644 --- a/include/boost/beast/websocket/teardown.hpp +++ b/include/boost/beast/websocket/teardown.hpp @@ -170,6 +170,6 @@ async_teardown( } // beast } // boost -#include +#include #endif diff --git a/test/beast/websocket/CMakeLists.txt b/test/beast/websocket/CMakeLists.txt index 20ed3d4f..2a937abe 100644 --- a/test/beast/websocket/CMakeLists.txt +++ b/test/beast/websocket/CMakeLists.txt @@ -16,6 +16,8 @@ add_executable (tests-beast-websocket ${EXTRAS_FILES} ${TEST_MAIN} Jamfile + _detail_prng.cpp + _detail_stream_base.cpp test.hpp _detail_prng.cpp accept.cpp diff --git a/test/beast/websocket/Jamfile b/test/beast/websocket/Jamfile index 7c5e5449..9271a889 100644 --- a/test/beast/websocket/Jamfile +++ b/test/beast/websocket/Jamfile @@ -9,6 +9,7 @@ local SOURCES = _detail_prng.cpp + _detail_stream_base.cpp accept.cpp close.cpp error.cpp diff --git a/test/beast/websocket/_detail_stream_base.cpp b/test/beast/websocket/_detail_stream_base.cpp new file mode 100644 index 00000000..0588478f --- /dev/null +++ b/test/beast/websocket/_detail_stream_base.cpp @@ -0,0 +1,11 @@ +// +// Copyright (w) 2016-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) +// +// Official repository: https://github.com/boostorg/beast +// + +// Test that header file is self-contained. +#include diff --git a/test/beast/websocket/close.cpp b/test/beast/websocket/close.cpp index 554e7295..ee488164 100644 --- a/test/beast/websocket/close.cpp +++ b/test/beast/websocket/close.cpp @@ -190,7 +190,7 @@ public: BOOST_THROW_EXCEPTION( system_error{ec}); }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); BEAST_EXPECT(count == 0); ws.async_close({}, [&](error_code ec) @@ -223,7 +223,7 @@ public: system_error{ec}); BEAST_EXPECT(n == 1); }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); BEAST_EXPECT(count == 0); ws.async_close({}, [&](error_code ec) @@ -259,7 +259,7 @@ public: BOOST_THROW_EXCEPTION( system_error{ec}); }); - while(! ws.wr_block_.is_locked()) + while(! ws.impl_->wr_block.is_locked()) { ioc.run_one(); if(! BEAST_EXPECT(! ioc.stopped())) @@ -300,7 +300,7 @@ public: system_error{ec}); BEAST_EXPECT(++count == 1); }); - while(! ws.wr_block_.is_locked()) + while(! ws.impl_->wr_block.is_locked()) { ioc.run_one(); if(! BEAST_EXPECT(! ioc.stopped())) @@ -341,7 +341,7 @@ public: system_error{ec}); BEAST_EXPECT(++count == 1); }); - while(! ws.wr_block_.is_locked()) + while(! ws.impl_->wr_block.is_locked()) { ioc.run_one(); if(! BEAST_EXPECT(! ioc.stopped())) @@ -436,7 +436,7 @@ public: system_error{ec}); BEAST_EXPECT(++count == 3); }); - BEAST_EXPECT(ws.rd_block_.is_locked()); + BEAST_EXPECT(ws.impl_->rd_block.is_locked()); ws.async_close({}, [&](error_code ec) { @@ -446,7 +446,7 @@ public: BEAST_EXPECT(++count == 2); }); BEAST_EXPECT(ws.is_open()); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); BEAST_EXPECT(count == 0); ioc.run(); BEAST_EXPECT(count == 3); diff --git a/test/beast/websocket/ping.cpp b/test/beast/websocket/ping.cpp index aa539cbb..99efe00a 100644 --- a/test/beast/websocket/ping.cpp +++ b/test/beast/websocket/ping.cpp @@ -115,7 +115,7 @@ public: system_error{ec}); BEAST_EXPECT(n == 12); }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); BEAST_EXPECT(count == 0); ws.async_ping({}, [&](error_code ec) @@ -147,7 +147,7 @@ public: BOOST_THROW_EXCEPTION( system_error{ec}); }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); BEAST_EXPECT(count == 0); ws.async_ping({}, [&](error_code ec) @@ -183,7 +183,7 @@ public: BOOST_THROW_EXCEPTION( system_error{ec}); }); - while(! ws.wr_block_.is_locked()) + while(! ws.impl_->wr_block.is_locked()) { ioc.run_one(); if(! BEAST_EXPECT(! ioc.stopped())) @@ -225,7 +225,7 @@ public: BOOST_THROW_EXCEPTION( system_error{ec}); }); - while(! ws.wr_block_.is_locked()) + while(! ws.impl_->wr_block.is_locked()) { ioc.run_one(); if(! BEAST_EXPECT(! ioc.stopped())) @@ -266,7 +266,7 @@ public: BOOST_THROW_EXCEPTION( system_error{ec}); }); - while(! ws.wr_block_.is_locked()) + while(! ws.impl_->wr_block.is_locked()) { ioc.run_one(); if(! BEAST_EXPECT(! ioc.stopped())) @@ -306,7 +306,7 @@ public: BOOST_THROW_EXCEPTION( system_error{ec}); }); - while(! ws.wr_block_.is_locked()) + while(! ws.impl_->wr_block.is_locked()) { ioc.run_one(); if(! BEAST_EXPECT(! ioc.stopped())) @@ -345,7 +345,7 @@ public: system_error{ec}); BEAST_EXPECT(n == 1); }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); ws.async_ping("", [&](error_code ec) { @@ -389,7 +389,7 @@ public: ec.message()); }); if(! BEAST_EXPECT(run_until(ioc, 100, - [&]{ return ws.wr_close_; }))) + [&]{ return ws.impl_->wr_close; }))) return; // Try to ping ws.async_ping("payload", diff --git a/test/beast/websocket/read2.cpp b/test/beast/websocket/read2.cpp index 24c2dedf..54647d8f 100644 --- a/test/beast/websocket/read2.cpp +++ b/test/beast/websocket/read2.cpp @@ -45,7 +45,7 @@ public: system_error{ec}); BEAST_EXPECT(++count == 1); }); - while(! ws.rd_block_.is_locked()) + while(! ws.impl_->rd_block.is_locked()) ioc.run_one(); multi_buffer b; ws.async_read(b, @@ -80,7 +80,7 @@ public: system_error{ec}); BEAST_EXPECT(++count == 2); }); - BOOST_ASSERT(ws.rd_block_.is_locked()); + BOOST_ASSERT(ws.impl_->rd_block.is_locked()); ws.async_close({}, [&](error_code ec) { @@ -117,7 +117,7 @@ public: BEAST_EXPECT(buffers_to_string(b.data()) == s); ++count; }); - BEAST_EXPECT(ws.rd_block_.is_locked()); + BEAST_EXPECT(ws.impl_->rd_block.is_locked()); ws.async_write(buffer(s), [&](error_code ec, std::size_t n) { @@ -127,7 +127,7 @@ public: BEAST_EXPECT(n == s.size()); ++count; }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); ioc.run(); BEAST_EXPECT(count == 2); }); @@ -166,7 +166,7 @@ public: BEAST_EXPECT(++count == 3); }); }); - BEAST_EXPECT(ws.rd_block_.is_locked()); + BEAST_EXPECT(ws.impl_->rd_block.is_locked()); ws.async_close({}, [&](error_code ec) { @@ -175,7 +175,7 @@ public: system_error{ec}); BEAST_EXPECT(++count == 2); }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); ioc.run(); BEAST_EXPECT(count == 3); }); @@ -204,7 +204,7 @@ public: system_error{ec}); BEAST_EXPECT(++count == 2); }); - BEAST_EXPECT(ws.rd_block_.is_locked()); + BEAST_EXPECT(ws.impl_->rd_block.is_locked()); ws.async_close({}, [&](error_code ec) { @@ -213,7 +213,7 @@ public: system_error{ec}); BEAST_EXPECT(++count == 1); }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); ioc.run(); BEAST_EXPECT(count == 2); }); diff --git a/test/beast/websocket/stream.cpp b/test/beast/websocket/stream.cpp index f5dfd94e..20f7e8ac 100644 --- a/test/beast/websocket/stream.cpp +++ b/test/beast/websocket/stream.cpp @@ -124,16 +124,16 @@ public: BOOST_STATIC_ASSERT(std::is_constructible< stream, test::stream&>::value); + // VFALCO Should these be allowed for NextLayer references? BOOST_STATIC_ASSERT(std::is_move_constructible< stream>::value); - - BOOST_STATIC_ASSERT(! std::is_move_assignable< + BOOST_STATIC_ASSERT(std::is_move_assignable< stream>::value); - log << "sizeof(websocket::stream_base) == " << - sizeof(websocket::detail::stream_base) << std::endl; log << "sizeof(websocket::stream) == " << sizeof(websocket::stream) << std::endl; + log << "sizeof(websocket::stream::impl_type) == " << + sizeof(websocket::stream::impl_type) << std::endl; testOptions(); } diff --git a/test/beast/websocket/write.cpp b/test/beast/websocket/write.cpp index eda04118..9f5886da 100644 --- a/test/beast/websocket/write.cpp +++ b/test/beast/websocket/write.cpp @@ -290,7 +290,7 @@ public: BOOST_THROW_EXCEPTION( system_error{ec}); }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); BEAST_EXPECT(count == 0); ws.async_write(sbuf("*"), [&](error_code ec, std::size_t n) @@ -323,7 +323,7 @@ public: BOOST_THROW_EXCEPTION( system_error{ec}); }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); BEAST_EXPECT(count == 0); ws.async_write(sbuf("*"), [&](error_code ec, std::size_t) @@ -359,7 +359,7 @@ public: BOOST_THROW_EXCEPTION( system_error{ec}); }); - while(! ws.wr_block_.is_locked()) + while(! ws.impl_->wr_block.is_locked()) { ioc.run_one(); if(! BEAST_EXPECT(! ioc.stopped())) @@ -401,7 +401,7 @@ public: system_error{ec}); BEAST_EXPECT(n == 16384); }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); ws.async_ping("", [&](error_code ec) { @@ -435,7 +435,7 @@ public: system_error{ec}); BEAST_EXPECT(n == 16384); }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); ws.async_ping("", [&](error_code ec) { @@ -468,7 +468,7 @@ public: system_error{ec}); BEAST_EXPECT(n == 16384); }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); ws.async_ping("", [&](error_code ec) { @@ -500,7 +500,7 @@ public: system_error{ec}); BEAST_EXPECT(n == 16384); }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); ws.async_ping("", [&](error_code ec) { @@ -538,7 +538,7 @@ public: system_error{ec}); BEAST_EXPECT(n == s.size()); }); - BEAST_EXPECT(ws.wr_block_.is_locked()); + BEAST_EXPECT(ws.impl_->wr_block.is_locked()); ws.async_ping("", [&](error_code ec) {