From 6baa607295f026e818dac26a3f7c68018b8fc05d Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 18 Feb 2019 12:42:24 -0800 Subject: [PATCH] Fixes to support Asio changes (API Change): This adjusts Beast's interfaces and implementation to match the changes in Boost.Asio. --- CMakeLists.txt | 8 + Jamfile | 8 + .../server-flex/advanced_server_flex.cpp | 224 +++++++----------- example/advanced/server/advanced_server.cpp | 36 ++- .../async-ssl/http_client_async_ssl.cpp | 13 +- .../http/client/async/http_client_async.cpp | 12 +- .../client/coro-ssl/http_client_coro_ssl.cpp | 7 +- example/http/client/coro/http_client_coro.cpp | 8 +- example/http/client/crawl/http_crawl.cpp | 110 ++------- .../client/sync-ssl/http_client_sync_ssl.cpp | 8 +- example/http/client/sync/http_client_sync.cpp | 12 +- .../async-ssl/http_server_async_ssl.cpp | 16 +- .../http/server/async/http_server_async.cpp | 18 +- .../server/coro-ssl/http_server_coro_ssl.cpp | 16 +- example/http/server/coro/http_server_coro.cpp | 14 +- example/http/server/fast/http_server_fast.cpp | 4 +- example/http/server/flex/http_server_flex.cpp | 36 +-- .../http/server/small/http_server_small.cpp | 2 +- .../http_server_stackless_ssl.cpp | 17 +- .../stackless/http_server_stackless.cpp | 16 +- .../async-ssl/websocket_client_async_ssl.cpp | 8 +- .../client/async/websocket_client_async.cpp | 7 +- .../coro-ssl/websocket_client_coro_ssl.cpp | 6 +- .../client/coro/websocket_client_coro.cpp | 5 +- .../async-ssl/websocket_server_async_ssl.cpp | 22 +- .../server/async/websocket_server_async.cpp | 18 +- .../server/chat-multi/http_session.hpp | 2 +- .../websocket/server/chat-multi/listener.cpp | 13 +- .../websocket/server/chat-multi/listener.hpp | 4 +- .../server/chat-multi/websocket_session.cpp | 25 +- .../server/chat-multi/websocket_session.hpp | 6 +- .../coro-ssl/websocket_server_coro_ssl.cpp | 13 +- .../server/coro/websocket_server_coro.cpp | 16 +- .../server/fast/websocket_server_fast.cpp | 47 ++-- .../websocket_server_stackless_ssl.cpp | 10 +- .../stackless/websocket_server_stackless.cpp | 12 +- .../boost/beast/_experimental/test/tcp.hpp | 29 ++- include/boost/beast/core/basic_stream.hpp | 182 ++++---------- .../beast/core/detail/get_io_context.hpp | 107 +++++++++ .../boost/beast/core/detail/stream_base.hpp | 6 +- .../boost/beast/core/impl/basic_stream.hpp | 74 +----- include/boost/beast/core/stream_traits.hpp | 4 +- include/boost/beast/core/tcp_stream.hpp | 9 +- include/boost/beast/http/file_body.hpp | 2 + .../boost/beast/http/impl/file_body_win32.ipp | 50 ++-- .../boost/beast/websocket/detail/frame.hpp | 8 + .../beast/websocket/impl/stream_impl.hpp | 2 +- .../boost/beast/websocket/impl/teardown.hpp | 48 ++-- include/boost/beast/websocket/stream.hpp | 5 +- include/boost/beast/websocket/teardown.hpp | 14 +- test/beast/core/CMakeLists.txt | 1 + test/beast/core/Jamfile | 1 + test/beast/core/_detail_get_io_context.cpp | 57 +++++ test/beast/core/basic_stream.cpp | 40 ++-- test/beast/core/tcp_stream.cpp | 34 +++ test/beast/websocket/stream.cpp | 6 +- test/beast/websocket/stream_base.cpp | 1 + test/doc/websocket_snippets.cpp | 6 +- 58 files changed, 762 insertions(+), 723 deletions(-) create mode 100644 include/boost/beast/core/detail/get_io_context.hpp create mode 100644 test/beast/core/_detail_get_io_context.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 26c94dea..2e1aaef6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,6 +84,11 @@ add_definitions (-DBOOST_ASIO_DISABLE_BOOST_ARRAY=1) add_definitions (-DBOOST_ASIO_DISABLE_BOOST_BIND=1) add_definitions (-DBOOST_ASIO_DISABLE_BOOST_DATE_TIME=1) add_definitions (-DBOOST_ASIO_DISABLE_BOOST_REGEX=1) + +# workaround for asio defect +add_definitions (-DBOOST_ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE=1) +add_definitions (-DBOOST_ASIO_DISABLE_WINDOWS_STREAM_HANDLE=1) + add_definitions (-DBOOST_COROUTINES_NO_DEPRECATION_WARNING=1) include_directories (${BOOST_ROOT}) @@ -174,6 +179,9 @@ endif() include_directories (.) +# VFALCO FIXME Need this for recent asio changes +add_definitions (-DBOOST_BEAST_NO_FILE_BODY_WIN32=1) + if (OPENSSL_FOUND) include_directories (${OPENSSL_INCLUDE_DIR}) endif() diff --git a/Jamfile b/Jamfile index 4b544335..3aed4ee9 100644 --- a/Jamfile +++ b/Jamfile @@ -93,6 +93,14 @@ project /boost/beast BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1 BOOST_ASIO_DISABLE_BOOST_REGEX=1 BOOST_COROUTINES_NO_DEPRECATION_WARNING=1 + + # workaround for asio defect + BOOST_ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE=1 + BOOST_ASIO_DISABLE_WINDOWS_STREAM_HANDLE=1 + + # VFALCO FIXME Need this for recent asio changes + BOOST_BEAST_NO_FILE_BODY_WIN32=1 + msvc:"/bigobj" msvc-14.1:"/permissive-" msvc:_SCL_SECURE_NO_WARNINGS=1 diff --git a/example/advanced/server-flex/advanced_server_flex.cpp b/example/advanced/server-flex/advanced_server_flex.cpp index 3a596959..256c36dc 100644 --- a/example/advanced/server-flex/advanced_server_flex.cpp +++ b/example/advanced/server-flex/advanced_server_flex.cpp @@ -331,27 +331,26 @@ public: } }; +//------------------------------------------------------------------------------ + // Handles a plain WebSocket connection class plain_websocket_session : public websocket_session , public std::enable_shared_from_this { - websocket::stream< - beast::tcp_stream> ws_; - bool close_ = false; + websocket::stream ws_; public: // Create the session explicit plain_websocket_session( - beast::tcp_stream&& stream) + beast::tcp_stream&& stream) : ws_(std::move(stream)) { } // Called by the base class - websocket::stream< - beast::tcp_stream>& + websocket::stream& ws() { return ws_; @@ -365,48 +364,28 @@ public: // Accept the WebSocket upgrade request do_accept(std::move(req)); } - - void - on_close(beast::error_code ec) - { - // Happens when close times out - if(ec == net::error::operation_aborted) - return; - - if(ec) - return fail(ec, "close"); - - // At this point the connection is gracefully closed - } }; +//------------------------------------------------------------------------------ + // Handles an SSL WebSocket connection class ssl_websocket_session : public websocket_session , public std::enable_shared_from_this { - websocket::stream>> ws_; - bool eof_ = false; + websocket::stream< + beast::ssl_stream> ws_; public: - // Create the http_session + // Create the ssl_websocket_session explicit - ssl_websocket_session(beast::ssl_stream< - beast::tcp_stream>&& stream) + ssl_websocket_session( + beast::ssl_stream&& stream) : ws_(std::move(stream)) { } - // Called by the base class - websocket::stream>>& - ws() - { - return ws_; - } - - // Start the asynchronous operation + // Start the session template void run(http::request> req) @@ -415,18 +394,15 @@ public: do_accept(std::move(req)); } - void - do_eof() + // Called by the base class + websocket::stream< + beast::ssl_stream>& + ws() { - eof_ = true; - - // Perform the SSL shutdown - ws_.next_layer().async_shutdown( - beast::bind_front_handler( - &ssl_websocket_session::on_shutdown, - shared_from_this())); + return ws_; } +private: void on_shutdown(beast::error_code ec) { @@ -437,10 +413,12 @@ public: } }; +//------------------------------------------------------------------------------ + template void make_websocket_session( - beast::tcp_stream stream, + beast::tcp_stream stream, http::request> req) { std::make_shared( @@ -450,7 +428,7 @@ make_websocket_session( template void make_websocket_session( - beast::ssl_stream> stream, + beast::ssl_stream stream, http::request> req) { std::make_shared( @@ -568,19 +546,15 @@ class http_session queue queue_; protected: - net::steady_timer timer_; beast::flat_buffer buffer_; public: // Construct the session http_session( - net::io_context& ioc, beast::flat_buffer buffer, std::shared_ptr const& doc_root) : doc_root_(doc_root) , queue_(*this) - , timer_(ioc, - (std::chrono::steady_clock::time_point::max)()) , buffer_(std::move(buffer)) { } @@ -621,7 +595,11 @@ public: // See if it is a WebSocket Upgrade if(websocket::is_upgrade(req_)) { - // Transfer the stream to a new WebSocket session + // Disable the timeout. + // The websocket::stream uses its own timeout settings. + beast::get_lowest_layer(derived().stream()).expires_never(); + + // Transfer the stream to a new WebSocket session. return make_websocket_session( derived().release_stream(), std::move(req_)); @@ -663,56 +641,50 @@ public: } }; +//------------------------------------------------------------------------------ + // Handles a plain HTTP connection class plain_http_session : public http_session , public std::enable_shared_from_this { - beast::tcp_stream stream_; + beast::tcp_stream stream_; public: - // Create the http_session + // Create the session plain_http_session( - beast::tcp_stream&& stream, + beast::tcp_stream&& stream, beast::flat_buffer&& buffer, std::shared_ptr const& doc_root) : http_session( - stream.get_executor().context(), std::move(buffer), doc_root) , stream_(std::move(stream)) { } + // Start the session + void + run() + { + this->do_read(); + } + // Called by the base class - beast::tcp_stream& + beast::tcp_stream& stream() { return stream_; } // Called by the base class - beast::tcp_stream + beast::tcp_stream release_stream() { return std::move(stream_); } - // Start the asynchronous operation - void - run() - { - // Make sure we run on the strand - if(! stream_.get_executor().running_in_this_thread()) - return net::post( - stream_.get_executor(), - beast::bind_front_handler( - &plain_http_session::run, - shared_from_this())); - - do_read(); - } - + // Called by the base class void do_eof() { @@ -722,70 +694,35 @@ public: // At this point the connection is closed gracefully } - - void - do_timeout() - { - // Closing the socket cancels all outstanding operations. They - // will complete with net::error::operation_aborted - beast::error_code ec; - stream_.socket().shutdown(tcp::socket::shutdown_both, ec); - stream_.socket().close(ec); - } }; +//------------------------------------------------------------------------------ + // Handles an SSL HTTP connection class ssl_http_session : public http_session , public std::enable_shared_from_this { - beast::ssl_stream< - beast::tcp_stream> stream_; - bool eof_ = false; + beast::ssl_stream stream_; public: // Create the http_session ssl_http_session( - beast::tcp_stream&& stream, + beast::tcp_stream&& stream, ssl::context& ctx, beast::flat_buffer&& buffer, std::shared_ptr const& doc_root) : http_session( - stream.get_executor().context(), std::move(buffer), doc_root) , stream_(std::move(stream), ctx) { } - // Called by the base class - beast::ssl_stream< - beast::tcp_stream>& - stream() - { - return stream_; - } - - // Called by the base class - beast::ssl_stream< - beast::tcp_stream> - release_stream() - { - return std::move(stream_); - } - - // Start the asynchronous operation + // Start the session void run() { - // Make sure we run on the strand - if(! stream_.get_executor().running_in_this_thread()) - return net::post( - stream_.get_executor(), - beast::bind_front_handler( - &ssl_http_session::run, - shared_from_this())); - // Set the timeout. beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); @@ -798,6 +735,36 @@ public: &ssl_http_session::on_handshake, shared_from_this())); } + + // Called by the base class + beast::ssl_stream& + stream() + { + return stream_; + } + + // Called by the base class + beast::ssl_stream + release_stream() + { + return std::move(stream_); + } + + // Called by the base class + void + do_eof() + { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Perform the SSL shutdown + stream_.async_shutdown( + beast::bind_front_handler( + &ssl_http_session::on_shutdown, + shared_from_this())); + } + +private: void on_handshake( beast::error_code ec, @@ -812,21 +779,6 @@ public: do_read(); } - void - do_eof() - { - eof_ = true; - - // Set the timeout. - beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); - - // Perform the SSL shutdown - stream_.async_shutdown( - beast::bind_front_handler( - &ssl_http_session::on_shutdown, - shared_from_this())); - } - void on_shutdown(beast::error_code ec) { @@ -846,7 +798,7 @@ public: // Detects SSL handshakes class detect_session : public std::enable_shared_from_this { - beast::tcp_stream stream_; + beast::tcp_stream stream_; ssl::context& ctx_; std::shared_ptr doc_root_; beast::flat_buffer buffer_; @@ -854,7 +806,7 @@ class detect_session : public std::enable_shared_from_this public: explicit detect_session( - tcp::socket socket, + tcp::socket&& socket, ssl::context& ctx, std::shared_ptr const& doc_root) : stream_(std::move(socket)) @@ -906,8 +858,10 @@ public: // Accepts incoming connections and launches the sessions class listener : public std::enable_shared_from_this { + net::io_context& ioc_; ssl::context& ctx_; tcp::acceptor acceptor_; + tcp::socket socket_; std::shared_ptr doc_root_; public: @@ -916,8 +870,10 @@ public: ssl::context& ctx, tcp::endpoint endpoint, std::shared_ptr const& doc_root) - : ctx_(ctx) - , acceptor_(ioc) + : ioc_(ioc) + , ctx_(ctx) + , acceptor_(beast::make_strand(ioc)) + , socket_(beast::make_strand(ioc)) , doc_root_(doc_root) { beast::error_code ec; @@ -969,13 +925,14 @@ public: do_accept() { acceptor_.async_accept( + socket_, beast::bind_front_handler( &listener::on_accept, shared_from_this())); } void - on_accept(beast::error_code ec, tcp::socket socket) + on_accept(beast::error_code ec) { if(ec) { @@ -985,11 +942,14 @@ public: { // Create the detector http_session and run it std::make_shared( - std::move(socket), + std::move(socket_), ctx_, doc_root_)->run(); } + // Make sure each session gets its own strand + socket_ = tcp::socket(beast::make_strand(ioc_)); + // Accept another connection do_accept(); } diff --git a/example/advanced/server/advanced_server.cpp b/example/advanced/server/advanced_server.cpp index 15ff7d02..7985bc3a 100644 --- a/example/advanced/server/advanced_server.cpp +++ b/example/advanced/server/advanced_server.cpp @@ -219,14 +219,13 @@ fail(beast::error_code ec, char const* what) // Echoes back all received WebSocket messages class websocket_session : public std::enable_shared_from_this { - websocket::stream> ws_; + websocket::stream ws_; beast::flat_buffer buffer_; - char ping_state_ = 0; public: // Take ownership of the socket explicit - websocket_session(tcp::socket socket) + websocket_session(tcp::socket&& socket) : ws_(std::move(socket)) { } @@ -258,6 +257,7 @@ public: shared_from_this())); } +private: void on_accept(beast::error_code ec) { @@ -413,7 +413,7 @@ class http_session : public std::enable_shared_from_this } }; - beast::tcp_stream stream_; + beast::tcp_stream stream_; beast::flat_buffer buffer_; std::shared_ptr doc_root_; http::request req_; @@ -421,9 +421,8 @@ class http_session : public std::enable_shared_from_this public: // Take ownership of the socket - explicit http_session( - tcp::socket socket, + tcp::socket&& socket, std::shared_ptr const& doc_root) : stream_(std::move(socket)) , doc_root_(doc_root) @@ -431,21 +430,14 @@ public: { } - // Start the asynchronous operation + // Start the session void run() { - // Make sure we run on the strand - if(! stream_.get_executor().running_in_this_thread()) - return net::post( - stream_.get_executor(), - std::bind( - &http_session::run, - shared_from_this())); - do_read(); } +private: void do_read() { @@ -532,6 +524,7 @@ public: // Accepts incoming connections and launches the sessions class listener : public std::enable_shared_from_this { + net::io_context& ioc_; tcp::acceptor acceptor_; tcp::socket socket_; std::shared_ptr doc_root_; @@ -541,8 +534,9 @@ public: net::io_context& ioc, tcp::endpoint endpoint, std::shared_ptr const& doc_root) - : acceptor_(ioc) - , socket_(ioc) + : ioc_(ioc) + , acceptor_(beast::make_strand(ioc)) + , socket_(beast::make_strand(ioc)) , doc_root_(doc_root) { beast::error_code ec; @@ -594,13 +588,14 @@ public: do_accept() { acceptor_.async_accept( + socket_, beast::bind_front_handler( &listener::on_accept, shared_from_this())); } void - on_accept(beast::error_code ec, tcp::socket socket) + on_accept(beast::error_code ec) { if(ec) { @@ -610,10 +605,13 @@ public: { // Create the http session and run it std::make_shared( - std::move(socket), + std::move(socket_), doc_root_)->run(); } + // Make sure each session gets its own strand + socket_ = tcp::socket(beast::make_strand(ioc_)); + // Accept another connection do_accept(); } diff --git a/example/http/client/async-ssl/http_client_async_ssl.cpp b/example/http/client/async-ssl/http_client_async_ssl.cpp index a7fa08c3..563349ae 100644 --- a/example/http/client/async-ssl/http_client_async_ssl.cpp +++ b/example/http/client/async-ssl/http_client_async_ssl.cpp @@ -33,10 +33,6 @@ using tcp = boost::asio::ip::tcp; // from //------------------------------------------------------------------------------ -// The type of stream to use -using stream_type = - beast::ssl_stream>; - // Report a failure void fail(beast::error_code ec, char const* what) @@ -48,17 +44,18 @@ fail(beast::error_code ec, char const* what) class session : public std::enable_shared_from_this { tcp::resolver resolver_; - stream_type stream_; + beast::ssl_stream stream_; beast::flat_buffer buffer_; // (Must persist between reads) http::request req_; http::response res_; public: - // Resolver and stream require an io_context + // Objects are constructed with a strand to + // ensure that handlers do not execute concurrently. explicit session(net::io_context& ioc, ssl::context& ctx) - : resolver_(ioc) - , stream_(ioc, ctx) + : resolver_(beast::make_strand(ioc)) + , stream_(beast::make_strand(ioc), ctx) { } diff --git a/example/http/client/async/http_client_async.cpp b/example/http/client/async/http_client_async.cpp index cc1de0b1..c1a58935 100644 --- a/example/http/client/async/http_client_async.cpp +++ b/example/http/client/async/http_client_async.cpp @@ -29,9 +29,6 @@ using tcp = boost::asio::ip::tcp; // from //------------------------------------------------------------------------------ -// The type of stream to use -using stream_type = beast::tcp_stream; - // Report a failure void fail(beast::error_code ec, char const* what) @@ -43,17 +40,18 @@ fail(beast::error_code ec, char const* what) class session : public std::enable_shared_from_this { tcp::resolver resolver_; - stream_type stream_; + beast::tcp_stream stream_; beast::flat_buffer buffer_; // (Must persist between reads) http::request req_; http::response res_; public: - // Resolver and socket require an io_context + // Objects are constructed with a strand to + // ensure that handlers do not execute concurrently. explicit session(net::io_context& ioc) - : resolver_(ioc) - , stream_(ioc) + : resolver_(beast::make_strand(ioc)) + , stream_(beast::make_strand(ioc)) { } diff --git a/example/http/client/coro-ssl/http_client_coro_ssl.cpp b/example/http/client/coro-ssl/http_client_coro_ssl.cpp index 6c603473..6157e5b7 100644 --- a/example/http/client/coro-ssl/http_client_coro_ssl.cpp +++ b/example/http/client/coro-ssl/http_client_coro_ssl.cpp @@ -33,11 +33,6 @@ using tcp = boost::asio::ip::tcp; // from //------------------------------------------------------------------------------ -// The type of stream to use -// Stackful coroutines are already stranded. -using stream_type = - beast::ssl_stream>; - // Report a failure void fail(beast::error_code ec, char const* what) @@ -60,7 +55,7 @@ do_session( // These objects perform our I/O tcp::resolver resolver(ioc); - stream_type stream(ioc, ctx); + beast::ssl_stream stream(ioc, ctx); // Set SNI Hostname (many hosts need this to handshake successfully) if(! SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) diff --git a/example/http/client/coro/http_client_coro.cpp b/example/http/client/coro/http_client_coro.cpp index f21583bd..b415a990 100644 --- a/example/http/client/coro/http_client_coro.cpp +++ b/example/http/client/coro/http_client_coro.cpp @@ -29,10 +29,6 @@ using tcp = boost::asio::ip::tcp; // from //------------------------------------------------------------------------------ -// The type of stream to use. -// Stackful coroutines are already stranded. -using stream_type = beast::tcp_stream; - // Report a failure void fail(beast::error_code ec, char const* what) @@ -53,8 +49,8 @@ do_session( beast::error_code ec; // These objects perform our I/O - tcp::resolver resolver{ioc}; - stream_type stream(ioc); + tcp::resolver resolver(ioc); + beast::tcp_stream stream(ioc); // Look up the domain name auto const results = resolver.async_resolve(host, port, yield[ec]); diff --git a/example/http/client/crawl/http_crawl.cpp b/example/http/client/crawl/http_crawl.cpp index d964a3be..b4e33446 100644 --- a/example/http/client/crawl/http_crawl.cpp +++ b/example/http/client/crawl/http_crawl.cpp @@ -151,10 +151,7 @@ class worker : public std::enable_shared_from_this crawl_report& report_; tcp::resolver resolver_; - tcp::socket socket_; - net::steady_timer timer_; - net::strand< - net::io_context::executor_type> strand_; + beast::tcp_stream stream_; beast::flat_buffer buffer_; // (Must persist between reads) http::request req_; http::response res_; @@ -167,11 +164,8 @@ public: crawl_report& report, net::io_context& ioc) : report_(report) - , resolver_(ioc) - , socket_(ioc) - , timer_(ioc, - (chrono::steady_clock::time_point::max)()) - , strand_(ioc.get_executor()) + , resolver_(beast::make_strand(ioc)) + , stream_(beast::make_strand(ioc)) { // Set up the common fields of the request req_.version(11); @@ -184,44 +178,9 @@ public: void run() { - // Run the timer. The timer is operated - // continuously, this simplifies the code. - on_timer({}); - do_get_host(); } - void - on_timer(beast::error_code ec) - { - if(ec && ec != net::error::operation_aborted) - { - // Should never happen - report_.aggregate( - [](crawl_report& rep) - { - ++rep.timer_failures; - }); - return; - } - - // Verify that the timer really expired since the deadline may have moved. - if(timer_.expiry() <= chrono::steady_clock::now()) - { - socket_.shutdown(tcp::socket::shutdown_both, ec); - socket_.close(ec); - return; - } - - // Wait on the timer - timer_.async_wait( - net::bind_executor( - strand_, - beast::bind_front_handler( - &worker::on_timer, - shared_from_this()))); - } - void do_get_host() { @@ -230,27 +189,19 @@ public: // nullptr means no more work if(! host) - { - timer_.cancel_one(); return; - } // The Host HTTP field is required req_.set(http::field::host, host); - // Set the timer - timer_.expires_after(chrono::seconds(timeout)); - // Set up an HTTP GET request message // Look up the domain name resolver_.async_resolve( host, "http", - net::bind_executor( - strand_, - beast::bind_front_handler( - &worker::on_resolve, - shared_from_this()))); + beast::bind_front_handler( + &worker::on_resolve, + shared_from_this())); } void @@ -268,18 +219,16 @@ public: return do_get_host(); } - // Set the timer - timer_.expires_after(chrono::seconds(timeout)); + // Set a timeout on the operation + stream_.expires_after(std::chrono::seconds(10)); // Make the connection on the IP address we get from a lookup - net::async_connect( - socket_, + beast::async_connect( + stream_, results, - net::bind_executor( - strand_, - beast::bind_front_handler( - &worker::on_connect, - shared_from_this()))); + beast::bind_front_handler( + &worker::on_connect, + shared_from_this())); } void @@ -295,18 +244,16 @@ public: return do_get_host(); } - // Set the timer - timer_.expires_after(chrono::seconds(timeout)); + // Set a timeout on the operation + stream_.expires_after(std::chrono::seconds(10)); // Send the HTTP request to the remote host http::async_write( - socket_, + stream_, req_, - net::bind_executor( - strand_, - beast::bind_front_handler( - &worker::on_write, - shared_from_this()))); + beast::bind_front_handler( + &worker::on_write, + shared_from_this())); } void @@ -325,21 +272,16 @@ public: }); return do_get_host(); } - - // Set the timer - timer_.expires_after(chrono::seconds(timeout)); // Receive the HTTP response res_ = {}; http::async_read( - socket_, + stream_, buffer_, res_, - net::bind_executor( - strand_, - beast::bind_front_handler( - &worker::on_read, - shared_from_this()))); + beast::bind_front_handler( + &worker::on_read, + shared_from_this())); } void @@ -368,8 +310,8 @@ public: }); // Gracefully close the socket - socket_.shutdown(tcp::socket::shutdown_both, ec); - socket_.close(ec); + stream_.socket().shutdown(tcp::socket::shutdown_both, ec); + stream_.close(); // If we get here then the connection is closed gracefully @@ -412,7 +354,7 @@ int main(int argc, char* argv[]) auto const threads = std::max(1, std::atoi(argv[1])); // The io_context is required for all I/O - net::io_context ioc{1}; + net::io_context ioc; // The work keeps io_context::run from returning auto work = net::make_work_guard(ioc); diff --git a/example/http/client/sync-ssl/http_client_sync_ssl.cpp b/example/http/client/sync-ssl/http_client_sync_ssl.cpp index dba5744d..28728904 100644 --- a/example/http/client/sync-ssl/http_client_sync_ssl.cpp +++ b/example/http/client/sync-ssl/http_client_sync_ssl.cpp @@ -57,7 +57,7 @@ int main(int argc, char** argv) net::io_context ioc; // The SSL context is required, and holds certificates - ssl::context ctx{ssl::context::sslv23_client}; + ssl::context ctx(ssl::context::sslv23_client); // This holds the root certificate used for verification load_root_certificates(ctx); @@ -66,8 +66,8 @@ int main(int argc, char** argv) ctx.set_verify_mode(ssl::verify_peer); // These objects perform our I/O - tcp::resolver resolver{ioc}; - beast::ssl_stream stream{ioc, ctx}; + tcp::resolver resolver(ioc); + beast::ssl_stream stream(ioc, ctx); // Set SNI Hostname (many hosts need this to handshake successfully) if(! SSL_set_tlsext_host_name(stream.native_handle(), host)) @@ -80,7 +80,7 @@ int main(int argc, char** argv) auto const results = resolver.resolve(host, port); // Make the connection on the IP address we get from a lookup - net::connect(stream.next_layer(), results.begin(), results.end()); + beast::connect(beast::get_lowest_layer(stream), results); // Perform the SSL handshake stream.handshake(ssl::stream_base::client); diff --git a/example/http/client/sync/http_client_sync.cpp b/example/http/client/sync/http_client_sync.cpp index 130aa719..bef72344 100644 --- a/example/http/client/sync/http_client_sync.cpp +++ b/example/http/client/sync/http_client_sync.cpp @@ -53,14 +53,14 @@ int main(int argc, char** argv) net::io_context ioc; // These objects perform our I/O - tcp::resolver resolver{ioc}; - tcp::socket socket{ioc}; + tcp::resolver resolver(ioc); + beast::tcp_stream stream(ioc); // Look up the domain name auto const results = resolver.resolve(host, port); // Make the connection on the IP address we get from a lookup - net::connect(socket, results.begin(), results.end()); + beast::connect(stream, results); // Set up an HTTP GET request message http::request req{http::verb::get, target, version}; @@ -68,7 +68,7 @@ int main(int argc, char** argv) req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); // Send the HTTP request to the remote host - http::write(socket, req); + http::write(stream, req); // This buffer is used for reading and must be persisted beast::flat_buffer buffer; @@ -77,14 +77,14 @@ int main(int argc, char** argv) http::response res; // Receive the HTTP response - http::read(socket, buffer, res); + http::read(stream, buffer, res); // Write the message to standard out std::cout << res << std::endl; // Gracefully close the socket beast::error_code ec; - socket.shutdown(tcp::socket::shutdown_both, ec); + stream.socket().shutdown(tcp::socket::shutdown_both, ec); // not_connected happens sometimes // so don't bother reporting it. diff --git a/example/http/server/async-ssl/http_server_async_ssl.cpp b/example/http/server/async-ssl/http_server_async_ssl.cpp index 0115f0d5..75f8bd69 100644 --- a/example/http/server/async-ssl/http_server_async_ssl.cpp +++ b/example/http/server/async-ssl/http_server_async_ssl.cpp @@ -255,7 +255,7 @@ class session : public std::enable_shared_from_this } }; - beast::ssl_stream> stream_; + beast::ssl_stream stream_; beast::flat_buffer buffer_; std::shared_ptr doc_root_; http::request req_; @@ -387,8 +387,10 @@ public: // Accepts incoming connections and launches the sessions class listener : public std::enable_shared_from_this { + net::io_context& ioc_; ssl::context& ctx_; tcp::acceptor acceptor_; + tcp::socket socket_; std::shared_ptr doc_root_; public: @@ -397,8 +399,10 @@ public: ssl::context& ctx, tcp::endpoint endpoint, std::shared_ptr const& doc_root) - : ctx_(ctx) + : ioc_(ioc) + , ctx_(ctx) , acceptor_(ioc) + , socket_(beast::make_strand(ioc)) , doc_root_(doc_root) { beast::error_code ec; @@ -450,13 +454,14 @@ public: do_accept() { acceptor_.async_accept( + socket_, beast::bind_front_handler( &listener::on_accept, shared_from_this())); } void - on_accept(beast::error_code ec, tcp::socket socket) + on_accept(beast::error_code ec) { if(ec) { @@ -466,11 +471,14 @@ public: { // Create the session and run it std::make_shared( - std::move(socket), + std::move(socket_), ctx_, doc_root_)->run(); } + // Make sure each session gets its own strand + socket_ = tcp::socket(beast::make_strand(ioc_)); + // Accept another connection do_accept(); } diff --git a/example/http/server/async/http_server_async.cpp b/example/http/server/async/http_server_async.cpp index 5839dd7c..b9efa92a 100644 --- a/example/http/server/async/http_server_async.cpp +++ b/example/http/server/async/http_server_async.cpp @@ -251,7 +251,7 @@ class session : public std::enable_shared_from_this } }; - beast::tcp_stream stream_; + beast::tcp_stream stream_; beast::flat_buffer buffer_; std::shared_ptr doc_root_; http::request req_; @@ -259,7 +259,7 @@ class session : public std::enable_shared_from_this send_lambda lambda_; public: - // Take ownership of the socket + // Take ownership of the stream session( tcp::socket&& socket, std::shared_ptr const& doc_root) @@ -352,7 +352,9 @@ public: // Accepts incoming connections and launches the sessions class listener : public std::enable_shared_from_this { + net::io_context& ioc_; tcp::acceptor acceptor_; + tcp::socket socket_; std::shared_ptr doc_root_; public: @@ -360,7 +362,9 @@ public: net::io_context& ioc, tcp::endpoint endpoint, std::shared_ptr const& doc_root) - : acceptor_(ioc) + : ioc_(ioc) + , acceptor_(beast::make_strand(ioc)) + , socket_(beast::make_strand(ioc)) , doc_root_(doc_root) { beast::error_code ec; @@ -412,13 +416,14 @@ public: do_accept() { acceptor_.async_accept( + socket_, beast::bind_front_handler( &listener::on_accept, shared_from_this())); } void - on_accept(beast::error_code ec, tcp::socket socket) + on_accept(beast::error_code ec) { if(ec) { @@ -428,10 +433,13 @@ public: { // Create the session and run it std::make_shared( - std::move(socket), + std::move(socket_), doc_root_)->run(); } + // Make sure each session gets its own strand + socket_ = tcp::socket(beast::make_strand(ioc_)); + // Accept another connection do_accept(); } diff --git a/example/http/server/coro-ssl/http_server_coro_ssl.cpp b/example/http/server/coro-ssl/http_server_coro_ssl.cpp index 89eec085..d4bdd30a 100644 --- a/example/http/server/coro-ssl/http_server_coro_ssl.cpp +++ b/example/http/server/coro-ssl/http_server_coro_ssl.cpp @@ -207,11 +207,6 @@ handle_request( //------------------------------------------------------------------------------ -// The type of stream to use -// Stackful coroutines are already stranded. -using stream_type = - beast::ssl_stream>; - // Report a failure void fail(beast::error_code ec, char const* what) @@ -223,13 +218,13 @@ fail(beast::error_code ec, char const* what) // The function object is used to send an HTTP message. struct send_lambda { - stream_type& stream_; + beast::ssl_stream& stream_; bool& close_; beast::error_code& ec_; net::yield_context yield_; send_lambda( - stream_type& stream, + beast::ssl_stream& stream, bool& close, beast::error_code& ec, net::yield_context yield) @@ -258,7 +253,7 @@ struct send_lambda // Handles an HTTP server connection void do_session( - stream_type& stream, + beast::ssl_stream& stream, std::shared_ptr const& doc_root, net::yield_context yield) { @@ -357,10 +352,11 @@ do_listen( fail(ec, "accept"); else net::spawn( - acceptor.get_executor().context(), + acceptor.get_executor(), std::bind( &do_session, - stream_type(std::move(socket), ctx), + beast::ssl_stream( + std::move(socket), ctx), doc_root, std::placeholders::_1)); } diff --git a/example/http/server/coro/http_server_coro.cpp b/example/http/server/coro/http_server_coro.cpp index 1ad41359..6cf41f24 100644 --- a/example/http/server/coro/http_server_coro.cpp +++ b/example/http/server/coro/http_server_coro.cpp @@ -204,10 +204,6 @@ handle_request( //------------------------------------------------------------------------------ -// The type of stream to use. -// Stackful coroutines are already stranded. -using stream_type = beast::tcp_stream; - // Report a failure void fail(beast::error_code ec, char const* what) @@ -219,13 +215,13 @@ fail(beast::error_code ec, char const* what) // The function object is used to send an HTTP message. struct send_lambda { - stream_type& stream_; + beast::tcp_stream& stream_; bool& close_; beast::error_code& ec_; net::yield_context yield_; send_lambda( - stream_type& stream, + beast::tcp_stream& stream, bool& close, beast::error_code& ec, net::yield_context yield) @@ -254,7 +250,7 @@ struct send_lambda // Handles an HTTP server connection void do_session( - stream_type& stream, + beast::tcp_stream& stream, std::shared_ptr const& doc_root, net::yield_context yield) { @@ -339,10 +335,10 @@ do_listen( fail(ec, "accept"); else net::spawn( - acceptor.get_executor().context(), + acceptor.get_executor(), std::bind( &do_session, - stream_type(std::move(socket)), + beast::tcp_stream(std::move(socket)), doc_root, std::placeholders::_1)); } diff --git a/example/http/server/fast/http_server_fast.cpp b/example/http/server/fast/http_server_fast.cpp index d6814542..a9e487b3 100644 --- a/example/http/server/fast/http_server_fast.cpp +++ b/example/http/server/fast/http_server_fast.cpp @@ -99,7 +99,7 @@ private: std::string doc_root_; // The socket for the currently connected client. - tcp::socket socket_{acceptor_.get_executor().context()}; + tcp::socket socket_{acceptor_.get_executor()}; // The buffer for performing reads beast::flat_static_buffer<8192> buffer_; @@ -112,7 +112,7 @@ private: // The timer putting a time limit on requests. net::basic_waitable_timer request_deadline_{ - acceptor_.get_executor().context(), (std::chrono::steady_clock::time_point::max)()}; + acceptor_.get_executor(), (std::chrono::steady_clock::time_point::max)()}; // The string-based response message. boost::optional>> string_response_; diff --git a/example/http/server/flex/http_server_flex.cpp b/example/http/server/flex/http_server_flex.cpp index 01d29503..fe0220c2 100644 --- a/example/http/server/flex/http_server_flex.cpp +++ b/example/http/server/flex/http_server_flex.cpp @@ -208,13 +208,6 @@ handle_request( //------------------------------------------------------------------------------ -// The type of plain streams -using plain_stream_type = beast::tcp_stream; - -// The type of TLS streams -using ssl_stream_type = - beast::ssl_stream>; - // Report a failure void fail(beast::error_code ec, char const* what) @@ -358,7 +351,7 @@ class plain_session : public session , public std::enable_shared_from_this { - plain_stream_type stream_; + beast::tcp_stream stream_; public: // Create the session @@ -374,7 +367,7 @@ public: } // Called by the base class - plain_stream_type& + beast::tcp_stream& stream() { return stream_; @@ -403,7 +396,7 @@ class ssl_session : public session , public std::enable_shared_from_this { - ssl_stream_type stream_; + beast::ssl_stream stream_; public: // Create the session @@ -420,7 +413,7 @@ public: } // Called by the base class - ssl_stream_type& + beast::ssl_stream& stream() { return stream_; @@ -442,6 +435,7 @@ public: &ssl_session::on_handshake, shared_from_this())); } + void on_handshake( beast::error_code ec, @@ -484,14 +478,14 @@ public: // Detects SSL handshakes class detect_session : public std::enable_shared_from_this { - plain_stream_type stream_; + beast::tcp_stream stream_; ssl::context& ctx_; std::shared_ptr doc_root_; beast::flat_buffer buffer_; public: detect_session( - tcp::socket socket, + tcp::socket&& socket, ssl::context& ctx, std::shared_ptr const& doc_root) : stream_(std::move(socket)) @@ -544,8 +538,10 @@ public: // Accepts incoming connections and launches the sessions class listener : public std::enable_shared_from_this { + net::io_context& ioc_; ssl::context& ctx_; tcp::acceptor acceptor_; + tcp::socket socket_; std::shared_ptr doc_root_; public: @@ -554,8 +550,10 @@ public: ssl::context& ctx, tcp::endpoint endpoint, std::shared_ptr const& doc_root) - : ctx_(ctx) - , acceptor_(ioc) + : ioc_(ioc) + , ctx_(ctx) + , acceptor_(beast::make_strand(ioc)) + , socket_(beast::make_strand(ioc)) , doc_root_(doc_root) { beast::error_code ec; @@ -607,13 +605,14 @@ public: do_accept() { acceptor_.async_accept( + socket_, beast::bind_front_handler( &listener::on_accept, shared_from_this())); } void - on_accept(beast::error_code ec, tcp::socket sock) + on_accept(beast::error_code ec) { if(ec) { @@ -623,11 +622,14 @@ public: { // Create the detector session and run it std::make_shared( - std::move(sock), + std::move(socket_), ctx_, doc_root_)->run(); } + // Make sure each session gets its own strand + socket_ = tcp::socket(beast::make_strand(ioc_)); + // Accept another connection do_accept(); } diff --git a/example/http/server/small/http_server_small.cpp b/example/http/server/small/http_server_small.cpp index f5d89fab..0e8762e0 100644 --- a/example/http/server/small/http_server_small.cpp +++ b/example/http/server/small/http_server_small.cpp @@ -76,7 +76,7 @@ private: // The timer for putting a deadline on connection processing. net::basic_waitable_timer deadline_{ - socket_.get_executor().context(), std::chrono::seconds(60)}; + socket_.get_executor(), std::chrono::seconds(60)}; // Asynchronously receive a complete request message. void diff --git a/example/http/server/stackless-ssl/http_server_stackless_ssl.cpp b/example/http/server/stackless-ssl/http_server_stackless_ssl.cpp index 2132c56d..b04eec51 100644 --- a/example/http/server/stackless-ssl/http_server_stackless_ssl.cpp +++ b/example/http/server/stackless-ssl/http_server_stackless_ssl.cpp @@ -209,10 +209,6 @@ handle_request( //------------------------------------------------------------------------------ -// The type of TLS streams -using ssl_stream_type = - beast::ssl_stream>; - // Report a failure void fail(beast::error_code ec, char const* what) @@ -264,7 +260,7 @@ class session } }; - ssl_stream_type stream_; + beast::ssl_stream stream_; beast::flat_buffer buffer_; std::shared_ptr doc_root_; http::request req_; @@ -383,6 +379,7 @@ class listener : public net::coroutine , public std::enable_shared_from_this { + net::io_context& ioc_; ssl::context& ctx_; tcp::acceptor acceptor_; tcp::socket socket_; @@ -394,9 +391,10 @@ public: ssl::context& ctx, tcp::endpoint endpoint, std::shared_ptr const& doc_root) - : ctx_(ctx) - , acceptor_(ioc) - , socket_(ioc) + : ioc_(ioc) + , ctx_(ctx) + , acceptor_(beast::make_strand(ioc)) + , socket_(beast::make_strand(ioc)) , doc_root_(doc_root) { beast::error_code ec; @@ -470,6 +468,9 @@ public: ctx_, doc_root_)->run(); } + + // Make sure each session gets its own strand + socket_ = tcp::socket(beast::make_strand(ioc_)); } } } diff --git a/example/http/server/stackless/http_server_stackless.cpp b/example/http/server/stackless/http_server_stackless.cpp index 1161933c..e85aabba 100644 --- a/example/http/server/stackless/http_server_stackless.cpp +++ b/example/http/server/stackless/http_server_stackless.cpp @@ -205,9 +205,6 @@ handle_request( //------------------------------------------------------------------------------ -// The type of stream to use -using stream_type = beast::tcp_stream; - // Report a failure void fail(beast::error_code ec, char const* what) @@ -258,7 +255,7 @@ class session } }; - stream_type stream_; + beast::tcp_stream stream_; beast::flat_buffer buffer_; std::shared_ptr doc_root_; http::request req_; @@ -269,7 +266,7 @@ public: // Take ownership of the socket explicit session( - tcp::socket socket, + tcp::socket&& socket, std::shared_ptr const& doc_root) : stream_(std::move(socket)) , doc_root_(doc_root) @@ -348,6 +345,7 @@ class listener : public net::coroutine , public std::enable_shared_from_this { + net::io_context& ioc_; tcp::acceptor acceptor_; tcp::socket socket_; std::shared_ptr doc_root_; @@ -357,8 +355,9 @@ public: net::io_context& ioc, tcp::endpoint endpoint, std::shared_ptr const& doc_root) - : acceptor_(ioc) - , socket_(ioc) + : ioc_(ioc) + , acceptor_(beast::make_strand(ioc)) + , socket_(beast::make_strand(ioc)) , doc_root_(doc_root) { beast::error_code ec; @@ -429,6 +428,9 @@ public: std::move(socket_), doc_root_)->run(); } + + // Make sure each session gets its own strand + socket_ = tcp::socket(beast::make_strand(ioc_)); } } } diff --git a/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp b/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp index 7921545f..1f3e748a 100644 --- a/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp +++ b/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp @@ -45,8 +45,8 @@ fail(beast::error_code ec, char const* what) class session : public std::enable_shared_from_this { tcp::resolver resolver_; - websocket::stream>> ws_; + websocket::stream< + beast::ssl_stream> ws_; beast::multi_buffer buffer_; std::string host_; std::string text_; @@ -55,8 +55,8 @@ public: // Resolver and socket require an io_context explicit session(net::io_context& ioc, ssl::context& ctx) - : resolver_(ioc) - , ws_(ioc, ctx) + : resolver_(beast::make_strand(ioc)) + , ws_(beast::make_strand(ioc), ctx) { } diff --git a/example/websocket/client/async/websocket_client_async.cpp b/example/websocket/client/async/websocket_client_async.cpp index 3026dfb1..124b25c9 100644 --- a/example/websocket/client/async/websocket_client_async.cpp +++ b/example/websocket/client/async/websocket_client_async.cpp @@ -40,8 +40,7 @@ fail(beast::error_code ec, char const* what) class session : public std::enable_shared_from_this { tcp::resolver resolver_; - websocket::stream< - beast::tcp_stream> ws_; + websocket::stream ws_; beast::multi_buffer buffer_; std::string host_; std::string text_; @@ -50,8 +49,8 @@ public: // Resolver and socket require an io_context explicit session(net::io_context& ioc) - : resolver_(ioc) - , ws_(ioc) + : resolver_(beast::make_strand(ioc)) + , ws_(beast::make_strand(ioc)) { } diff --git a/example/websocket/client/coro-ssl/websocket_client_coro_ssl.cpp b/example/websocket/client/coro-ssl/websocket_client_coro_ssl.cpp index b0d16ca5..cd6ff392 100644 --- a/example/websocket/client/coro-ssl/websocket_client_coro_ssl.cpp +++ b/example/websocket/client/coro-ssl/websocket_client_coro_ssl.cpp @@ -54,9 +54,9 @@ do_session( beast::error_code ec; // These objects perform our I/O - tcp::resolver resolver{ioc}; - websocket::stream>> ws(ioc, ctx); + tcp::resolver resolver(ioc); + websocket::stream< + beast::ssl_stream> ws(ioc, ctx); // Look up the domain name auto const results = resolver.async_resolve(host, port, yield[ec]); diff --git a/example/websocket/client/coro/websocket_client_coro.cpp b/example/websocket/client/coro/websocket_client_coro.cpp index 0b48a2ae..5c2c2c3f 100644 --- a/example/websocket/client/coro/websocket_client_coro.cpp +++ b/example/websocket/client/coro/websocket_client_coro.cpp @@ -48,9 +48,8 @@ do_session( beast::error_code ec; // These objects perform our I/O - tcp::resolver resolver{ioc}; - websocket::stream< - beast::tcp_stream> ws(ioc); + tcp::resolver resolver(ioc); + websocket::stream ws(ioc); // Look up the domain name auto const results = resolver.async_resolve(host, port, yield[ec]); diff --git a/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp b/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp index 6c94e1d5..5dcaef92 100644 --- a/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp +++ b/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp @@ -48,13 +48,13 @@ fail(beast::error_code ec, char const* what) // Echoes back all received WebSocket messages class session : public std::enable_shared_from_this { - websocket::stream>> ws_; + websocket::stream< + beast::ssl_stream> ws_; beast::multi_buffer buffer_; public: // Take ownership of the socket - session(tcp::socket socket, ssl::context& ctx) + session(tcp::socket&& socket, ssl::context& ctx) : ws_(std::move(socket), ctx) { } @@ -172,16 +172,20 @@ public: // Accepts incoming connections and launches the sessions class listener : public std::enable_shared_from_this { + net::io_context& ioc_; ssl::context& ctx_; tcp::acceptor acceptor_; + tcp::socket socket_; public: listener( net::io_context& ioc, ssl::context& ctx, tcp::endpoint endpoint) - : ctx_(ctx) - , acceptor_(ioc) + : ioc_(ioc) + , ctx_(ctx) + , acceptor_(beast::make_strand(ioc)) + , socket_(beast::make_strand(ioc)) { beast::error_code ec; @@ -232,13 +236,14 @@ public: do_accept() { acceptor_.async_accept( + socket_, beast::bind_front_handler( &listener::on_accept, shared_from_this())); } void - on_accept(beast::error_code ec, tcp::socket socket) + on_accept(beast::error_code ec) { if(ec) { @@ -247,9 +252,12 @@ public: else { // Create the session and run it - std::make_shared(std::move(socket), ctx_)->run(); + std::make_shared(std::move(socket_), ctx_)->run(); } + // Make sure each session gets its own strand + socket_ = tcp::socket(beast::make_strand(ioc_)); + // Accept another connection do_accept(); } diff --git a/example/websocket/server/async/websocket_server_async.cpp b/example/websocket/server/async/websocket_server_async.cpp index 57cb6b45..92417c9e 100644 --- a/example/websocket/server/async/websocket_server_async.cpp +++ b/example/websocket/server/async/websocket_server_async.cpp @@ -43,13 +43,13 @@ fail(beast::error_code ec, char const* what) // Echoes back all received WebSocket messages class session : public std::enable_shared_from_this { - websocket::stream> ws_; + websocket::stream ws_; beast::multi_buffer buffer_; public: // Take ownership of the socket explicit - session(tcp::socket socket) + session(tcp::socket&& socket) : ws_(std::move(socket)) { } @@ -146,13 +146,17 @@ public: // Accepts incoming connections and launches the sessions class listener : public std::enable_shared_from_this { + net::io_context& ioc_; tcp::acceptor acceptor_; + tcp::socket socket_; public: listener( net::io_context& ioc, tcp::endpoint endpoint) - : acceptor_(ioc) + : ioc_(ioc) + , acceptor_(ioc) + , socket_(beast::make_strand(ioc_)) { beast::error_code ec; @@ -203,13 +207,14 @@ public: do_accept() { acceptor_.async_accept( + socket_, beast::bind_front_handler( &listener::on_accept, shared_from_this())); } void - on_accept(beast::error_code ec, tcp::socket socket) + on_accept(beast::error_code ec) { if(ec) { @@ -218,9 +223,12 @@ public: else { // Create the session and run it - std::make_shared(std::move(socket))->run(); + std::make_shared(std::move(socket_))->run(); } + // Make sure each session gets its own strand + socket_ = tcp::socket(beast::make_strand(ioc_)); + // Accept another connection do_accept(); } diff --git a/example/websocket/server/chat-multi/http_session.hpp b/example/websocket/server/chat-multi/http_session.hpp index 0f3275f2..98cd9e9d 100644 --- a/example/websocket/server/chat-multi/http_session.hpp +++ b/example/websocket/server/chat-multi/http_session.hpp @@ -21,7 +21,7 @@ */ class http_session : public boost::enable_shared_from_this { - beast::tcp_stream stream_; + beast::tcp_stream stream_; beast::flat_buffer buffer_; boost::shared_ptr state_; http::request req_; diff --git a/example/websocket/server/chat-multi/listener.cpp b/example/websocket/server/chat-multi/listener.cpp index 9c618714..0ea91b59 100644 --- a/example/websocket/server/chat-multi/listener.cpp +++ b/example/websocket/server/chat-multi/listener.cpp @@ -16,7 +16,9 @@ listener( net::io_context& ioc, tcp::endpoint endpoint, boost::shared_ptr const& state) - : acceptor_(ioc) + : ioc_(ioc) + , acceptor_(ioc) + , socket_(beast::make_strand(ioc)) , state_(state) { beast::error_code ec; @@ -61,6 +63,7 @@ run() { // Start accepting a connection acceptor_.async_accept( + socket_, beast::bind_front_handler( &listener::on_accept, shared_from_this())); @@ -80,18 +83,22 @@ fail(beast::error_code ec, char const* what) // Handle a connection void listener:: -on_accept(beast::error_code ec, tcp::socket socket) +on_accept(beast::error_code ec) { if(ec) return fail(ec, "accept"); else // Launch a new session for this connection boost::make_shared( - std::move(socket), + std::move(socket_), state_)->run(); + // Make sure each session gets its own strand + socket_ = tcp::socket(beast::make_strand(ioc_)); + // Accept another connection acceptor_.async_accept( + socket_, beast::bind_front_handler( &listener::on_accept, shared_from_this())); diff --git a/example/websocket/server/chat-multi/listener.hpp b/example/websocket/server/chat-multi/listener.hpp index 56240fb0..7c09bad2 100644 --- a/example/websocket/server/chat-multi/listener.hpp +++ b/example/websocket/server/chat-multi/listener.hpp @@ -22,11 +22,13 @@ class shared_state; // Accepts incoming connections and launches the sessions class listener : public boost::enable_shared_from_this { + net::io_context& ioc_; tcp::acceptor acceptor_; + tcp::socket socket_; boost::shared_ptr state_; void fail(beast::error_code ec, char const* what); - void on_accept(beast::error_code ec, tcp::socket socket); + void on_accept(beast::error_code ec); public: listener( diff --git a/example/websocket/server/chat-multi/websocket_session.cpp b/example/websocket/server/chat-multi/websocket_session.cpp index 70af3eba..f8c86326 100644 --- a/example/websocket/server/chat-multi/websocket_session.cpp +++ b/example/websocket/server/chat-multi/websocket_session.cpp @@ -83,17 +83,22 @@ void websocket_session:: send(boost::shared_ptr const& ss) { - // Get on the strand if we aren't already, - // otherwise we will concurrently access - // objects which are not thread-safe. - if(! ws_.get_executor().running_in_this_thread()) - return net::post( - ws_.get_executor(), - beast::bind_front_handler( - &websocket_session::send, - shared_from_this(), - ss)); + // Post our work to the strand, this ensures + // that the members of `this` will not be + // accessed concurrently. + net::post( + ws_.get_executor(), + beast::bind_front_handler( + &websocket_session::on_send, + shared_from_this(), + ss)); +} + +void +websocket_session:: +on_send(boost::shared_ptr const& ss) +{ // Always add to queue queue_.push_back(ss); diff --git a/example/websocket/server/chat-multi/websocket_session.hpp b/example/websocket/server/chat-multi/websocket_session.hpp index a3604ccd..04ddc4c4 100644 --- a/example/websocket/server/chat-multi/websocket_session.hpp +++ b/example/websocket/server/chat-multi/websocket_session.hpp @@ -27,7 +27,7 @@ class shared_state; class websocket_session : public boost::enable_shared_from_this { beast::flat_buffer buffer_; - websocket::stream> ws_; + websocket::stream ws_; boost::shared_ptr state_; std::vector> queue_; @@ -50,6 +50,10 @@ public: // Send a message void send(boost::shared_ptr const& ss); + +private: + void + on_send(boost::shared_ptr const& ss); }; template diff --git a/example/websocket/server/coro-ssl/websocket_server_coro_ssl.cpp b/example/websocket/server/coro-ssl/websocket_server_coro_ssl.cpp index 55854343..ab48c96a 100644 --- a/example/websocket/server/coro-ssl/websocket_server_coro_ssl.cpp +++ b/example/websocket/server/coro-ssl/websocket_server_coro_ssl.cpp @@ -38,11 +38,6 @@ using tcp = boost::asio::ip::tcp; // from //------------------------------------------------------------------------------ -// The type of websocket stream to use -// Stackful coroutines are already stranded. -using ws_type = websocket::stream>>; - // Report a failure void fail(beast::error_code ec, char const* what) @@ -53,7 +48,8 @@ fail(beast::error_code ec, char const* what) // Echoes back all received WebSocket messages void do_session( - ws_type& ws, + websocket::stream< + beast::ssl_stream>& ws, net::yield_context yield) { beast::error_code ec; @@ -153,10 +149,11 @@ do_listen( fail(ec, "accept"); else net::spawn( - acceptor.get_executor().context(), + acceptor.get_executor(), std::bind( &do_session, - ws_type(std::move(socket), ctx), + websocket::stream>(std::move(socket), ctx), std::placeholders::_1)); } } diff --git a/example/websocket/server/coro/websocket_server_coro.cpp b/example/websocket/server/coro/websocket_server_coro.cpp index 35b83ccb..8403d3bb 100644 --- a/example/websocket/server/coro/websocket_server_coro.cpp +++ b/example/websocket/server/coro/websocket_server_coro.cpp @@ -33,11 +33,6 @@ using tcp = boost::asio::ip::tcp; // from //------------------------------------------------------------------------------ -// The type of websocket stream to use -// Stackful coroutines are already stranded. -using ws_type = websocket::stream< - beast::tcp_stream>; - // Report a failure void fail(beast::error_code ec, char const* what) @@ -47,7 +42,9 @@ fail(beast::error_code ec, char const* what) // Echoes back all received WebSocket messages void -do_session(ws_type& ws, net::yield_context yield) +do_session( + websocket::stream& ws, + net::yield_context yield) { beast::error_code ec; @@ -133,10 +130,11 @@ do_listen( fail(ec, "accept"); else net::spawn( - acceptor.get_executor().context(), + acceptor.get_executor(), std::bind( &do_session, - ws_type(std::move(socket)), + websocket::stream< + beast::tcp_stream>(std::move(socket)), std::placeholders::_1)); } } @@ -157,7 +155,7 @@ int main(int argc, char* argv[]) auto const threads = std::max(1, std::atoi(argv[3])); // The io_context is required for all I/O - net::io_context ioc{threads}; + net::io_context ioc(threads); // Spawn a listening port net::spawn(ioc, diff --git a/example/websocket/server/fast/websocket_server_fast.cpp b/example/websocket/server/fast/websocket_server_fast.cpp index a0bd5f41..da57d20e 100644 --- a/example/websocket/server/fast/websocket_server_fast.cpp +++ b/example/websocket/server/fast/websocket_server_fast.cpp @@ -78,13 +78,8 @@ setup_stream(websocket::stream& ws) //------------------------------------------------------------------------------ -// The type of websocket stream to use -// Stackful coroutines are already stranded. -using ws_type = websocket::stream< - beast::tcp_stream>; - void -do_sync_session(ws_type& ws) +do_sync_session(websocket::stream& ws) { beast::error_code ec; @@ -135,7 +130,8 @@ do_sync_listen( std::thread(std::bind( &do_sync_session, - ws_type(std::move(socket)))).detach(); + websocket::stream( + std::move(socket)))).detach(); } } @@ -144,13 +140,13 @@ do_sync_listen( // Echoes back all received WebSocket messages class async_session : public std::enable_shared_from_this { - websocket::stream> ws_; + websocket::stream ws_; beast::multi_buffer buffer_; public: // Take ownership of the socket explicit - async_session(tcp::socket socket) + async_session(tcp::socket&& socket) : ws_(std::move(socket)) { setup_stream(ws_); @@ -160,6 +156,11 @@ public: void run() { + // Set suggested timeout settings for the websocket + ws_.set_option( + websocket::stream_base::suggested_settings( + websocket::role_type::server)); + // Set a decorator to change the Server of the handshake ws_.set_option(websocket::stream_base::decorator( [](websocket::response_type& res) @@ -240,13 +241,17 @@ public: // Accepts incoming connections and launches the sessions class async_listener : public std::enable_shared_from_this { + net::io_context& ioc_; tcp::acceptor acceptor_; + tcp::socket socket_; public: async_listener( net::io_context& ioc, tcp::endpoint endpoint) - : acceptor_(ioc) + : ioc_(ioc) + , acceptor_(beast::make_strand(ioc)) + , socket_(beast::make_strand(ioc)) { beast::error_code ec; @@ -297,13 +302,14 @@ public: do_accept() { acceptor_.async_accept( + socket_, beast::bind_front_handler( &async_listener::on_accept, shared_from_this())); } void - on_accept(beast::error_code ec, tcp::socket socket) + on_accept(beast::error_code ec) { if(ec) { @@ -312,9 +318,12 @@ public: else { // Create the async_session and run it - std::make_shared(std::move(socket))->run(); + std::make_shared(std::move(socket_))->run(); } + // Make sure each session gets its own strand + socket_ = tcp::socket(beast::make_strand(ioc_)); + // Accept another connection do_accept(); } @@ -323,12 +332,19 @@ public: //------------------------------------------------------------------------------ void -do_coro_session(ws_type& ws, net::yield_context yield) +do_coro_session( + websocket::stream& ws, + net::yield_context yield) { beast::error_code ec; setup_stream(ws); + // Set suggested timeout settings for the websocket + ws.set_option( + websocket::stream_base::suggested_settings( + websocket::role_type::server)); + // Set a decorator to change the Server of the handshake ws.set_option(websocket::stream_base::decorator( [](websocket::response_type& res) @@ -395,10 +411,11 @@ do_coro_listen( } net::spawn( - acceptor.get_executor().context(), + acceptor.get_executor(), std::bind( &do_coro_session, - ws_type(std::move(socket)), + websocket::stream< + beast::tcp_stream>(std::move(socket)), std::placeholders::_1)); } } diff --git a/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp b/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp index 436f48cc..ecf3757e 100644 --- a/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp +++ b/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp @@ -51,8 +51,7 @@ class session : public net::coroutine , public std::enable_shared_from_this { - websocket::stream>> ws_; + websocket::stream> ws_; beast::multi_buffer buffer_; public: @@ -166,6 +165,7 @@ class listener : public net::coroutine , public std::enable_shared_from_this { + net::io_context& ioc_; ssl::context& ctx_; tcp::acceptor acceptor_; tcp::socket socket_; @@ -175,7 +175,8 @@ public: net::io_context& ioc, ssl::context& ctx, tcp::endpoint endpoint) - : ctx_(ctx) + : ioc_(ioc) + , ctx_(ctx) , acceptor_(ioc) , socket_(ioc) { @@ -247,6 +248,9 @@ public: // Create the session and run it std::make_shared(std::move(socket_), ctx_)->run(); } + + // Make sure each session gets its own strand + socket_ = tcp::socket(beast::make_strand(ioc_)); } } } diff --git a/example/websocket/server/stackless/websocket_server_stackless.cpp b/example/websocket/server/stackless/websocket_server_stackless.cpp index 0bea203f..f7e3a35a 100644 --- a/example/websocket/server/stackless/websocket_server_stackless.cpp +++ b/example/websocket/server/stackless/websocket_server_stackless.cpp @@ -46,8 +46,7 @@ class session : public net::coroutine , public std::enable_shared_from_this { - websocket::stream< - beast::tcp_stream> ws_; + websocket::stream ws_; beast::multi_buffer buffer_; public: @@ -143,6 +142,7 @@ class listener : public net::coroutine , public std::enable_shared_from_this { + net::io_context& ioc_; tcp::acceptor acceptor_; tcp::socket socket_; @@ -150,8 +150,9 @@ public: listener( net::io_context& ioc, tcp::endpoint endpoint) - : acceptor_(ioc) - , socket_(ioc) + : ioc_(ioc) + , acceptor_(beast::make_strand(ioc)) + , socket_(beast::make_strand(ioc)) { beast::error_code ec; @@ -221,6 +222,9 @@ public: // Create the session and run it std::make_shared(std::move(socket_))->run(); } + + // Make sure each session gets its own strand + socket_ = tcp::socket(beast::make_strand(ioc_)); } } } diff --git a/include/boost/beast/_experimental/test/tcp.hpp b/include/boost/beast/_experimental/test/tcp.hpp index 22173587..1dd84a06 100644 --- a/include/boost/beast/_experimental/test/tcp.hpp +++ b/include/boost/beast/_experimental/test/tcp.hpp @@ -11,6 +11,7 @@ #define BOOST_BEAST_TEST_TCP_HPP #include +#include #include #include #include @@ -60,26 +61,28 @@ run_for( ioc.restart(); } -/** Connect two TCP/IP sockets together. +/** Connect two TCP sockets together. */ -inline +template bool connect( - net::ip::tcp::socket& s1, - net::ip::tcp::socket& s2) + net::basic_stream_socket& s1, + net::basic_stream_socket& s2) { - // Sockets must use the same I/O context - BOOST_ASSERT( - std::addressof(s1.get_executor().context()) == - std::addressof(s2.get_executor().context())); - auto& ioc = s1.get_executor().context(); - s1 = net::ip::tcp::socket(ioc); - s2 = net::ip::tcp::socket(ioc); + auto ioc1 = beast::detail::get_io_context(s1); + auto ioc2 = beast::detail::get_io_context(s2); + if(! BEAST_EXPECT(ioc1 != nullptr)) + return false; + if(! BEAST_EXPECT(ioc2 != nullptr)) + return false; + if(! BEAST_EXPECT(ioc1 == ioc2)) + return false; + auto& ioc = *ioc1; try { - net::ip::tcp::acceptor a( - s1.get_executor().context()); + net::basic_socket_acceptor< + net::ip::tcp, Executor> a(s1.get_executor()); auto ep = net::ip::tcp::endpoint( net::ip::make_address_v4("127.0.0.1"), 0); a.open(ep.protocol()); diff --git a/include/boost/beast/core/basic_stream.hpp b/include/boost/beast/core/basic_stream.hpp index c5a96da0..94939358 100644 --- a/include/boost/beast/core/basic_stream.hpp +++ b/include/boost/beast/core/basic_stream.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include // VFALCO This is unfortunate #include #include @@ -21,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -42,14 +42,14 @@ namespace beast { /** A stream socket wrapper with timeouts and associated executor. This stream wraps a `net::basic_stream_socket` to provide - the following additional features: + the following features: @li Optional timeouts may be specified for each logical asynchronous operation performing any reading, writing, or connecting. @li An Executor may be associated with the stream, which will be used to invoke any completion handlers which do not already have - an associated executor. This achieves partial support for + an associated executor. This achieves support for [P1322R0] Networking TS enhancement to enable custom I/O executors. Although the stream supports multiple concurrent outstanding asynchronous @@ -109,7 +109,7 @@ namespace beast { HTTP response with a different timeout. @code - void process_http_1 (tcp_stream& stream, net::yield_context yield) + void process_http_1 (tcp_stream& stream, net::yield_context yield) { flat_buffer buffer; http::request req; @@ -133,7 +133,7 @@ namespace beast { applies to the entire combined operation of reading and writing: @code - void process_http_2 (tcp_stream& stream, net::yield_context yield) + void process_http_2 (tcp_stream& stream, net::yield_context yield) { flat_buffer buffer; http::request req; @@ -155,7 +155,7 @@ namespace beast { thusly: @code - void do_ssl_handshake (net::ssl::stream>& stream, net::yield_context yield) + void do_ssl_handshake (net::ssl::stream& stream, net::yield_context yield) { // Require that the SSL handshake take no longer than 10 seconds stream.expires_after(std::chrono::seconds(10)); @@ -176,7 +176,8 @@ namespace beast { @tparam Executor A type meeting the requirements of Executor to be used for submitting all completion handlers which do not already have an - associated executor. + associated executor. If this type is omitted, the default of `net::executor` + will be used. @par Thread Safety Distinct objects: Safe.@n @@ -190,12 +191,33 @@ namespace beast { @li [P1322R0] Networking TS enhancement to enable custom I/O executors. */ -template +template< + class Protocol, + class Executor = net::executor> class basic_stream #if ! BOOST_BEAST_DOXYGEN : private detail::stream_base #endif { +public: + /// The type of the underlying socket. + using socket_type = + net::basic_stream_socket; + + /** The type of the executor associated with the stream. + + This will be the type of executor used to invoke completion + handlers which do not have an explicit associated executor. + */ + using executor_type = beast::executor_type; + + /// The protocol type. + using protocol_type = Protocol; + + /// The endpoint type. + using endpoint_type = typename Protocol::endpoint; + +private: static_assert(net::is_executor::value, "Executor requirements not met"); @@ -206,33 +228,26 @@ public: #endif struct impl_type : boost::enable_shared_from_this - , boost::empty_value { + // must come first + net::basic_stream_socket< + Protocol, Executor> socket; + op_state read; op_state write; - net::basic_stream_socket socket; - impl_type(impl_type&&) = default; template explicit - impl_type(Executor const&, Args&&...); - - template - impl_type(net::basic_stream_socket&& socket_, - std::true_type); - - template - impl_type(net::basic_stream_socket&& socket_, - std::false_type); + impl_type(Args&&...); impl_type& operator=(impl_type&&) = delete; - Executor const& - ex() const noexcept + beast::executor_type + ex() noexcept { - return this->boost::empty_value::get(); + return this->socket.get_executor(); } void reset(); // set timeouts to never @@ -248,13 +263,6 @@ private: // but the implementation is still waiting on a timer. boost::shared_ptr impl_; - // Restricted until P1322R0 is incorporated into Boost.Asio. - static_assert( - std::is_convertible< - decltype(std::declval().context()), - net::io_context&>::value, - "Only net::io_context is currently supported for executor_type::context()"); - template class async_op; template @@ -287,22 +295,6 @@ private: #endif public: - /** The type of the executor associated with the stream. - - This will be the type of executor used to invoke completion - handlers which do not have an explicit associated executor. - */ - using executor_type = Executor; - - /// The type of the underlying socket. - using socket_type = net::basic_stream_socket; - - /// The protocol type. - using protocol_type = Protocol; - - /// The endpoint type. - using endpoint_type = typename Protocol::endpoint; - /** Destructor This function destroys the stream, cancelling any outstanding @@ -311,106 +303,20 @@ public: */ ~basic_stream(); - /** Construct the stream from an execution context. + /** Constructor - This constructor creates the stream from an execution context. - The underlying socket needs to be open and connected or accepted - before data can be sent or received on it. - - @param ctx An object whose type meets the requirements of - ExecutionContext, which the stream will use to dispatch - handlers that do not have an explicit associated executor. - Currently, the only supported type for `ctx` is `net::io_context`. + This constructor creates the stream by forwarding all arguments + to the underlying socket. The socket then needs to be open and + connected or accepted before data can be sent or received on it. @param args A list of parameters forwarded to the constructor of the underlying socket. - - @note This function does not participate in overload resolution unless: - - @li `std::is_convertible::value` is `true`, and - - @li `std::is_constructible::value` is `true`. - - @see [P1322R0] Networking TS enhancement to enable custom I/O executors - */ - template< - class ExecutionContext, - class... Args - #if ! BOOST_BEAST_DOXYGEN - , class = typename std::enable_if< - std::is_convertible< - ExecutionContext&, - net::execution_context&>::value && - std::is_constructible< - executor_type, - typename ExecutionContext::executor_type>::value - >::type - #endif - > - explicit - basic_stream(ExecutionContext& ctx, Args&&... args); - - /** Construct the stream from an executor. - - This constructor creates the stream from an executor. - The underlying socket needs to be open and connected or accepted - before data can be sent or received on it. - - @param ex The executor to use when dispatching handlers that do - not have an explicit associated executor. - Currently, only executors that return a `net::io_context&` from - `ex.context()` are supported. - - @param args A list of parameters forwarded to the constructor of - the underlying socket. - - @see [P1322R0] Networking TS enhancement to enable custom I/O executors */ template explicit - basic_stream( - executor_type const& ex, Args&&... args); + basic_stream(Args&&... args); - /** Construct the stream from an existing socket. - - This constructor creates the stream from an existing socket. - The underlying socket needs to be open and connected or accepted - before data can be sent or received on it. - - @param socket The socket to construct from. Ownership of the - socket will be transferred by move-construction. - - @param args A list of parameters forwarded to the constructor of - the underlying socket. - - @note This function does not participate in overload resolution unless: - - @li `OtherProtocol` is convertible to `Protocol`, and one of: - - @li `executor_type` of the stream is constructible from the type of - context returned by calling `socket.get_executor().context()`, or - - @li `executor_type` of the stream is constructible from the type of - executor returned by calling `socket.get_executor()`. - */ - template< - class OtherProtocol -#if ! BOOST_BEAST_DOXYGEN - ,class = typename std::enable_if< - std::is_convertible::value && ( - std::is_constructible&>().get_executor().context())>::value || - std::is_constructible&>().get_executor())>::value) - >::type -#endif - > - explicit - basic_stream(net::basic_stream_socket&& socket); - - /** Move constructor. + /** Move constructor @param other The other object from which the move will occur. diff --git a/include/boost/beast/core/detail/get_io_context.hpp b/include/boost/beast/core/detail/get_io_context.hpp new file mode 100644 index 00000000..287a6f53 --- /dev/null +++ b/include/boost/beast/core/detail/get_io_context.hpp @@ -0,0 +1,107 @@ +// +// 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_DETAIL_GET_IO_CONTEXT_HPP +#define BOOST_BEAST_DETAIL_GET_IO_CONTEXT_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace detail { + +//------------------------------------------------------------------------------ + +inline +net::io_context* +get_io_context(net::io_context& ioc) +{ + return std::addressof(ioc); +} + +inline +net::io_context* +get_io_context(net::io_context::executor_type const& ex) +{ + return std::addressof(ex.context()); +} + +inline +net::io_context* +get_io_context(net::strand< + net::io_context::executor_type> const& ex) +{ + return std::addressof( + ex.get_inner_executor().context()); +} + +template +net::io_context* +get_io_context(net::strand const& ex) +{ + return get_io_context(ex.get_inner_executor()); +} + +template< + class T, + class = typename std::enable_if< + std::is_same::value>::type> +net::io_context* +get_io_context(T const& ex) +{ + auto p = ex.template target(); + if(! p) + return nullptr; + return std::addressof(p->context()); +} + +inline +net::io_context* +get_io_context(...) +{ + return nullptr; +} + +//------------------------------------------------------------------------------ + +template +net::io_context* +get_io_context_impl(T& t, std::true_type) +{ + return get_io_context( + t.get_executor()); +} + +template +net::io_context* +get_io_context_impl(T const&, std::false_type) +{ + return nullptr; +} + +// Returns the io_context*, or nullptr, for any object. +template +net::io_context* +get_io_context(T& t) +{ + return get_io_context_impl(t, + has_get_executor{}); +} + +} // detail +} // beast +} // boost + +#endif diff --git a/include/boost/beast/core/detail/stream_base.hpp b/include/boost/beast/core/detail/stream_base.hpp index 140a0d16..1a04e2a4 100644 --- a/include/boost/beast/core/detail/stream_base.hpp +++ b/include/boost/beast/core/detail/stream_base.hpp @@ -15,6 +15,7 @@ #include #include #include +#include namespace boost { namespace beast { @@ -48,9 +49,10 @@ struct stream_base bool pending = false; // if op is pending bool timeout = false; // if timed out + template explicit - op_state(net::io_context& ioc) - : timer(ioc) + op_state(Args&&... args) + : timer(std::forward(args)...) { } }; diff --git a/include/boost/beast/core/impl/basic_stream.hpp b/include/boost/beast/core/impl/basic_stream.hpp index 097b20d5..680234b4 100644 --- a/include/boost/beast/core/impl/basic_stream.hpp +++ b/include/boost/beast/core/impl/basic_stream.hpp @@ -32,44 +32,10 @@ template template basic_stream:: impl_type:: -impl_type( - Executor const& ex_, - Args&&... args) - : boost::empty_value( - boost::empty_init_t{}, ex_) - , read(ex().context()) - , write(ex().context()) - , socket(std::forward(args)...) -{ - reset(); -} - -template -template -basic_stream:: -impl_type:: -impl_type(net::basic_stream_socket&& socket_, - std::true_type) - : boost::empty_value( - boost::empty_init_t{}, socket_.get_executor()) - , read(ex().context()) - , write(ex().context()) - , socket(std::move(socket_)) -{ - reset(); -} - -template -template -basic_stream:: -impl_type:: -impl_type(net::basic_stream_socket&& socket_, - std::false_type) - : boost::empty_value(boost::empty_init_t{}, - socket_.get_executor().context()) - , read(ex().context()) - , write(ex().context()) - , socket(std::move(socket_)) +impl_type(Args&&... args) + : socket(std::forward(args)...) + , read(ex()) + , write(ex()) { reset(); } @@ -434,40 +400,12 @@ basic_stream:: impl_->close(); } -template -template -basic_stream:: -basic_stream(ExecutionContext& ctx, Args&&... args) - : impl_(boost::make_shared( - ctx.get_executor(), - ctx, std::forward(args)...)) -{ - // Restriction is necessary until Asio fully supports P1322R0 - static_assert( - std::is_same::value, - "Only net::io_context is currently supported for ExecutionContext"); -} - template template basic_stream:: -basic_stream( - executor_type const& ex, Args&&... args) +basic_stream(Args&&... args) : impl_(boost::make_shared( - ex, - ex.context(), std::forward(args)...)) -{ -} - -template -template -basic_stream:: -basic_stream(net::basic_stream_socket&& socket) - : impl_(boost::make_shared( - std::move(socket), - std::is_constructible&>().get_executor())>{})) + std::forward(args)...)) { } diff --git a/include/boost/beast/core/stream_traits.hpp b/include/boost/beast/core/stream_traits.hpp index f58703df..f1815953 100644 --- a/include/boost/beast/core/stream_traits.hpp +++ b/include/boost/beast/core/stream_traits.hpp @@ -438,10 +438,10 @@ using is_async_stream = std::integral_constant +template void beast_close_socket( - net::basic_socket& sock) + net::basic_socket& sock) { boost::system::error_code ec; sock.close(ec); diff --git a/include/boost/beast/core/tcp_stream.hpp b/include/boost/beast/core/tcp_stream.hpp index 3f3c4415..2710de12 100644 --- a/include/boost/beast/core/tcp_stream.hpp +++ b/include/boost/beast/core/tcp_stream.hpp @@ -12,20 +12,17 @@ #include #include +#include #include namespace boost { namespace beast { -/** A TCP/IP stream socket with timeouts, rate limits, and executor. - - @tparam Executor The type of executor to use for all completion - handlers which do not already have an associated executor. +/** A TCP/IP stream socket with timeouts, rate limits, and polymorphic executor. @see basic_stream */ -template -using tcp_stream = basic_stream; +using tcp_stream = basic_stream; } // beast } // boost diff --git a/include/boost/beast/http/file_body.hpp b/include/boost/beast/http/file_body.hpp index 42e7a153..f8c8ac25 100644 --- a/include/boost/beast/http/file_body.hpp +++ b/include/boost/beast/http/file_body.hpp @@ -30,6 +30,8 @@ using file_body = basic_file_body; } // beast } // boost +#ifndef BOOST_BEAST_NO_FILE_BODY_WIN32 #include +#endif #endif diff --git a/include/boost/beast/http/impl/file_body_win32.ipp b/include/boost/beast/http/impl/file_body_win32.ipp index 06c8f60d..0969cc9d 100644 --- a/include/boost/beast/http/impl/file_body_win32.ipp +++ b/include/boost/beast/http/impl/file_body_win32.ipp @@ -32,7 +32,7 @@ namespace beast { namespace http { namespace detail { -template +template class write_some_win32_op; } // detail @@ -52,7 +52,7 @@ struct basic_file_body friend class reader; friend struct basic_file_body; - template + template friend class detail::write_some_win32_op; template< class Protocol, bool isRequest, class Fields> @@ -101,7 +101,7 @@ struct basic_file_body class writer { - template + template friend class detail::write_some_win32_op; template< class Protocol, bool isRequest, class Fields> @@ -276,7 +276,6 @@ reset(file_win32&& file, error_code& ec) namespace detail { template -inline boost::winapi::DWORD_ lowPart(Unsigned n) { @@ -286,7 +285,6 @@ lowPart(Unsigned n) } template -inline boost::winapi::DWORD_ highPart(Unsigned n, std::true_type) { @@ -296,7 +294,6 @@ highPart(Unsigned n, std::true_type) } template -inline boost::winapi::DWORD_ highPart(Unsigned, std::false_type) { @@ -304,7 +301,6 @@ highPart(Unsigned, std::false_type) } template -inline boost::winapi::DWORD_ highPart(Unsigned n) { @@ -329,14 +325,17 @@ public: #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR template< - class Protocol, class Handler, - bool isRequest, class Fields> + class Protocol, class Executor, + class Handler, bool isRequest, class Fields> class write_some_win32_op - : public beast::async_op_base< - Handler, typename net::basic_stream_socket< - Protocol>::executor_type> + : public beast::async_op_base { - net::basic_stream_socket& sock_; + static_assert( + std::is_same::value, + "Executor must be net::io_context::executor_type"); + + net::basic_stream_socket< + Protocol, Executor>& sock_; serializer, Fields>& sr_; std::size_t bytes_transferred_ = 0; @@ -346,14 +345,14 @@ public: template write_some_win32_op( Handler_&& h, - net::basic_stream_socket& s, + net::basic_stream_socket< + Protocol, Executor>& s, serializer,Fields>& sr) : async_op_base< - Handler, typename net::basic_stream_socket< - Protocol>::executor_type>( - std::forward(h), - s.get_executor()) + Handler, Executor>( + std::forward(h), + s.get_executor()) , sock_(s) , sr_(sr) { @@ -442,10 +441,13 @@ public: //------------------------------------------------------------------------------ -template +template< + class Protocol, class Executor, + bool isRequest, class Fields> std::size_t write_some( - net::basic_stream_socket& sock, + net::basic_stream_socket< + Protocol, Executor>& sock, serializer, Fields>& sr, error_code& ec) @@ -509,21 +511,23 @@ write_some( #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR template< - class Protocol, + class Protocol, class Executor, bool isRequest, class Fields, class WriteHandler> BOOST_ASIO_INITFN_RESULT_TYPE( WriteHandler, void(error_code, std::size_t)) async_write_some( - net::basic_stream_socket& sock, + net::basic_stream_socket< + Protocol, Executor>& sock, serializer, Fields>& sr, WriteHandler&& handler) { + BOOST_BEAST_HANDLER_INIT( WriteHandler, void(error_code, std::size_t)); detail::write_some_win32_op< - Protocol, + Protocol, Executor, BOOST_ASIO_HANDLER_TYPE(WriteHandler, void(error_code, std::size_t)), isRequest, Fields>{ diff --git a/include/boost/beast/websocket/detail/frame.hpp b/include/boost/beast/websocket/detail/frame.hpp index d4f695f4..8c999fad 100644 --- a/include/boost/beast/websocket/detail/frame.hpp +++ b/include/boost/beast/websocket/detail/frame.hpp @@ -19,7 +19,15 @@ #include #include #include +// This is for +#if BOOST_WORKAROUND(BOOST_MSVC, > 0) +# pragma warning (push) +# pragma warning (disable: 4127) // conditional expression is constant +#endif #include +#if BOOST_WORKAROUND(BOOST_MSVC, > 0) +# pragma warning (pop) +#endif #include namespace boost { diff --git a/include/boost/beast/websocket/impl/stream_impl.hpp b/include/boost/beast/websocket/impl/stream_impl.hpp index 7dd1c914..1976b3b7 100644 --- a/include/boost/beast/websocket/impl/stream_impl.hpp +++ b/include/boost/beast/websocket/impl/stream_impl.hpp @@ -103,7 +103,7 @@ struct stream::impl_type template impl_type(Args&&... args) : stream(std::forward(args)...) - , timer(stream.get_executor().context()) + , timer(stream.get_executor()) { timeout_opt.handshake_timeout = none(); timeout_opt.idle_timeout = none(); diff --git a/include/boost/beast/websocket/impl/teardown.hpp b/include/boost/beast/websocket/impl/teardown.hpp index 65861a56..6bd643c0 100644 --- a/include/boost/beast/websocket/impl/teardown.hpp +++ b/include/boost/beast/websocket/impl/teardown.hpp @@ -25,14 +25,18 @@ namespace websocket { namespace detail { -template +template< + class Protocol, class Executor, + class Handler> class teardown_tcp_op : public beast::async_op_base< Handler, beast::executor_type< - net::ip::tcp::socket>> + net::basic_stream_socket< + Protocol, Executor>>> , public net::coroutine { - using socket_type = net::ip::tcp::socket; + using socket_type = + net::basic_stream_socket; socket_type& s_; role_type role_; @@ -46,8 +50,10 @@ public: role_type role) : async_op_base>( - std::forward(h), s.get_executor()) + net::basic_stream_socket< + Protocol, Executor>>>( + std::forward(h), + s.get_executor()) , s_(s) , role_(role) { @@ -60,7 +66,6 @@ public: std::size_t bytes_transferred = 0, bool cont = true) { - using tcp = net::ip::tcp; BOOST_ASIO_CORO_REENTER(*this) { nb_ = s_.non_blocking(); @@ -68,7 +73,7 @@ public: if(ec) goto upcall; if(role_ == role_type::server) - s_.shutdown(tcp::socket::shutdown_send, ec); + s_.shutdown(net::socket_base::shutdown_send, ec); if(ec) goto upcall; for(;;) @@ -81,7 +86,7 @@ public: { BOOST_ASIO_CORO_YIELD s_.async_wait( - net::ip::tcp::socket::wait_read, + net::socket_base::wait_read, beast::detail::bind_continuation(std::move(*this))); continue; } @@ -100,7 +105,7 @@ public: } } if(role_ == role_type::client) - s_.shutdown(tcp::socket::shutdown_send, ec); + s_.shutdown(net::socket_base::shutdown_send, ec); if(ec) goto upcall; s_.close(ec); @@ -124,15 +129,17 @@ public: //------------------------------------------------------------------------------ +template void teardown( role_type role, - net::ip::tcp::socket& socket, + net::basic_stream_socket< + Protocol, Executor>& socket, error_code& ec) { if(role == role_type::server) socket.shutdown( - net::ip::tcp::socket::shutdown_send, ec); + net::socket_base::shutdown_send, ec); if(ec) return; for(;;) @@ -156,25 +163,32 @@ teardown( } if(role == role_type::client) socket.shutdown( - net::ip::tcp::socket::shutdown_send, ec); + net::socket_base::shutdown_send, ec); if(ec) return; socket.close(ec); } -template +template< + class Protocol, class Executor, + class TeardownHandler> void async_teardown( role_type role, - net::ip::tcp::socket& socket, + net::basic_stream_socket< + Protocol, Executor>& socket, TeardownHandler&& handler) { static_assert(beast::detail::is_invocable< TeardownHandler, void(error_code)>::value, "TeardownHandler requirements not met"); - detail::teardown_tcp_op::type>(std::forward< - TeardownHandler>(handler), socket, role); + detail::teardown_tcp_op< + Protocol, + Executor, + typename std::decay::type>( + std::forward(handler), + socket, + role); } } // websocket diff --git a/include/boost/beast/websocket/stream.hpp b/include/boost/beast/websocket/stream.hpp index 1066176b..62bc50a5 100644 --- a/include/boost/beast/websocket/stream.hpp +++ b/include/boost/beast/websocket/stream.hpp @@ -80,13 +80,12 @@ class frame_test; To declare the @ref stream object with a @ref tcp_stream in a multi-threaded asynchronous program using a strand, you may write: @code - websocket::stream> ws{net::io_context::strand(ioc)}; + websocket::stream ws{net::io_context::strand(ioc)}; @endcode Alternatively, for a single-threaded or synchronous application you may write: @code - websocket::stream> ws(ioc); + websocket::stream ws(ioc); @endcode @tparam NextLayer The type representing the next layer, to which diff --git a/include/boost/beast/websocket/teardown.hpp b/include/boost/beast/websocket/teardown.hpp index 63d5b3e9..3a19e466 100644 --- a/include/boost/beast/websocket/teardown.hpp +++ b/include/boost/beast/websocket/teardown.hpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include namespace boost { @@ -125,11 +125,12 @@ namespace websocket { @param ec Set to the error if any occurred. */ -BOOST_BEAST_DECL +template void teardown( role_type role, - net::ip::tcp::socket& socket, + net::basic_stream_socket< + Protocol, Executor>& socket, error_code& ec); /** Start tearing down a `net::ip::tcp::socket`. @@ -159,11 +160,14 @@ teardown( manner equivalent to using net::io_context::post(). */ -template +template< + class Protocol, class Executor, + class TeardownHandler> void async_teardown( role_type role, - net::ip::tcp::socket& socket, + net::basic_stream_socket< + Protocol, Executor>& socket, TeardownHandler&& handler); } // websocket diff --git a/test/beast/core/CMakeLists.txt b/test/beast/core/CMakeLists.txt index 4f7957cd..0be21dd7 100644 --- a/test/beast/core/CMakeLists.txt +++ b/test/beast/core/CMakeLists.txt @@ -24,6 +24,7 @@ add_executable (tests-beast-core _detail_bind_continuation.cpp _detail_buffer.cpp _detail_clamp.cpp + _detail_get_io_context.cpp _detail_is_invocable.cpp _detail_read.cpp _detail_sha1.cpp diff --git a/test/beast/core/Jamfile b/test/beast/core/Jamfile index fca43e0a..21a5e6f4 100644 --- a/test/beast/core/Jamfile +++ b/test/beast/core/Jamfile @@ -12,6 +12,7 @@ local SOURCES = _detail_bind_continuation.cpp _detail_buffer.cpp _detail_clamp.cpp + _detail_get_io_context.cpp _detail_is_invocable.cpp _detail_read.cpp _detail_sha1.cpp diff --git a/test/beast/core/_detail_get_io_context.cpp b/test/beast/core/_detail_get_io_context.cpp new file mode 100644 index 00000000..a18c848b --- /dev/null +++ b/test/beast/core/_detail_get_io_context.cpp @@ -0,0 +1,57 @@ +// +// 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 +// + +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace detail { + +class get_io_context_test : public beast::unit_test::suite +{ +public: + void + testFunction() + { + struct none + { + }; + + net::io_context ioc; + BEAST_EXPECT(get_io_context(5) == nullptr); + BEAST_EXPECT(get_io_context(none{}) == nullptr); + BEAST_EXPECT(get_io_context(ioc) == &ioc); + BEAST_EXPECT(get_io_context(ioc.get_executor()) == &ioc); + BEAST_EXPECT(get_io_context(make_strand(ioc)) == &ioc); + BEAST_EXPECT(get_io_context(net::executor(ioc.get_executor())) == &ioc); +#if 0 + // VFALCO FIXME + BEAST_EXPECT( + get_io_context( + net::strand( + net::executor(ioc.get_executor()))) == &ioc); +#endif + } + + void + run() override + { + testFunction(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,core,get_io_context); + +} // detail +} // beast +} // boost diff --git a/test/beast/core/basic_stream.cpp b/test/beast/core/basic_stream.cpp index 317210ca..88b18bfa 100644 --- a/test/beast/core/basic_stream.cpp +++ b/test/beast/core/basic_stream.cpp @@ -303,7 +303,6 @@ private: } }; - } // (anon) class basic_stream_test @@ -319,9 +318,6 @@ public: void testSpecialMembers() { - using stream_type = tcp_stream< - net::io_context::executor_type>; - net::io_context ioc; // net::io_context::executor_type @@ -332,7 +328,8 @@ public: basic_stream s2(ex); basic_stream s3(ioc, tcp::v4()); basic_stream s4(std::move(s1)); - s2.socket() = tcp::socket(ioc); + s2.socket() = + net::basic_stream_socket(ioc); BEAST_EXPECT(s1.get_executor() == ex); BEAST_EXPECT(s2.get_executor() == ex); BEAST_EXPECT(s3.get_executor() == ex); @@ -358,14 +355,19 @@ public: basic_stream s1(ex); basic_stream s2(ex, tcp::v4()); basic_stream s3(std::move(s1)); - s2.socket() = tcp::socket(ioc); + #if 0 + s2.socket() = net::basic_stream_socket< + tcp, strand>(ioc); + #endif BEAST_EXPECT(s1.get_executor() == ex); BEAST_EXPECT(s2.get_executor() == ex); BEAST_EXPECT(s3.get_executor() == ex); +#if 0 BEAST_EXPECT((! static_cast< basic_stream const&>( s2).socket().is_open())); +#endif test_sync_stream< basic_stream< @@ -378,6 +380,7 @@ public: // construction from existing socket +#if 0 { tcp::socket sock(ioc); basic_stream stream(std::move(sock)); @@ -387,22 +390,13 @@ public: tcp::socket sock(ioc); basic_stream stream(std::move(sock)); } - - struct other_type - { - }; - - BOOST_STATIC_ASSERT(! std::is_constructible< - stream_type, other_type>::value); - - BOOST_STATIC_ASSERT(! std::is_constructible< - stream_type, other_type, tcp::socket>::value); +#endif // layers { net::socket_base::keep_alive opt; - stream_type s(ioc); + tcp_stream s(ioc); s.socket().open(tcp::v4()); s.socket().get_option(opt); BEAST_EXPECT(! opt.value()); @@ -450,7 +444,7 @@ public: void testRead() { - using stream_type = tcp_stream< + using stream_type = basic_stream; char buf[4]; @@ -585,7 +579,7 @@ public: void testWrite() { - using stream_type = tcp_stream< + using stream_type = basic_stream; char buf[4]; @@ -667,7 +661,7 @@ public: void testConnect() { - using stream_type = tcp_stream< + using stream_type = basic_stream; struct range @@ -1057,7 +1051,7 @@ public: void testMembers() { - using stream_type = tcp_stream< + using stream_type = basic_stream; class handler @@ -1184,7 +1178,7 @@ public: return {}; } - void process_http_1 (tcp_stream& stream, net::yield_context yield) + void process_http_1 (tcp_stream& stream, net::yield_context yield) { flat_buffer buffer; http::request req; @@ -1201,7 +1195,7 @@ public: http::async_write (stream, res, yield); } - void process_http_2 (tcp_stream& stream, net::yield_context yield) + void process_http_2 (tcp_stream& stream, net::yield_context yield) { flat_buffer buffer; http::request req; diff --git a/test/beast/core/tcp_stream.cpp b/test/beast/core/tcp_stream.cpp index 422d96a2..631520e0 100644 --- a/test/beast/core/tcp_stream.cpp +++ b/test/beast/core/tcp_stream.cpp @@ -9,3 +9,37 @@ // Test that header file is self-contained. #include + +#include + +namespace boost { +namespace beast { + +class tcp_stream_test + : public beast::unit_test::suite +{ +public: + using tcp = net::ip::tcp; + + void + testStream() + { + net::io_context ioc; + { + tcp::socket s(ioc); + tcp_stream s1(std::move(s)); + } + } + + void + run() + { + testStream(); + pass(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,core,tcp_stream); + +} // beast +} // boost diff --git a/test/beast/websocket/stream.cpp b/test/beast/websocket/stream.cpp index 7695cb52..7978c7df 100644 --- a/test/beast/websocket/stream.cpp +++ b/test/beast/websocket/stream.cpp @@ -10,6 +10,7 @@ // Test that header file is self-contained. #include +#include #include #include @@ -157,11 +158,10 @@ public: { net::io_context ioc; { - websocket::stream> ws{net::io_context::strand(ioc)}; + websocket::stream ws{make_strand(ioc)}; } { - websocket::stream> ws(ioc); + websocket::stream ws(ioc); } } diff --git a/test/beast/websocket/stream_base.cpp b/test/beast/websocket/stream_base.cpp index 5bf1b1d5..6b176b56 100644 --- a/test/beast/websocket/stream_base.cpp +++ b/test/beast/websocket/stream_base.cpp @@ -28,6 +28,7 @@ public: run() override { testJavadoc(); + pass(); } }; diff --git a/test/doc/websocket_snippets.cpp b/test/doc/websocket_snippets.cpp index 0b25c6f2..d29eabae 100644 --- a/test/doc/websocket_snippets.cpp +++ b/test/doc/websocket_snippets.cpp @@ -59,8 +59,8 @@ boost::ignore_unused(ec); { //[ws_snippet_6 std::string const host = "example.com"; - net::ip::tcp::resolver r{ioc}; - stream> ws{ioc}; + net::ip::tcp::resolver r(ioc); + stream ws(ioc); auto const results = r.resolve(host, "ws"); connect(get_lowest_layer(ws), results.begin(), results.end()); //] @@ -69,7 +69,7 @@ boost::ignore_unused(ec); { //[ws_snippet_7 net::ip::tcp::acceptor acceptor{ioc}; - stream> ws{acceptor.get_executor().context()}; + stream ws{acceptor.get_executor()}; acceptor.accept(get_lowest_layer(ws).socket()); //] }