Fixes to support Asio changes (API Change):

This adjusts Beast's interfaces and implementation to match
the changes in Boost.Asio.
This commit is contained in:
Vinnie Falco
2019-02-18 12:42:24 -08:00
parent ac24e58fb3
commit 6baa607295
58 changed files with 762 additions and 723 deletions

View File

@ -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_BIND=1)
add_definitions (-DBOOST_ASIO_DISABLE_BOOST_DATE_TIME=1) add_definitions (-DBOOST_ASIO_DISABLE_BOOST_DATE_TIME=1)
add_definitions (-DBOOST_ASIO_DISABLE_BOOST_REGEX=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) add_definitions (-DBOOST_COROUTINES_NO_DEPRECATION_WARNING=1)
include_directories (${BOOST_ROOT}) include_directories (${BOOST_ROOT})
@ -174,6 +179,9 @@ endif()
include_directories (.) include_directories (.)
# VFALCO FIXME Need this for recent asio changes
add_definitions (-DBOOST_BEAST_NO_FILE_BODY_WIN32=1)
if (OPENSSL_FOUND) if (OPENSSL_FOUND)
include_directories (${OPENSSL_INCLUDE_DIR}) include_directories (${OPENSSL_INCLUDE_DIR})
endif() endif()

View File

@ -93,6 +93,14 @@ project /boost/beast
<define>BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1 <define>BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1
<define>BOOST_ASIO_DISABLE_BOOST_REGEX=1 <define>BOOST_ASIO_DISABLE_BOOST_REGEX=1
<define>BOOST_COROUTINES_NO_DEPRECATION_WARNING=1 <define>BOOST_COROUTINES_NO_DEPRECATION_WARNING=1
# workaround for asio defect
<define>BOOST_ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE=1
<define>BOOST_ASIO_DISABLE_WINDOWS_STREAM_HANDLE=1
# VFALCO FIXME Need this for recent asio changes
<define>BOOST_BEAST_NO_FILE_BODY_WIN32=1
<toolset>msvc:<cxxflags>"/bigobj" <toolset>msvc:<cxxflags>"/bigobj"
<toolset>msvc-14.1:<cxxflags>"/permissive-" <toolset>msvc-14.1:<cxxflags>"/permissive-"
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1 <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1

View File

@ -331,27 +331,26 @@ public:
} }
}; };
//------------------------------------------------------------------------------
// Handles a plain WebSocket connection // Handles a plain WebSocket connection
class plain_websocket_session class plain_websocket_session
: public websocket_session<plain_websocket_session> : public websocket_session<plain_websocket_session>
, public std::enable_shared_from_this<plain_websocket_session> , public std::enable_shared_from_this<plain_websocket_session>
{ {
websocket::stream< websocket::stream<beast::tcp_stream> ws_;
beast::tcp_stream<net::io_context::strand>> ws_;
bool close_ = false;
public: public:
// Create the session // Create the session
explicit explicit
plain_websocket_session( plain_websocket_session(
beast::tcp_stream<net::io_context::strand>&& stream) beast::tcp_stream&& stream)
: ws_(std::move(stream)) : ws_(std::move(stream))
{ {
} }
// Called by the base class // Called by the base class
websocket::stream< websocket::stream<beast::tcp_stream>&
beast::tcp_stream<net::io_context::strand>>&
ws() ws()
{ {
return ws_; return ws_;
@ -365,48 +364,28 @@ public:
// Accept the WebSocket upgrade request // Accept the WebSocket upgrade request
do_accept(std::move(req)); 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 // Handles an SSL WebSocket connection
class ssl_websocket_session class ssl_websocket_session
: public websocket_session<ssl_websocket_session> : public websocket_session<ssl_websocket_session>
, public std::enable_shared_from_this<ssl_websocket_session> , public std::enable_shared_from_this<ssl_websocket_session>
{ {
websocket::stream<beast::ssl_stream< websocket::stream<
beast::tcp_stream<net::io_context::strand>>> ws_; beast::ssl_stream<beast::tcp_stream>> ws_;
bool eof_ = false;
public: public:
// Create the http_session // Create the ssl_websocket_session
explicit explicit
ssl_websocket_session(beast::ssl_stream< ssl_websocket_session(
beast::tcp_stream<net::io_context::strand>>&& stream) beast::ssl_stream<beast::tcp_stream>&& stream)
: ws_(std::move(stream)) : ws_(std::move(stream))
{ {
} }
// Called by the base class // Start the session
websocket::stream<beast::ssl_stream<
beast::tcp_stream<net::io_context::strand>>>&
ws()
{
return ws_;
}
// Start the asynchronous operation
template<class Body, class Allocator> template<class Body, class Allocator>
void void
run(http::request<Body, http::basic_fields<Allocator>> req) run(http::request<Body, http::basic_fields<Allocator>> req)
@ -415,18 +394,15 @@ public:
do_accept(std::move(req)); do_accept(std::move(req));
} }
void // Called by the base class
do_eof() websocket::stream<
beast::ssl_stream<beast::tcp_stream>>&
ws()
{ {
eof_ = true; return ws_;
// Perform the SSL shutdown
ws_.next_layer().async_shutdown(
beast::bind_front_handler(
&ssl_websocket_session::on_shutdown,
shared_from_this()));
} }
private:
void void
on_shutdown(beast::error_code ec) on_shutdown(beast::error_code ec)
{ {
@ -437,10 +413,12 @@ public:
} }
}; };
//------------------------------------------------------------------------------
template<class Body, class Allocator> template<class Body, class Allocator>
void void
make_websocket_session( make_websocket_session(
beast::tcp_stream<net::io_context::strand> stream, beast::tcp_stream stream,
http::request<Body, http::basic_fields<Allocator>> req) http::request<Body, http::basic_fields<Allocator>> req)
{ {
std::make_shared<plain_websocket_session>( std::make_shared<plain_websocket_session>(
@ -450,7 +428,7 @@ make_websocket_session(
template<class Body, class Allocator> template<class Body, class Allocator>
void void
make_websocket_session( make_websocket_session(
beast::ssl_stream<beast::tcp_stream<net::io_context::strand>> stream, beast::ssl_stream<beast::tcp_stream> stream,
http::request<Body, http::basic_fields<Allocator>> req) http::request<Body, http::basic_fields<Allocator>> req)
{ {
std::make_shared<ssl_websocket_session>( std::make_shared<ssl_websocket_session>(
@ -568,19 +546,15 @@ class http_session
queue queue_; queue queue_;
protected: protected:
net::steady_timer timer_;
beast::flat_buffer buffer_; beast::flat_buffer buffer_;
public: public:
// Construct the session // Construct the session
http_session( http_session(
net::io_context& ioc,
beast::flat_buffer buffer, beast::flat_buffer buffer,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
: doc_root_(doc_root) : doc_root_(doc_root)
, queue_(*this) , queue_(*this)
, timer_(ioc,
(std::chrono::steady_clock::time_point::max)())
, buffer_(std::move(buffer)) , buffer_(std::move(buffer))
{ {
} }
@ -621,7 +595,11 @@ public:
// See if it is a WebSocket Upgrade // See if it is a WebSocket Upgrade
if(websocket::is_upgrade(req_)) 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( return make_websocket_session(
derived().release_stream(), derived().release_stream(),
std::move(req_)); std::move(req_));
@ -663,56 +641,50 @@ public:
} }
}; };
//------------------------------------------------------------------------------
// Handles a plain HTTP connection // Handles a plain HTTP connection
class plain_http_session class plain_http_session
: public http_session<plain_http_session> : public http_session<plain_http_session>
, public std::enable_shared_from_this<plain_http_session> , public std::enable_shared_from_this<plain_http_session>
{ {
beast::tcp_stream<net::io_context::strand> stream_; beast::tcp_stream stream_;
public: public:
// Create the http_session // Create the session
plain_http_session( plain_http_session(
beast::tcp_stream<net::io_context::strand>&& stream, beast::tcp_stream&& stream,
beast::flat_buffer&& buffer, beast::flat_buffer&& buffer,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
: http_session<plain_http_session>( : http_session<plain_http_session>(
stream.get_executor().context(),
std::move(buffer), std::move(buffer),
doc_root) doc_root)
, stream_(std::move(stream)) , stream_(std::move(stream))
{ {
} }
// Start the session
void
run()
{
this->do_read();
}
// Called by the base class // Called by the base class
beast::tcp_stream<net::io_context::strand>& beast::tcp_stream&
stream() stream()
{ {
return stream_; return stream_;
} }
// Called by the base class // Called by the base class
beast::tcp_stream<net::io_context::strand> beast::tcp_stream
release_stream() release_stream()
{ {
return std::move(stream_); return std::move(stream_);
} }
// Start the asynchronous operation // Called by the base class
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();
}
void void
do_eof() do_eof()
{ {
@ -722,70 +694,35 @@ public:
// At this point the connection is closed gracefully // 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 // Handles an SSL HTTP connection
class ssl_http_session class ssl_http_session
: public http_session<ssl_http_session> : public http_session<ssl_http_session>
, public std::enable_shared_from_this<ssl_http_session> , public std::enable_shared_from_this<ssl_http_session>
{ {
beast::ssl_stream< beast::ssl_stream<beast::tcp_stream> stream_;
beast::tcp_stream<net::io_context::strand>> stream_;
bool eof_ = false;
public: public:
// Create the http_session // Create the http_session
ssl_http_session( ssl_http_session(
beast::tcp_stream<net::io_context::strand>&& stream, beast::tcp_stream&& stream,
ssl::context& ctx, ssl::context& ctx,
beast::flat_buffer&& buffer, beast::flat_buffer&& buffer,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
: http_session<ssl_http_session>( : http_session<ssl_http_session>(
stream.get_executor().context(),
std::move(buffer), std::move(buffer),
doc_root) doc_root)
, stream_(std::move(stream), ctx) , stream_(std::move(stream), ctx)
{ {
} }
// Called by the base class // Start the session
beast::ssl_stream<
beast::tcp_stream<net::io_context::strand>>&
stream()
{
return stream_;
}
// Called by the base class
beast::ssl_stream<
beast::tcp_stream<net::io_context::strand>>
release_stream()
{
return std::move(stream_);
}
// Start the asynchronous operation
void void
run() 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. // Set the timeout.
beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
@ -798,6 +735,36 @@ public:
&ssl_http_session::on_handshake, &ssl_http_session::on_handshake,
shared_from_this())); shared_from_this()));
} }
// 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_);
}
// 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 void
on_handshake( on_handshake(
beast::error_code ec, beast::error_code ec,
@ -812,21 +779,6 @@ public:
do_read(); 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 void
on_shutdown(beast::error_code ec) on_shutdown(beast::error_code ec)
{ {
@ -846,7 +798,7 @@ public:
// Detects SSL handshakes // Detects SSL handshakes
class detect_session : public std::enable_shared_from_this<detect_session> class detect_session : public std::enable_shared_from_this<detect_session>
{ {
beast::tcp_stream<net::io_context::strand> stream_; beast::tcp_stream stream_;
ssl::context& ctx_; ssl::context& ctx_;
std::shared_ptr<std::string const> doc_root_; std::shared_ptr<std::string const> doc_root_;
beast::flat_buffer buffer_; beast::flat_buffer buffer_;
@ -854,7 +806,7 @@ class detect_session : public std::enable_shared_from_this<detect_session>
public: public:
explicit explicit
detect_session( detect_session(
tcp::socket socket, tcp::socket&& socket,
ssl::context& ctx, ssl::context& ctx,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
: stream_(std::move(socket)) : stream_(std::move(socket))
@ -906,8 +858,10 @@ public:
// Accepts incoming connections and launches the sessions // Accepts incoming connections and launches the sessions
class listener : public std::enable_shared_from_this<listener> class listener : public std::enable_shared_from_this<listener>
{ {
net::io_context& ioc_;
ssl::context& ctx_; ssl::context& ctx_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
tcp::socket socket_;
std::shared_ptr<std::string const> doc_root_; std::shared_ptr<std::string const> doc_root_;
public: public:
@ -916,8 +870,10 @@ public:
ssl::context& ctx, ssl::context& ctx,
tcp::endpoint endpoint, tcp::endpoint endpoint,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
: ctx_(ctx) : ioc_(ioc)
, acceptor_(ioc) , ctx_(ctx)
, acceptor_(beast::make_strand(ioc))
, socket_(beast::make_strand(ioc))
, doc_root_(doc_root) , doc_root_(doc_root)
{ {
beast::error_code ec; beast::error_code ec;
@ -969,13 +925,14 @@ public:
do_accept() do_accept()
{ {
acceptor_.async_accept( acceptor_.async_accept(
socket_,
beast::bind_front_handler( beast::bind_front_handler(
&listener::on_accept, &listener::on_accept,
shared_from_this())); shared_from_this()));
} }
void void
on_accept(beast::error_code ec, tcp::socket socket) on_accept(beast::error_code ec)
{ {
if(ec) if(ec)
{ {
@ -985,11 +942,14 @@ public:
{ {
// Create the detector http_session and run it // Create the detector http_session and run it
std::make_shared<detect_session>( std::make_shared<detect_session>(
std::move(socket), std::move(socket_),
ctx_, ctx_,
doc_root_)->run(); doc_root_)->run();
} }
// Make sure each session gets its own strand
socket_ = tcp::socket(beast::make_strand(ioc_));
// Accept another connection // Accept another connection
do_accept(); do_accept();
} }

View File

@ -219,14 +219,13 @@ fail(beast::error_code ec, char const* what)
// Echoes back all received WebSocket messages // Echoes back all received WebSocket messages
class websocket_session : public std::enable_shared_from_this<websocket_session> class websocket_session : public std::enable_shared_from_this<websocket_session>
{ {
websocket::stream<beast::tcp_stream<net::io_context::strand>> ws_; websocket::stream<beast::tcp_stream> ws_;
beast::flat_buffer buffer_; beast::flat_buffer buffer_;
char ping_state_ = 0;
public: public:
// Take ownership of the socket // Take ownership of the socket
explicit explicit
websocket_session(tcp::socket socket) websocket_session(tcp::socket&& socket)
: ws_(std::move(socket)) : ws_(std::move(socket))
{ {
} }
@ -258,6 +257,7 @@ public:
shared_from_this())); shared_from_this()));
} }
private:
void void
on_accept(beast::error_code ec) on_accept(beast::error_code ec)
{ {
@ -413,7 +413,7 @@ class http_session : public std::enable_shared_from_this<http_session>
} }
}; };
beast::tcp_stream<net::io_context::strand> stream_; beast::tcp_stream stream_;
beast::flat_buffer buffer_; beast::flat_buffer buffer_;
std::shared_ptr<std::string const> doc_root_; std::shared_ptr<std::string const> doc_root_;
http::request<http::string_body> req_; http::request<http::string_body> req_;
@ -421,9 +421,8 @@ class http_session : public std::enable_shared_from_this<http_session>
public: public:
// Take ownership of the socket // Take ownership of the socket
explicit
http_session( http_session(
tcp::socket socket, tcp::socket&& socket,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
: stream_(std::move(socket)) : stream_(std::move(socket))
, doc_root_(doc_root) , doc_root_(doc_root)
@ -431,21 +430,14 @@ public:
{ {
} }
// Start the asynchronous operation // Start the session
void void
run() 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(); do_read();
} }
private:
void void
do_read() do_read()
{ {
@ -532,6 +524,7 @@ public:
// Accepts incoming connections and launches the sessions // Accepts incoming connections and launches the sessions
class listener : public std::enable_shared_from_this<listener> class listener : public std::enable_shared_from_this<listener>
{ {
net::io_context& ioc_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
tcp::socket socket_; tcp::socket socket_;
std::shared_ptr<std::string const> doc_root_; std::shared_ptr<std::string const> doc_root_;
@ -541,8 +534,9 @@ public:
net::io_context& ioc, net::io_context& ioc,
tcp::endpoint endpoint, tcp::endpoint endpoint,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
: acceptor_(ioc) : ioc_(ioc)
, socket_(ioc) , acceptor_(beast::make_strand(ioc))
, socket_(beast::make_strand(ioc))
, doc_root_(doc_root) , doc_root_(doc_root)
{ {
beast::error_code ec; beast::error_code ec;
@ -594,13 +588,14 @@ public:
do_accept() do_accept()
{ {
acceptor_.async_accept( acceptor_.async_accept(
socket_,
beast::bind_front_handler( beast::bind_front_handler(
&listener::on_accept, &listener::on_accept,
shared_from_this())); shared_from_this()));
} }
void void
on_accept(beast::error_code ec, tcp::socket socket) on_accept(beast::error_code ec)
{ {
if(ec) if(ec)
{ {
@ -610,10 +605,13 @@ public:
{ {
// Create the http session and run it // Create the http session and run it
std::make_shared<http_session>( std::make_shared<http_session>(
std::move(socket), std::move(socket_),
doc_root_)->run(); doc_root_)->run();
} }
// Make sure each session gets its own strand
socket_ = tcp::socket(beast::make_strand(ioc_));
// Accept another connection // Accept another connection
do_accept(); do_accept();
} }

View File

@ -33,10 +33,6 @@ using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// The type of stream to use
using stream_type =
beast::ssl_stream<beast::tcp_stream<net::io_context::executor_type>>;
// Report a failure // Report a failure
void void
fail(beast::error_code ec, char const* what) 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<session> class session : public std::enable_shared_from_this<session>
{ {
tcp::resolver resolver_; tcp::resolver resolver_;
stream_type stream_; beast::ssl_stream<beast::tcp_stream> stream_;
beast::flat_buffer buffer_; // (Must persist between reads) beast::flat_buffer buffer_; // (Must persist between reads)
http::request<http::empty_body> req_; http::request<http::empty_body> req_;
http::response<http::string_body> res_; http::response<http::string_body> res_;
public: public:
// Resolver and stream require an io_context // Objects are constructed with a strand to
// ensure that handlers do not execute concurrently.
explicit explicit
session(net::io_context& ioc, ssl::context& ctx) session(net::io_context& ioc, ssl::context& ctx)
: resolver_(ioc) : resolver_(beast::make_strand(ioc))
, stream_(ioc, ctx) , stream_(beast::make_strand(ioc), ctx)
{ {
} }

View File

@ -29,9 +29,6 @@ using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// The type of stream to use
using stream_type = beast::tcp_stream<net::io_context::executor_type>;
// Report a failure // Report a failure
void void
fail(beast::error_code ec, char const* what) 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<session> class session : public std::enable_shared_from_this<session>
{ {
tcp::resolver resolver_; tcp::resolver resolver_;
stream_type stream_; beast::tcp_stream stream_;
beast::flat_buffer buffer_; // (Must persist between reads) beast::flat_buffer buffer_; // (Must persist between reads)
http::request<http::empty_body> req_; http::request<http::empty_body> req_;
http::response<http::string_body> res_; http::response<http::string_body> res_;
public: public:
// Resolver and socket require an io_context // Objects are constructed with a strand to
// ensure that handlers do not execute concurrently.
explicit explicit
session(net::io_context& ioc) session(net::io_context& ioc)
: resolver_(ioc) : resolver_(beast::make_strand(ioc))
, stream_(ioc) , stream_(beast::make_strand(ioc))
{ {
} }

View File

@ -33,11 +33,6 @@ using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// The type of stream to use
// Stackful coroutines are already stranded.
using stream_type =
beast::ssl_stream<beast::tcp_stream<net::io_context::executor_type>>;
// Report a failure // Report a failure
void void
fail(beast::error_code ec, char const* what) fail(beast::error_code ec, char const* what)
@ -60,7 +55,7 @@ do_session(
// These objects perform our I/O // These objects perform our I/O
tcp::resolver resolver(ioc); tcp::resolver resolver(ioc);
stream_type stream(ioc, ctx); beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx);
// Set SNI Hostname (many hosts need this to handshake successfully) // Set SNI Hostname (many hosts need this to handshake successfully)
if(! SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) if(! SSL_set_tlsext_host_name(stream.native_handle(), host.c_str()))

View File

@ -29,10 +29,6 @@ using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// The type of stream to use.
// Stackful coroutines are already stranded.
using stream_type = beast::tcp_stream<net::io_context::executor_type>;
// Report a failure // Report a failure
void void
fail(beast::error_code ec, char const* what) fail(beast::error_code ec, char const* what)
@ -53,8 +49,8 @@ do_session(
beast::error_code ec; beast::error_code ec;
// These objects perform our I/O // These objects perform our I/O
tcp::resolver resolver{ioc}; tcp::resolver resolver(ioc);
stream_type stream(ioc); beast::tcp_stream stream(ioc);
// Look up the domain name // Look up the domain name
auto const results = resolver.async_resolve(host, port, yield[ec]); auto const results = resolver.async_resolve(host, port, yield[ec]);

View File

@ -151,10 +151,7 @@ class worker : public std::enable_shared_from_this<worker>
crawl_report& report_; crawl_report& report_;
tcp::resolver resolver_; tcp::resolver resolver_;
tcp::socket socket_; beast::tcp_stream stream_;
net::steady_timer timer_;
net::strand<
net::io_context::executor_type> strand_;
beast::flat_buffer buffer_; // (Must persist between reads) beast::flat_buffer buffer_; // (Must persist between reads)
http::request<http::empty_body> req_; http::request<http::empty_body> req_;
http::response<http::string_body> res_; http::response<http::string_body> res_;
@ -167,11 +164,8 @@ public:
crawl_report& report, crawl_report& report,
net::io_context& ioc) net::io_context& ioc)
: report_(report) : report_(report)
, resolver_(ioc) , resolver_(beast::make_strand(ioc))
, socket_(ioc) , stream_(beast::make_strand(ioc))
, timer_(ioc,
(chrono::steady_clock::time_point::max)())
, strand_(ioc.get_executor())
{ {
// Set up the common fields of the request // Set up the common fields of the request
req_.version(11); req_.version(11);
@ -184,44 +178,9 @@ public:
void void
run() run()
{ {
// Run the timer. The timer is operated
// continuously, this simplifies the code.
on_timer({});
do_get_host(); 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 void
do_get_host() do_get_host()
{ {
@ -230,27 +189,19 @@ public:
// nullptr means no more work // nullptr means no more work
if(! host) if(! host)
{
timer_.cancel_one();
return; return;
}
// The Host HTTP field is required // The Host HTTP field is required
req_.set(http::field::host, host); req_.set(http::field::host, host);
// Set the timer
timer_.expires_after(chrono::seconds(timeout));
// Set up an HTTP GET request message // Set up an HTTP GET request message
// Look up the domain name // Look up the domain name
resolver_.async_resolve( resolver_.async_resolve(
host, host,
"http", "http",
net::bind_executor( beast::bind_front_handler(
strand_, &worker::on_resolve,
beast::bind_front_handler( shared_from_this()));
&worker::on_resolve,
shared_from_this())));
} }
void void
@ -268,18 +219,16 @@ public:
return do_get_host(); return do_get_host();
} }
// Set the timer // Set a timeout on the operation
timer_.expires_after(chrono::seconds(timeout)); stream_.expires_after(std::chrono::seconds(10));
// Make the connection on the IP address we get from a lookup // Make the connection on the IP address we get from a lookup
net::async_connect( beast::async_connect(
socket_, stream_,
results, results,
net::bind_executor( beast::bind_front_handler(
strand_, &worker::on_connect,
beast::bind_front_handler( shared_from_this()));
&worker::on_connect,
shared_from_this())));
} }
void void
@ -295,18 +244,16 @@ public:
return do_get_host(); return do_get_host();
} }
// Set the timer // Set a timeout on the operation
timer_.expires_after(chrono::seconds(timeout)); stream_.expires_after(std::chrono::seconds(10));
// Send the HTTP request to the remote host // Send the HTTP request to the remote host
http::async_write( http::async_write(
socket_, stream_,
req_, req_,
net::bind_executor( beast::bind_front_handler(
strand_, &worker::on_write,
beast::bind_front_handler( shared_from_this()));
&worker::on_write,
shared_from_this())));
} }
void void
@ -325,21 +272,16 @@ public:
}); });
return do_get_host(); return do_get_host();
} }
// Set the timer
timer_.expires_after(chrono::seconds(timeout));
// Receive the HTTP response // Receive the HTTP response
res_ = {}; res_ = {};
http::async_read( http::async_read(
socket_, stream_,
buffer_, buffer_,
res_, res_,
net::bind_executor( beast::bind_front_handler(
strand_, &worker::on_read,
beast::bind_front_handler( shared_from_this()));
&worker::on_read,
shared_from_this())));
} }
void void
@ -368,8 +310,8 @@ public:
}); });
// Gracefully close the socket // Gracefully close the socket
socket_.shutdown(tcp::socket::shutdown_both, ec); stream_.socket().shutdown(tcp::socket::shutdown_both, ec);
socket_.close(ec); stream_.close();
// If we get here then the connection is closed gracefully // 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<int>(1, std::atoi(argv[1])); auto const threads = std::max<int>(1, std::atoi(argv[1]));
// The io_context is required for all I/O // 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 // The work keeps io_context::run from returning
auto work = net::make_work_guard(ioc); auto work = net::make_work_guard(ioc);

View File

@ -57,7 +57,7 @@ int main(int argc, char** argv)
net::io_context ioc; net::io_context ioc;
// The SSL context is required, and holds certificates // 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 // This holds the root certificate used for verification
load_root_certificates(ctx); load_root_certificates(ctx);
@ -66,8 +66,8 @@ int main(int argc, char** argv)
ctx.set_verify_mode(ssl::verify_peer); ctx.set_verify_mode(ssl::verify_peer);
// These objects perform our I/O // These objects perform our I/O
tcp::resolver resolver{ioc}; tcp::resolver resolver(ioc);
beast::ssl_stream<tcp::socket> stream{ioc, ctx}; beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx);
// Set SNI Hostname (many hosts need this to handshake successfully) // Set SNI Hostname (many hosts need this to handshake successfully)
if(! SSL_set_tlsext_host_name(stream.native_handle(), host)) 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); auto const results = resolver.resolve(host, port);
// Make the connection on the IP address we get from a lookup // 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 // Perform the SSL handshake
stream.handshake(ssl::stream_base::client); stream.handshake(ssl::stream_base::client);

View File

@ -53,14 +53,14 @@ int main(int argc, char** argv)
net::io_context ioc; net::io_context ioc;
// These objects perform our I/O // These objects perform our I/O
tcp::resolver resolver{ioc}; tcp::resolver resolver(ioc);
tcp::socket socket{ioc}; beast::tcp_stream stream(ioc);
// Look up the domain name // Look up the domain name
auto const results = resolver.resolve(host, port); auto const results = resolver.resolve(host, port);
// Make the connection on the IP address we get from a lookup // 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 // Set up an HTTP GET request message
http::request<http::string_body> req{http::verb::get, target, version}; http::request<http::string_body> 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); req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
// Send the HTTP request to the remote host // 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 // This buffer is used for reading and must be persisted
beast::flat_buffer buffer; beast::flat_buffer buffer;
@ -77,14 +77,14 @@ int main(int argc, char** argv)
http::response<http::dynamic_body> res; http::response<http::dynamic_body> res;
// Receive the HTTP response // Receive the HTTP response
http::read(socket, buffer, res); http::read(stream, buffer, res);
// Write the message to standard out // Write the message to standard out
std::cout << res << std::endl; std::cout << res << std::endl;
// Gracefully close the socket // Gracefully close the socket
beast::error_code ec; beast::error_code ec;
socket.shutdown(tcp::socket::shutdown_both, ec); stream.socket().shutdown(tcp::socket::shutdown_both, ec);
// not_connected happens sometimes // not_connected happens sometimes
// so don't bother reporting it. // so don't bother reporting it.

View File

@ -255,7 +255,7 @@ class session : public std::enable_shared_from_this<session>
} }
}; };
beast::ssl_stream<beast::tcp_stream<net::io_context::strand>> stream_; beast::ssl_stream<beast::tcp_stream> stream_;
beast::flat_buffer buffer_; beast::flat_buffer buffer_;
std::shared_ptr<std::string const> doc_root_; std::shared_ptr<std::string const> doc_root_;
http::request<http::string_body> req_; http::request<http::string_body> req_;
@ -387,8 +387,10 @@ public:
// Accepts incoming connections and launches the sessions // Accepts incoming connections and launches the sessions
class listener : public std::enable_shared_from_this<listener> class listener : public std::enable_shared_from_this<listener>
{ {
net::io_context& ioc_;
ssl::context& ctx_; ssl::context& ctx_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
tcp::socket socket_;
std::shared_ptr<std::string const> doc_root_; std::shared_ptr<std::string const> doc_root_;
public: public:
@ -397,8 +399,10 @@ public:
ssl::context& ctx, ssl::context& ctx,
tcp::endpoint endpoint, tcp::endpoint endpoint,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
: ctx_(ctx) : ioc_(ioc)
, ctx_(ctx)
, acceptor_(ioc) , acceptor_(ioc)
, socket_(beast::make_strand(ioc))
, doc_root_(doc_root) , doc_root_(doc_root)
{ {
beast::error_code ec; beast::error_code ec;
@ -450,13 +454,14 @@ public:
do_accept() do_accept()
{ {
acceptor_.async_accept( acceptor_.async_accept(
socket_,
beast::bind_front_handler( beast::bind_front_handler(
&listener::on_accept, &listener::on_accept,
shared_from_this())); shared_from_this()));
} }
void void
on_accept(beast::error_code ec, tcp::socket socket) on_accept(beast::error_code ec)
{ {
if(ec) if(ec)
{ {
@ -466,11 +471,14 @@ public:
{ {
// Create the session and run it // Create the session and run it
std::make_shared<session>( std::make_shared<session>(
std::move(socket), std::move(socket_),
ctx_, ctx_,
doc_root_)->run(); doc_root_)->run();
} }
// Make sure each session gets its own strand
socket_ = tcp::socket(beast::make_strand(ioc_));
// Accept another connection // Accept another connection
do_accept(); do_accept();
} }

View File

@ -251,7 +251,7 @@ class session : public std::enable_shared_from_this<session>
} }
}; };
beast::tcp_stream<net::io_context::strand> stream_; beast::tcp_stream stream_;
beast::flat_buffer buffer_; beast::flat_buffer buffer_;
std::shared_ptr<std::string const> doc_root_; std::shared_ptr<std::string const> doc_root_;
http::request<http::string_body> req_; http::request<http::string_body> req_;
@ -259,7 +259,7 @@ class session : public std::enable_shared_from_this<session>
send_lambda lambda_; send_lambda lambda_;
public: public:
// Take ownership of the socket // Take ownership of the stream
session( session(
tcp::socket&& socket, tcp::socket&& socket,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
@ -352,7 +352,9 @@ public:
// Accepts incoming connections and launches the sessions // Accepts incoming connections and launches the sessions
class listener : public std::enable_shared_from_this<listener> class listener : public std::enable_shared_from_this<listener>
{ {
net::io_context& ioc_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
tcp::socket socket_;
std::shared_ptr<std::string const> doc_root_; std::shared_ptr<std::string const> doc_root_;
public: public:
@ -360,7 +362,9 @@ public:
net::io_context& ioc, net::io_context& ioc,
tcp::endpoint endpoint, tcp::endpoint endpoint,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
: acceptor_(ioc) : ioc_(ioc)
, acceptor_(beast::make_strand(ioc))
, socket_(beast::make_strand(ioc))
, doc_root_(doc_root) , doc_root_(doc_root)
{ {
beast::error_code ec; beast::error_code ec;
@ -412,13 +416,14 @@ public:
do_accept() do_accept()
{ {
acceptor_.async_accept( acceptor_.async_accept(
socket_,
beast::bind_front_handler( beast::bind_front_handler(
&listener::on_accept, &listener::on_accept,
shared_from_this())); shared_from_this()));
} }
void void
on_accept(beast::error_code ec, tcp::socket socket) on_accept(beast::error_code ec)
{ {
if(ec) if(ec)
{ {
@ -428,10 +433,13 @@ public:
{ {
// Create the session and run it // Create the session and run it
std::make_shared<session>( std::make_shared<session>(
std::move(socket), std::move(socket_),
doc_root_)->run(); doc_root_)->run();
} }
// Make sure each session gets its own strand
socket_ = tcp::socket(beast::make_strand(ioc_));
// Accept another connection // Accept another connection
do_accept(); do_accept();
} }

View File

@ -207,11 +207,6 @@ handle_request(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// The type of stream to use
// Stackful coroutines are already stranded.
using stream_type =
beast::ssl_stream<beast::tcp_stream<net::io_context::executor_type>>;
// Report a failure // Report a failure
void void
fail(beast::error_code ec, char const* what) 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. // The function object is used to send an HTTP message.
struct send_lambda struct send_lambda
{ {
stream_type& stream_; beast::ssl_stream<beast::tcp_stream>& stream_;
bool& close_; bool& close_;
beast::error_code& ec_; beast::error_code& ec_;
net::yield_context yield_; net::yield_context yield_;
send_lambda( send_lambda(
stream_type& stream, beast::ssl_stream<beast::tcp_stream>& stream,
bool& close, bool& close,
beast::error_code& ec, beast::error_code& ec,
net::yield_context yield) net::yield_context yield)
@ -258,7 +253,7 @@ struct send_lambda
// Handles an HTTP server connection // Handles an HTTP server connection
void void
do_session( do_session(
stream_type& stream, beast::ssl_stream<beast::tcp_stream>& stream,
std::shared_ptr<std::string const> const& doc_root, std::shared_ptr<std::string const> const& doc_root,
net::yield_context yield) net::yield_context yield)
{ {
@ -357,10 +352,11 @@ do_listen(
fail(ec, "accept"); fail(ec, "accept");
else else
net::spawn( net::spawn(
acceptor.get_executor().context(), acceptor.get_executor(),
std::bind( std::bind(
&do_session, &do_session,
stream_type(std::move(socket), ctx), beast::ssl_stream<beast::tcp_stream>(
std::move(socket), ctx),
doc_root, doc_root,
std::placeholders::_1)); std::placeholders::_1));
} }

View File

@ -204,10 +204,6 @@ handle_request(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// The type of stream to use.
// Stackful coroutines are already stranded.
using stream_type = beast::tcp_stream<net::io_context::executor_type>;
// Report a failure // Report a failure
void void
fail(beast::error_code ec, char const* what) 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. // The function object is used to send an HTTP message.
struct send_lambda struct send_lambda
{ {
stream_type& stream_; beast::tcp_stream& stream_;
bool& close_; bool& close_;
beast::error_code& ec_; beast::error_code& ec_;
net::yield_context yield_; net::yield_context yield_;
send_lambda( send_lambda(
stream_type& stream, beast::tcp_stream& stream,
bool& close, bool& close,
beast::error_code& ec, beast::error_code& ec,
net::yield_context yield) net::yield_context yield)
@ -254,7 +250,7 @@ struct send_lambda
// Handles an HTTP server connection // Handles an HTTP server connection
void void
do_session( do_session(
stream_type& stream, beast::tcp_stream& stream,
std::shared_ptr<std::string const> const& doc_root, std::shared_ptr<std::string const> const& doc_root,
net::yield_context yield) net::yield_context yield)
{ {
@ -339,10 +335,10 @@ do_listen(
fail(ec, "accept"); fail(ec, "accept");
else else
net::spawn( net::spawn(
acceptor.get_executor().context(), acceptor.get_executor(),
std::bind( std::bind(
&do_session, &do_session,
stream_type(std::move(socket)), beast::tcp_stream(std::move(socket)),
doc_root, doc_root,
std::placeholders::_1)); std::placeholders::_1));
} }

View File

@ -99,7 +99,7 @@ private:
std::string doc_root_; std::string doc_root_;
// The socket for the currently connected client. // 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 // The buffer for performing reads
beast::flat_static_buffer<8192> buffer_; beast::flat_static_buffer<8192> buffer_;
@ -112,7 +112,7 @@ private:
// The timer putting a time limit on requests. // The timer putting a time limit on requests.
net::basic_waitable_timer<std::chrono::steady_clock> request_deadline_{ net::basic_waitable_timer<std::chrono::steady_clock> 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. // The string-based response message.
boost::optional<http::response<http::string_body, http::basic_fields<alloc_t>>> string_response_; boost::optional<http::response<http::string_body, http::basic_fields<alloc_t>>> string_response_;

View File

@ -208,13 +208,6 @@ handle_request(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// The type of plain streams
using plain_stream_type = beast::tcp_stream<net::io_context::strand>;
// The type of TLS streams
using ssl_stream_type =
beast::ssl_stream<beast::tcp_stream<net::io_context::strand>>;
// Report a failure // Report a failure
void void
fail(beast::error_code ec, char const* what) fail(beast::error_code ec, char const* what)
@ -358,7 +351,7 @@ class plain_session
: public session<plain_session> : public session<plain_session>
, public std::enable_shared_from_this<plain_session> , public std::enable_shared_from_this<plain_session>
{ {
plain_stream_type stream_; beast::tcp_stream stream_;
public: public:
// Create the session // Create the session
@ -374,7 +367,7 @@ public:
} }
// Called by the base class // Called by the base class
plain_stream_type& beast::tcp_stream&
stream() stream()
{ {
return stream_; return stream_;
@ -403,7 +396,7 @@ class ssl_session
: public session<ssl_session> : public session<ssl_session>
, public std::enable_shared_from_this<ssl_session> , public std::enable_shared_from_this<ssl_session>
{ {
ssl_stream_type stream_; beast::ssl_stream<beast::tcp_stream> stream_;
public: public:
// Create the session // Create the session
@ -420,7 +413,7 @@ public:
} }
// Called by the base class // Called by the base class
ssl_stream_type& beast::ssl_stream<beast::tcp_stream>&
stream() stream()
{ {
return stream_; return stream_;
@ -442,6 +435,7 @@ public:
&ssl_session::on_handshake, &ssl_session::on_handshake,
shared_from_this())); shared_from_this()));
} }
void void
on_handshake( on_handshake(
beast::error_code ec, beast::error_code ec,
@ -484,14 +478,14 @@ public:
// Detects SSL handshakes // Detects SSL handshakes
class detect_session : public std::enable_shared_from_this<detect_session> class detect_session : public std::enable_shared_from_this<detect_session>
{ {
plain_stream_type stream_; beast::tcp_stream stream_;
ssl::context& ctx_; ssl::context& ctx_;
std::shared_ptr<std::string const> doc_root_; std::shared_ptr<std::string const> doc_root_;
beast::flat_buffer buffer_; beast::flat_buffer buffer_;
public: public:
detect_session( detect_session(
tcp::socket socket, tcp::socket&& socket,
ssl::context& ctx, ssl::context& ctx,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
: stream_(std::move(socket)) : stream_(std::move(socket))
@ -544,8 +538,10 @@ public:
// Accepts incoming connections and launches the sessions // Accepts incoming connections and launches the sessions
class listener : public std::enable_shared_from_this<listener> class listener : public std::enable_shared_from_this<listener>
{ {
net::io_context& ioc_;
ssl::context& ctx_; ssl::context& ctx_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
tcp::socket socket_;
std::shared_ptr<std::string const> doc_root_; std::shared_ptr<std::string const> doc_root_;
public: public:
@ -554,8 +550,10 @@ public:
ssl::context& ctx, ssl::context& ctx,
tcp::endpoint endpoint, tcp::endpoint endpoint,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
: ctx_(ctx) : ioc_(ioc)
, acceptor_(ioc) , ctx_(ctx)
, acceptor_(beast::make_strand(ioc))
, socket_(beast::make_strand(ioc))
, doc_root_(doc_root) , doc_root_(doc_root)
{ {
beast::error_code ec; beast::error_code ec;
@ -607,13 +605,14 @@ public:
do_accept() do_accept()
{ {
acceptor_.async_accept( acceptor_.async_accept(
socket_,
beast::bind_front_handler( beast::bind_front_handler(
&listener::on_accept, &listener::on_accept,
shared_from_this())); shared_from_this()));
} }
void void
on_accept(beast::error_code ec, tcp::socket sock) on_accept(beast::error_code ec)
{ {
if(ec) if(ec)
{ {
@ -623,11 +622,14 @@ public:
{ {
// Create the detector session and run it // Create the detector session and run it
std::make_shared<detect_session>( std::make_shared<detect_session>(
std::move(sock), std::move(socket_),
ctx_, ctx_,
doc_root_)->run(); doc_root_)->run();
} }
// Make sure each session gets its own strand
socket_ = tcp::socket(beast::make_strand(ioc_));
// Accept another connection // Accept another connection
do_accept(); do_accept();
} }

View File

@ -76,7 +76,7 @@ private:
// The timer for putting a deadline on connection processing. // The timer for putting a deadline on connection processing.
net::basic_waitable_timer<std::chrono::steady_clock> deadline_{ net::basic_waitable_timer<std::chrono::steady_clock> deadline_{
socket_.get_executor().context(), std::chrono::seconds(60)}; socket_.get_executor(), std::chrono::seconds(60)};
// Asynchronously receive a complete request message. // Asynchronously receive a complete request message.
void void

View File

@ -209,10 +209,6 @@ handle_request(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// The type of TLS streams
using ssl_stream_type =
beast::ssl_stream<beast::tcp_stream<net::io_context::strand>>;
// Report a failure // Report a failure
void void
fail(beast::error_code ec, char const* what) fail(beast::error_code ec, char const* what)
@ -264,7 +260,7 @@ class session
} }
}; };
ssl_stream_type stream_; beast::ssl_stream<beast::tcp_stream> stream_;
beast::flat_buffer buffer_; beast::flat_buffer buffer_;
std::shared_ptr<std::string const> doc_root_; std::shared_ptr<std::string const> doc_root_;
http::request<http::string_body> req_; http::request<http::string_body> req_;
@ -383,6 +379,7 @@ class listener
: public net::coroutine : public net::coroutine
, public std::enable_shared_from_this<listener> , public std::enable_shared_from_this<listener>
{ {
net::io_context& ioc_;
ssl::context& ctx_; ssl::context& ctx_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
tcp::socket socket_; tcp::socket socket_;
@ -394,9 +391,10 @@ public:
ssl::context& ctx, ssl::context& ctx,
tcp::endpoint endpoint, tcp::endpoint endpoint,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
: ctx_(ctx) : ioc_(ioc)
, acceptor_(ioc) , ctx_(ctx)
, socket_(ioc) , acceptor_(beast::make_strand(ioc))
, socket_(beast::make_strand(ioc))
, doc_root_(doc_root) , doc_root_(doc_root)
{ {
beast::error_code ec; beast::error_code ec;
@ -470,6 +468,9 @@ public:
ctx_, ctx_,
doc_root_)->run(); doc_root_)->run();
} }
// Make sure each session gets its own strand
socket_ = tcp::socket(beast::make_strand(ioc_));
} }
} }
} }

View File

@ -205,9 +205,6 @@ handle_request(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// The type of stream to use
using stream_type = beast::tcp_stream<net::io_context::strand>;
// Report a failure // Report a failure
void void
fail(beast::error_code ec, char const* what) 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_; beast::flat_buffer buffer_;
std::shared_ptr<std::string const> doc_root_; std::shared_ptr<std::string const> doc_root_;
http::request<http::string_body> req_; http::request<http::string_body> req_;
@ -269,7 +266,7 @@ public:
// Take ownership of the socket // Take ownership of the socket
explicit explicit
session( session(
tcp::socket socket, tcp::socket&& socket,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
: stream_(std::move(socket)) : stream_(std::move(socket))
, doc_root_(doc_root) , doc_root_(doc_root)
@ -348,6 +345,7 @@ class listener
: public net::coroutine : public net::coroutine
, public std::enable_shared_from_this<listener> , public std::enable_shared_from_this<listener>
{ {
net::io_context& ioc_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
tcp::socket socket_; tcp::socket socket_;
std::shared_ptr<std::string const> doc_root_; std::shared_ptr<std::string const> doc_root_;
@ -357,8 +355,9 @@ public:
net::io_context& ioc, net::io_context& ioc,
tcp::endpoint endpoint, tcp::endpoint endpoint,
std::shared_ptr<std::string const> const& doc_root) std::shared_ptr<std::string const> const& doc_root)
: acceptor_(ioc) : ioc_(ioc)
, socket_(ioc) , acceptor_(beast::make_strand(ioc))
, socket_(beast::make_strand(ioc))
, doc_root_(doc_root) , doc_root_(doc_root)
{ {
beast::error_code ec; beast::error_code ec;
@ -429,6 +428,9 @@ public:
std::move(socket_), std::move(socket_),
doc_root_)->run(); doc_root_)->run();
} }
// Make sure each session gets its own strand
socket_ = tcp::socket(beast::make_strand(ioc_));
} }
} }
} }

View File

@ -45,8 +45,8 @@ fail(beast::error_code ec, char const* what)
class session : public std::enable_shared_from_this<session> class session : public std::enable_shared_from_this<session>
{ {
tcp::resolver resolver_; tcp::resolver resolver_;
websocket::stream<beast::ssl_stream< websocket::stream<
beast::tcp_stream<net::io_context::executor_type>>> ws_; beast::ssl_stream<beast::tcp_stream>> ws_;
beast::multi_buffer buffer_; beast::multi_buffer buffer_;
std::string host_; std::string host_;
std::string text_; std::string text_;
@ -55,8 +55,8 @@ public:
// Resolver and socket require an io_context // Resolver and socket require an io_context
explicit explicit
session(net::io_context& ioc, ssl::context& ctx) session(net::io_context& ioc, ssl::context& ctx)
: resolver_(ioc) : resolver_(beast::make_strand(ioc))
, ws_(ioc, ctx) , ws_(beast::make_strand(ioc), ctx)
{ {
} }

View File

@ -40,8 +40,7 @@ fail(beast::error_code ec, char const* what)
class session : public std::enable_shared_from_this<session> class session : public std::enable_shared_from_this<session>
{ {
tcp::resolver resolver_; tcp::resolver resolver_;
websocket::stream< websocket::stream<beast::tcp_stream> ws_;
beast::tcp_stream<net::io_context::executor_type>> ws_;
beast::multi_buffer buffer_; beast::multi_buffer buffer_;
std::string host_; std::string host_;
std::string text_; std::string text_;
@ -50,8 +49,8 @@ public:
// Resolver and socket require an io_context // Resolver and socket require an io_context
explicit explicit
session(net::io_context& ioc) session(net::io_context& ioc)
: resolver_(ioc) : resolver_(beast::make_strand(ioc))
, ws_(ioc) , ws_(beast::make_strand(ioc))
{ {
} }

View File

@ -54,9 +54,9 @@ do_session(
beast::error_code ec; beast::error_code ec;
// These objects perform our I/O // These objects perform our I/O
tcp::resolver resolver{ioc}; tcp::resolver resolver(ioc);
websocket::stream<beast::ssl_stream< websocket::stream<
beast::tcp_stream<net::io_context::executor_type>>> ws(ioc, ctx); beast::ssl_stream<beast::tcp_stream>> ws(ioc, ctx);
// Look up the domain name // Look up the domain name
auto const results = resolver.async_resolve(host, port, yield[ec]); auto const results = resolver.async_resolve(host, port, yield[ec]);

View File

@ -48,9 +48,8 @@ do_session(
beast::error_code ec; beast::error_code ec;
// These objects perform our I/O // These objects perform our I/O
tcp::resolver resolver{ioc}; tcp::resolver resolver(ioc);
websocket::stream< websocket::stream<beast::tcp_stream> ws(ioc);
beast::tcp_stream<net::io_context::executor_type>> ws(ioc);
// Look up the domain name // Look up the domain name
auto const results = resolver.async_resolve(host, port, yield[ec]); auto const results = resolver.async_resolve(host, port, yield[ec]);

View File

@ -48,13 +48,13 @@ fail(beast::error_code ec, char const* what)
// Echoes back all received WebSocket messages // Echoes back all received WebSocket messages
class session : public std::enable_shared_from_this<session> class session : public std::enable_shared_from_this<session>
{ {
websocket::stream<beast::ssl_stream< websocket::stream<
beast::tcp_stream<net::io_context::strand>>> ws_; beast::ssl_stream<beast::tcp_stream>> ws_;
beast::multi_buffer buffer_; beast::multi_buffer buffer_;
public: public:
// Take ownership of the socket // Take ownership of the socket
session(tcp::socket socket, ssl::context& ctx) session(tcp::socket&& socket, ssl::context& ctx)
: ws_(std::move(socket), ctx) : ws_(std::move(socket), ctx)
{ {
} }
@ -172,16 +172,20 @@ public:
// Accepts incoming connections and launches the sessions // Accepts incoming connections and launches the sessions
class listener : public std::enable_shared_from_this<listener> class listener : public std::enable_shared_from_this<listener>
{ {
net::io_context& ioc_;
ssl::context& ctx_; ssl::context& ctx_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
tcp::socket socket_;
public: public:
listener( listener(
net::io_context& ioc, net::io_context& ioc,
ssl::context& ctx, ssl::context& ctx,
tcp::endpoint endpoint) tcp::endpoint endpoint)
: ctx_(ctx) : ioc_(ioc)
, acceptor_(ioc) , ctx_(ctx)
, acceptor_(beast::make_strand(ioc))
, socket_(beast::make_strand(ioc))
{ {
beast::error_code ec; beast::error_code ec;
@ -232,13 +236,14 @@ public:
do_accept() do_accept()
{ {
acceptor_.async_accept( acceptor_.async_accept(
socket_,
beast::bind_front_handler( beast::bind_front_handler(
&listener::on_accept, &listener::on_accept,
shared_from_this())); shared_from_this()));
} }
void void
on_accept(beast::error_code ec, tcp::socket socket) on_accept(beast::error_code ec)
{ {
if(ec) if(ec)
{ {
@ -247,9 +252,12 @@ public:
else else
{ {
// Create the session and run it // Create the session and run it
std::make_shared<session>(std::move(socket), ctx_)->run(); std::make_shared<session>(std::move(socket_), ctx_)->run();
} }
// Make sure each session gets its own strand
socket_ = tcp::socket(beast::make_strand(ioc_));
// Accept another connection // Accept another connection
do_accept(); do_accept();
} }

View File

@ -43,13 +43,13 @@ fail(beast::error_code ec, char const* what)
// Echoes back all received WebSocket messages // Echoes back all received WebSocket messages
class session : public std::enable_shared_from_this<session> class session : public std::enable_shared_from_this<session>
{ {
websocket::stream<beast::tcp_stream<net::io_context::strand>> ws_; websocket::stream<beast::tcp_stream> ws_;
beast::multi_buffer buffer_; beast::multi_buffer buffer_;
public: public:
// Take ownership of the socket // Take ownership of the socket
explicit explicit
session(tcp::socket socket) session(tcp::socket&& socket)
: ws_(std::move(socket)) : ws_(std::move(socket))
{ {
} }
@ -146,13 +146,17 @@ public:
// Accepts incoming connections and launches the sessions // Accepts incoming connections and launches the sessions
class listener : public std::enable_shared_from_this<listener> class listener : public std::enable_shared_from_this<listener>
{ {
net::io_context& ioc_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
tcp::socket socket_;
public: public:
listener( listener(
net::io_context& ioc, net::io_context& ioc,
tcp::endpoint endpoint) tcp::endpoint endpoint)
: acceptor_(ioc) : ioc_(ioc)
, acceptor_(ioc)
, socket_(beast::make_strand(ioc_))
{ {
beast::error_code ec; beast::error_code ec;
@ -203,13 +207,14 @@ public:
do_accept() do_accept()
{ {
acceptor_.async_accept( acceptor_.async_accept(
socket_,
beast::bind_front_handler( beast::bind_front_handler(
&listener::on_accept, &listener::on_accept,
shared_from_this())); shared_from_this()));
} }
void void
on_accept(beast::error_code ec, tcp::socket socket) on_accept(beast::error_code ec)
{ {
if(ec) if(ec)
{ {
@ -218,9 +223,12 @@ public:
else else
{ {
// Create the session and run it // Create the session and run it
std::make_shared<session>(std::move(socket))->run(); std::make_shared<session>(std::move(socket_))->run();
} }
// Make sure each session gets its own strand
socket_ = tcp::socket(beast::make_strand(ioc_));
// Accept another connection // Accept another connection
do_accept(); do_accept();
} }

View File

@ -21,7 +21,7 @@
*/ */
class http_session : public boost::enable_shared_from_this<http_session> class http_session : public boost::enable_shared_from_this<http_session>
{ {
beast::tcp_stream<net::io_context::strand> stream_; beast::tcp_stream stream_;
beast::flat_buffer buffer_; beast::flat_buffer buffer_;
boost::shared_ptr<shared_state> state_; boost::shared_ptr<shared_state> state_;
http::request<http::string_body> req_; http::request<http::string_body> req_;

View File

@ -16,7 +16,9 @@ listener(
net::io_context& ioc, net::io_context& ioc,
tcp::endpoint endpoint, tcp::endpoint endpoint,
boost::shared_ptr<shared_state> const& state) boost::shared_ptr<shared_state> const& state)
: acceptor_(ioc) : ioc_(ioc)
, acceptor_(ioc)
, socket_(beast::make_strand(ioc))
, state_(state) , state_(state)
{ {
beast::error_code ec; beast::error_code ec;
@ -61,6 +63,7 @@ run()
{ {
// Start accepting a connection // Start accepting a connection
acceptor_.async_accept( acceptor_.async_accept(
socket_,
beast::bind_front_handler( beast::bind_front_handler(
&listener::on_accept, &listener::on_accept,
shared_from_this())); shared_from_this()));
@ -80,18 +83,22 @@ fail(beast::error_code ec, char const* what)
// Handle a connection // Handle a connection
void void
listener:: listener::
on_accept(beast::error_code ec, tcp::socket socket) on_accept(beast::error_code ec)
{ {
if(ec) if(ec)
return fail(ec, "accept"); return fail(ec, "accept");
else else
// Launch a new session for this connection // Launch a new session for this connection
boost::make_shared<http_session>( boost::make_shared<http_session>(
std::move(socket), std::move(socket_),
state_)->run(); state_)->run();
// Make sure each session gets its own strand
socket_ = tcp::socket(beast::make_strand(ioc_));
// Accept another connection // Accept another connection
acceptor_.async_accept( acceptor_.async_accept(
socket_,
beast::bind_front_handler( beast::bind_front_handler(
&listener::on_accept, &listener::on_accept,
shared_from_this())); shared_from_this()));

View File

@ -22,11 +22,13 @@ class shared_state;
// Accepts incoming connections and launches the sessions // Accepts incoming connections and launches the sessions
class listener : public boost::enable_shared_from_this<listener> class listener : public boost::enable_shared_from_this<listener>
{ {
net::io_context& ioc_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
tcp::socket socket_;
boost::shared_ptr<shared_state> state_; boost::shared_ptr<shared_state> state_;
void fail(beast::error_code ec, char const* what); 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: public:
listener( listener(

View File

@ -83,17 +83,22 @@ void
websocket_session:: websocket_session::
send(boost::shared_ptr<std::string const> const& ss) send(boost::shared_ptr<std::string const> const& ss)
{ {
// Get on the strand if we aren't already, // Post our work to the strand, this ensures
// otherwise we will concurrently access // that the members of `this` will not be
// objects which are not thread-safe. // accessed concurrently.
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));
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<std::string const> const& ss)
{
// Always add to queue // Always add to queue
queue_.push_back(ss); queue_.push_back(ss);

View File

@ -27,7 +27,7 @@ class shared_state;
class websocket_session : public boost::enable_shared_from_this<websocket_session> class websocket_session : public boost::enable_shared_from_this<websocket_session>
{ {
beast::flat_buffer buffer_; beast::flat_buffer buffer_;
websocket::stream<beast::tcp_stream<net::io_context::strand>> ws_; websocket::stream<beast::tcp_stream> ws_;
boost::shared_ptr<shared_state> state_; boost::shared_ptr<shared_state> state_;
std::vector<boost::shared_ptr<std::string const>> queue_; std::vector<boost::shared_ptr<std::string const>> queue_;
@ -50,6 +50,10 @@ public:
// Send a message // Send a message
void void
send(boost::shared_ptr<std::string const> const& ss); send(boost::shared_ptr<std::string const> const& ss);
private:
void
on_send(boost::shared_ptr<std::string const> const& ss);
}; };
template<class Body, class Allocator> template<class Body, class Allocator>

View File

@ -38,11 +38,6 @@ using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// The type of websocket stream to use
// Stackful coroutines are already stranded.
using ws_type = websocket::stream<beast::ssl_stream<
beast::tcp_stream<net::io_context::executor_type>>>;
// Report a failure // Report a failure
void void
fail(beast::error_code ec, char const* what) 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 // Echoes back all received WebSocket messages
void void
do_session( do_session(
ws_type& ws, websocket::stream<
beast::ssl_stream<beast::tcp_stream>>& ws,
net::yield_context yield) net::yield_context yield)
{ {
beast::error_code ec; beast::error_code ec;
@ -153,10 +149,11 @@ do_listen(
fail(ec, "accept"); fail(ec, "accept");
else else
net::spawn( net::spawn(
acceptor.get_executor().context(), acceptor.get_executor(),
std::bind( std::bind(
&do_session, &do_session,
ws_type(std::move(socket), ctx), websocket::stream<beast::ssl_stream<
beast::tcp_stream>>(std::move(socket), ctx),
std::placeholders::_1)); std::placeholders::_1));
} }
} }

View File

@ -33,11 +33,6 @@ using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// The type of websocket stream to use
// Stackful coroutines are already stranded.
using ws_type = websocket::stream<
beast::tcp_stream<net::io_context::executor_type>>;
// Report a failure // Report a failure
void void
fail(beast::error_code ec, char const* what) 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 // Echoes back all received WebSocket messages
void void
do_session(ws_type& ws, net::yield_context yield) do_session(
websocket::stream<beast::tcp_stream>& ws,
net::yield_context yield)
{ {
beast::error_code ec; beast::error_code ec;
@ -133,10 +130,11 @@ do_listen(
fail(ec, "accept"); fail(ec, "accept");
else else
net::spawn( net::spawn(
acceptor.get_executor().context(), acceptor.get_executor(),
std::bind( std::bind(
&do_session, &do_session,
ws_type(std::move(socket)), websocket::stream<
beast::tcp_stream>(std::move(socket)),
std::placeholders::_1)); std::placeholders::_1));
} }
} }
@ -157,7 +155,7 @@ int main(int argc, char* argv[])
auto const threads = std::max<int>(1, std::atoi(argv[3])); auto const threads = std::max<int>(1, std::atoi(argv[3]));
// The io_context is required for all I/O // The io_context is required for all I/O
net::io_context ioc{threads}; net::io_context ioc(threads);
// Spawn a listening port // Spawn a listening port
net::spawn(ioc, net::spawn(ioc,

View File

@ -78,13 +78,8 @@ setup_stream(websocket::stream<NextLayer>& ws)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// The type of websocket stream to use
// Stackful coroutines are already stranded.
using ws_type = websocket::stream<
beast::tcp_stream<net::io_context::executor_type>>;
void void
do_sync_session(ws_type& ws) do_sync_session(websocket::stream<beast::tcp_stream>& ws)
{ {
beast::error_code ec; beast::error_code ec;
@ -135,7 +130,8 @@ do_sync_listen(
std::thread(std::bind( std::thread(std::bind(
&do_sync_session, &do_sync_session,
ws_type(std::move(socket)))).detach(); websocket::stream<beast::tcp_stream>(
std::move(socket)))).detach();
} }
} }
@ -144,13 +140,13 @@ do_sync_listen(
// Echoes back all received WebSocket messages // Echoes back all received WebSocket messages
class async_session : public std::enable_shared_from_this<async_session> class async_session : public std::enable_shared_from_this<async_session>
{ {
websocket::stream<beast::tcp_stream<net::io_context::strand>> ws_; websocket::stream<beast::tcp_stream> ws_;
beast::multi_buffer buffer_; beast::multi_buffer buffer_;
public: public:
// Take ownership of the socket // Take ownership of the socket
explicit explicit
async_session(tcp::socket socket) async_session(tcp::socket&& socket)
: ws_(std::move(socket)) : ws_(std::move(socket))
{ {
setup_stream(ws_); setup_stream(ws_);
@ -160,6 +156,11 @@ public:
void void
run() 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 // Set a decorator to change the Server of the handshake
ws_.set_option(websocket::stream_base::decorator( ws_.set_option(websocket::stream_base::decorator(
[](websocket::response_type& res) [](websocket::response_type& res)
@ -240,13 +241,17 @@ public:
// Accepts incoming connections and launches the sessions // Accepts incoming connections and launches the sessions
class async_listener : public std::enable_shared_from_this<async_listener> class async_listener : public std::enable_shared_from_this<async_listener>
{ {
net::io_context& ioc_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
tcp::socket socket_;
public: public:
async_listener( async_listener(
net::io_context& ioc, net::io_context& ioc,
tcp::endpoint endpoint) tcp::endpoint endpoint)
: acceptor_(ioc) : ioc_(ioc)
, acceptor_(beast::make_strand(ioc))
, socket_(beast::make_strand(ioc))
{ {
beast::error_code ec; beast::error_code ec;
@ -297,13 +302,14 @@ public:
do_accept() do_accept()
{ {
acceptor_.async_accept( acceptor_.async_accept(
socket_,
beast::bind_front_handler( beast::bind_front_handler(
&async_listener::on_accept, &async_listener::on_accept,
shared_from_this())); shared_from_this()));
} }
void void
on_accept(beast::error_code ec, tcp::socket socket) on_accept(beast::error_code ec)
{ {
if(ec) if(ec)
{ {
@ -312,9 +318,12 @@ public:
else else
{ {
// Create the async_session and run it // Create the async_session and run it
std::make_shared<async_session>(std::move(socket))->run(); std::make_shared<async_session>(std::move(socket_))->run();
} }
// Make sure each session gets its own strand
socket_ = tcp::socket(beast::make_strand(ioc_));
// Accept another connection // Accept another connection
do_accept(); do_accept();
} }
@ -323,12 +332,19 @@ public:
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void void
do_coro_session(ws_type& ws, net::yield_context yield) do_coro_session(
websocket::stream<beast::tcp_stream>& ws,
net::yield_context yield)
{ {
beast::error_code ec; beast::error_code ec;
setup_stream(ws); 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 // Set a decorator to change the Server of the handshake
ws.set_option(websocket::stream_base::decorator( ws.set_option(websocket::stream_base::decorator(
[](websocket::response_type& res) [](websocket::response_type& res)
@ -395,10 +411,11 @@ do_coro_listen(
} }
net::spawn( net::spawn(
acceptor.get_executor().context(), acceptor.get_executor(),
std::bind( std::bind(
&do_coro_session, &do_coro_session,
ws_type(std::move(socket)), websocket::stream<
beast::tcp_stream>(std::move(socket)),
std::placeholders::_1)); std::placeholders::_1));
} }
} }

View File

@ -51,8 +51,7 @@ class session
: public net::coroutine : public net::coroutine
, public std::enable_shared_from_this<session> , public std::enable_shared_from_this<session>
{ {
websocket::stream<beast::ssl_stream< websocket::stream<beast::ssl_stream<beast::tcp_stream>> ws_;
beast::tcp_stream<net::io_context::strand>>> ws_;
beast::multi_buffer buffer_; beast::multi_buffer buffer_;
public: public:
@ -166,6 +165,7 @@ class listener
: public net::coroutine : public net::coroutine
, public std::enable_shared_from_this<listener> , public std::enable_shared_from_this<listener>
{ {
net::io_context& ioc_;
ssl::context& ctx_; ssl::context& ctx_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
tcp::socket socket_; tcp::socket socket_;
@ -175,7 +175,8 @@ public:
net::io_context& ioc, net::io_context& ioc,
ssl::context& ctx, ssl::context& ctx,
tcp::endpoint endpoint) tcp::endpoint endpoint)
: ctx_(ctx) : ioc_(ioc)
, ctx_(ctx)
, acceptor_(ioc) , acceptor_(ioc)
, socket_(ioc) , socket_(ioc)
{ {
@ -247,6 +248,9 @@ public:
// Create the session and run it // Create the session and run it
std::make_shared<session>(std::move(socket_), ctx_)->run(); std::make_shared<session>(std::move(socket_), ctx_)->run();
} }
// Make sure each session gets its own strand
socket_ = tcp::socket(beast::make_strand(ioc_));
} }
} }
} }

View File

@ -46,8 +46,7 @@ class session
: public net::coroutine : public net::coroutine
, public std::enable_shared_from_this<session> , public std::enable_shared_from_this<session>
{ {
websocket::stream< websocket::stream<beast::tcp_stream> ws_;
beast::tcp_stream<net::io_context::strand>> ws_;
beast::multi_buffer buffer_; beast::multi_buffer buffer_;
public: public:
@ -143,6 +142,7 @@ class listener
: public net::coroutine : public net::coroutine
, public std::enable_shared_from_this<listener> , public std::enable_shared_from_this<listener>
{ {
net::io_context& ioc_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
tcp::socket socket_; tcp::socket socket_;
@ -150,8 +150,9 @@ public:
listener( listener(
net::io_context& ioc, net::io_context& ioc,
tcp::endpoint endpoint) tcp::endpoint endpoint)
: acceptor_(ioc) : ioc_(ioc)
, socket_(ioc) , acceptor_(beast::make_strand(ioc))
, socket_(beast::make_strand(ioc))
{ {
beast::error_code ec; beast::error_code ec;
@ -221,6 +222,9 @@ public:
// Create the session and run it // Create the session and run it
std::make_shared<session>(std::move(socket_))->run(); std::make_shared<session>(std::move(socket_))->run();
} }
// Make sure each session gets its own strand
socket_ = tcp::socket(beast::make_strand(ioc_));
} }
} }
} }

View File

@ -11,6 +11,7 @@
#define BOOST_BEAST_TEST_TCP_HPP #define BOOST_BEAST_TEST_TCP_HPP
#include <boost/beast/core/detail/config.hpp> #include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/get_io_context.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp> #include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/beast/_experimental/test/handler.hpp> #include <boost/beast/_experimental/test/handler.hpp>
#include <boost/asio/ip/tcp.hpp> #include <boost/asio/ip/tcp.hpp>
@ -60,26 +61,28 @@ run_for(
ioc.restart(); ioc.restart();
} }
/** Connect two TCP/IP sockets together. /** Connect two TCP sockets together.
*/ */
inline template<class Executor>
bool bool
connect( connect(
net::ip::tcp::socket& s1, net::basic_stream_socket<net::ip::tcp, Executor>& s1,
net::ip::tcp::socket& s2) net::basic_stream_socket<net::ip::tcp, Executor>& s2)
{ {
// Sockets must use the same I/O context auto ioc1 = beast::detail::get_io_context(s1);
BOOST_ASSERT( auto ioc2 = beast::detail::get_io_context(s2);
std::addressof(s1.get_executor().context()) == if(! BEAST_EXPECT(ioc1 != nullptr))
std::addressof(s2.get_executor().context())); return false;
auto& ioc = s1.get_executor().context(); if(! BEAST_EXPECT(ioc2 != nullptr))
s1 = net::ip::tcp::socket(ioc); return false;
s2 = net::ip::tcp::socket(ioc); if(! BEAST_EXPECT(ioc1 == ioc2))
return false;
auto& ioc = *ioc1;
try try
{ {
net::ip::tcp::acceptor a( net::basic_socket_acceptor<
s1.get_executor().context()); net::ip::tcp, Executor> a(s1.get_executor());
auto ep = net::ip::tcp::endpoint( auto ep = net::ip::tcp::endpoint(
net::ip::make_address_v4("127.0.0.1"), 0); net::ip::make_address_v4("127.0.0.1"), 0);
a.open(ep.protocol()); a.open(ep.protocol());

View File

@ -13,6 +13,7 @@
#include <boost/beast/core/detail/config.hpp> #include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/stream_base.hpp> #include <boost/beast/core/detail/stream_base.hpp>
#include <boost/beast/core/error.hpp> #include <boost/beast/core/error.hpp>
#include <boost/beast/core/stream_traits.hpp>
#include <boost/beast/websocket/role.hpp> // VFALCO This is unfortunate #include <boost/beast/websocket/role.hpp> // VFALCO This is unfortunate
#include <boost/asio/async_result.hpp> #include <boost/asio/async_result.hpp>
#include <boost/asio/basic_stream_socket.hpp> #include <boost/asio/basic_stream_socket.hpp>
@ -21,7 +22,6 @@
#include <boost/asio/is_executor.hpp> #include <boost/asio/is_executor.hpp>
#include <boost/core/empty_value.hpp> #include <boost/core/empty_value.hpp>
#include <boost/config/workaround.hpp> #include <boost/config/workaround.hpp>
#include <boost/optional.hpp>
#include <boost/enable_shared_from_this.hpp> #include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <chrono> #include <chrono>
@ -42,14 +42,14 @@ namespace beast {
/** A stream socket wrapper with timeouts and associated executor. /** A stream socket wrapper with timeouts and associated executor.
This stream wraps a `net::basic_stream_socket` to provide 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 @li Optional timeouts may be specified for each logical asynchronous
operation performing any reading, writing, or connecting. operation performing any reading, writing, or connecting.
@li An <em>Executor</em> may be associated with the stream, which will @li An <em>Executor</em> may be associated with the stream, which will
be used to invoke any completion handlers which do not already have 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
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html">[P1322R0] Networking TS enhancement to enable custom I/O executors</a>. <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html">[P1322R0] Networking TS enhancement to enable custom I/O executors</a>.
Although the stream supports multiple concurrent outstanding asynchronous Although the stream supports multiple concurrent outstanding asynchronous
@ -109,7 +109,7 @@ namespace beast {
HTTP response with a different timeout. HTTP response with a different timeout.
@code @code
void process_http_1 (tcp_stream<net::io_context::executor_type>& stream, net::yield_context yield) void process_http_1 (tcp_stream& stream, net::yield_context yield)
{ {
flat_buffer buffer; flat_buffer buffer;
http::request<http::empty_body> req; http::request<http::empty_body> req;
@ -133,7 +133,7 @@ namespace beast {
applies to the entire combined operation of reading and writing: applies to the entire combined operation of reading and writing:
@code @code
void process_http_2 (tcp_stream<net::io_context::executor_type>& stream, net::yield_context yield) void process_http_2 (tcp_stream& stream, net::yield_context yield)
{ {
flat_buffer buffer; flat_buffer buffer;
http::request<http::empty_body> req; http::request<http::empty_body> req;
@ -155,7 +155,7 @@ namespace beast {
thusly: thusly:
@code @code
void do_ssl_handshake (net::ssl::stream<tcp_stream<net::io_context::executor_type>>& stream, net::yield_context yield) void do_ssl_handshake (net::ssl::stream<tcp_stream>& stream, net::yield_context yield)
{ {
// Require that the SSL handshake take no longer than 10 seconds // Require that the SSL handshake take no longer than 10 seconds
stream.expires_after(std::chrono::seconds(10)); stream.expires_after(std::chrono::seconds(10));
@ -176,7 +176,8 @@ namespace beast {
@tparam Executor A type meeting the requirements of <em>Executor</em> to @tparam Executor A type meeting the requirements of <em>Executor</em> to
be used for submitting all completion handlers which do not already have an 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 @par Thread Safety
<em>Distinct objects</em>: Safe.@n <em>Distinct objects</em>: Safe.@n
@ -190,12 +191,33 @@ namespace beast {
@li <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html">[P1322R0] Networking TS enhancement to enable custom I/O executors</a>. @li <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html">[P1322R0] Networking TS enhancement to enable custom I/O executors</a>.
*/ */
template<class Protocol, class Executor> template<
class Protocol,
class Executor = net::executor>
class basic_stream class basic_stream
#if ! BOOST_BEAST_DOXYGEN #if ! BOOST_BEAST_DOXYGEN
: private detail::stream_base : private detail::stream_base
#endif #endif
{ {
public:
/// The type of the underlying socket.
using socket_type =
net::basic_stream_socket<Protocol, Executor>;
/** 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<socket_type>;
/// The protocol type.
using protocol_type = Protocol;
/// The endpoint type.
using endpoint_type = typename Protocol::endpoint;
private:
static_assert(net::is_executor<Executor>::value, static_assert(net::is_executor<Executor>::value,
"Executor requirements not met"); "Executor requirements not met");
@ -206,33 +228,26 @@ public:
#endif #endif
struct impl_type struct impl_type
: boost::enable_shared_from_this<impl_type> : boost::enable_shared_from_this<impl_type>
, boost::empty_value<Executor>
{ {
// must come first
net::basic_stream_socket<
Protocol, Executor> socket;
op_state read; op_state read;
op_state write; op_state write;
net::basic_stream_socket<Protocol> socket;
impl_type(impl_type&&) = default; impl_type(impl_type&&) = default;
template<class... Args> template<class... Args>
explicit explicit
impl_type(Executor const&, Args&&...); impl_type(Args&&...);
template<class OtherProtocol>
impl_type(net::basic_stream_socket<OtherProtocol>&& socket_,
std::true_type);
template<class OtherProtocol>
impl_type(net::basic_stream_socket<OtherProtocol>&& socket_,
std::false_type);
impl_type& operator=(impl_type&&) = delete; impl_type& operator=(impl_type&&) = delete;
Executor const& beast::executor_type<socket_type>
ex() const noexcept ex() noexcept
{ {
return this->boost::empty_value<Executor>::get(); return this->socket.get_executor();
} }
void reset(); // set timeouts to never void reset(); // set timeouts to never
@ -248,13 +263,6 @@ private:
// but the implementation is still waiting on a timer. // but the implementation is still waiting on a timer.
boost::shared_ptr<impl_type> impl_; boost::shared_ptr<impl_type> impl_;
// Restricted until P1322R0 is incorporated into Boost.Asio.
static_assert(
std::is_convertible<
decltype(std::declval<Executor const&>().context()),
net::io_context&>::value,
"Only net::io_context is currently supported for executor_type::context()");
template<bool, class, class> class async_op; template<bool, class, class> class async_op;
template<class, class, class> template<class, class, class>
@ -287,22 +295,6 @@ private:
#endif #endif
public: 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<Protocol>;
/// The protocol type.
using protocol_type = Protocol;
/// The endpoint type.
using endpoint_type = typename Protocol::endpoint;
/** Destructor /** Destructor
This function destroys the stream, cancelling any outstanding This function destroys the stream, cancelling any outstanding
@ -311,106 +303,20 @@ public:
*/ */
~basic_stream(); ~basic_stream();
/** Construct the stream from an execution context. /** Constructor
This constructor creates the stream from an execution context. This constructor creates the stream by forwarding all arguments
The underlying socket needs to be open and connected or accepted to the underlying socket. The socket then needs to be open and
before data can be sent or received on it. connected or accepted before data can be sent or received on it.
@param ctx An object whose type meets the requirements of
<em>ExecutionContext</em>, 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`.
@param args A list of parameters forwarded to the constructor of @param args A list of parameters forwarded to the constructor of
the underlying socket. the underlying socket.
@note This function does not participate in overload resolution unless:
@li `std::is_convertible<ExecutionContext&, net::execution_context&>::value` is `true`, and
@li `std::is_constructible<executor_type, typename ExecutionContext::executor_type>::value` is `true`.
@see <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html">[P1322R0] Networking TS enhancement to enable custom I/O executors</a>
*/
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 <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html">[P1322R0] Networking TS enhancement to enable custom I/O executors</a>
*/ */
template<class... Args> template<class... Args>
explicit explicit
basic_stream( basic_stream(Args&&... args);
executor_type const& ex, Args&&... args);
/** Construct the stream from an existing socket. /** Move constructor
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<OtherProtocol, Protocol>::value && (
std::is_constructible<Executor,
decltype(std::declval<net::basic_stream_socket<
Protocol>&>().get_executor().context())>::value ||
std::is_constructible<Executor,
decltype(std::declval<net::basic_stream_socket<
Protocol>&>().get_executor())>::value)
>::type
#endif
>
explicit
basic_stream(net::basic_stream_socket<OtherProtocol>&& socket);
/** Move constructor.
@param other The other object from which the move will occur. @param other The other object from which the move will occur.

View File

@ -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 <boost/beast/core/stream_traits.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/strand.hpp>
#include <memory>
#include <type_traits>
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<class Executor>
net::io_context*
get_io_context(net::strand<Executor> const& ex)
{
return get_io_context(ex.get_inner_executor());
}
template<
class T,
class = typename std::enable_if<
std::is_same<T, net::executor>::value>::type>
net::io_context*
get_io_context(T const& ex)
{
auto p = ex.template target<typename
net::io_context::executor_type>();
if(! p)
return nullptr;
return std::addressof(p->context());
}
inline
net::io_context*
get_io_context(...)
{
return nullptr;
}
//------------------------------------------------------------------------------
template<class T>
net::io_context*
get_io_context_impl(T& t, std::true_type)
{
return get_io_context(
t.get_executor());
}
template<class T>
net::io_context*
get_io_context_impl(T const&, std::false_type)
{
return nullptr;
}
// Returns the io_context*, or nullptr, for any object.
template<class T>
net::io_context*
get_io_context(T& t)
{
return get_io_context_impl(t,
has_get_executor<T>{});
}
} // detail
} // beast
} // boost
#endif

View File

@ -15,6 +15,7 @@
#include <boost/core/exchange.hpp> #include <boost/core/exchange.hpp>
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
#include <utility>
namespace boost { namespace boost {
namespace beast { namespace beast {
@ -48,9 +49,10 @@ struct stream_base
bool pending = false; // if op is pending bool pending = false; // if op is pending
bool timeout = false; // if timed out bool timeout = false; // if timed out
template<class... Args>
explicit explicit
op_state(net::io_context& ioc) op_state(Args&&... args)
: timer(ioc) : timer(std::forward<Args>(args)...)
{ {
} }
}; };

View File

@ -32,44 +32,10 @@ template<class Protocol, class Executor>
template<class... Args> template<class... Args>
basic_stream<Protocol, Executor>:: basic_stream<Protocol, Executor>::
impl_type:: impl_type::
impl_type( impl_type(Args&&... args)
Executor const& ex_, : socket(std::forward<Args>(args)...)
Args&&... args) , read(ex())
: boost::empty_value<Executor>( , write(ex())
boost::empty_init_t{}, ex_)
, read(ex().context())
, write(ex().context())
, socket(std::forward<Args>(args)...)
{
reset();
}
template<class Protocol, class Executor>
template<class OtherProtocol>
basic_stream<Protocol, Executor>::
impl_type::
impl_type(net::basic_stream_socket<OtherProtocol>&& socket_,
std::true_type)
: boost::empty_value<Executor>(
boost::empty_init_t{}, socket_.get_executor())
, read(ex().context())
, write(ex().context())
, socket(std::move(socket_))
{
reset();
}
template<class Protocol, class Executor>
template<class OtherProtocol>
basic_stream<Protocol, Executor>::
impl_type::
impl_type(net::basic_stream_socket<OtherProtocol>&& socket_,
std::false_type)
: boost::empty_value<Executor>(boost::empty_init_t{},
socket_.get_executor().context())
, read(ex().context())
, write(ex().context())
, socket(std::move(socket_))
{ {
reset(); reset();
} }
@ -434,40 +400,12 @@ basic_stream<Protocol, Executor>::
impl_->close(); impl_->close();
} }
template<class Protocol, class Executor>
template<class ExecutionContext, class... Args, class>
basic_stream<Protocol, Executor>::
basic_stream(ExecutionContext& ctx, Args&&... args)
: impl_(boost::make_shared<impl_type>(
ctx.get_executor(),
ctx, std::forward<Args>(args)...))
{
// Restriction is necessary until Asio fully supports P1322R0
static_assert(
std::is_same<ExecutionContext, net::io_context>::value,
"Only net::io_context is currently supported for ExecutionContext");
}
template<class Protocol, class Executor> template<class Protocol, class Executor>
template<class... Args> template<class... Args>
basic_stream<Protocol, Executor>:: basic_stream<Protocol, Executor>::
basic_stream( basic_stream(Args&&... args)
executor_type const& ex, Args&&... args)
: impl_(boost::make_shared<impl_type>( : impl_(boost::make_shared<impl_type>(
ex, std::forward<Args>(args)...))
ex.context(), std::forward<Args>(args)...))
{
}
template<class Protocol, class Executor>
template<class OtherProtocol, class>
basic_stream<Protocol, Executor>::
basic_stream(net::basic_stream_socket<OtherProtocol>&& socket)
: impl_(boost::make_shared<impl_type>(
std::move(socket),
std::is_constructible<Executor,
decltype(std::declval<net::basic_stream_socket<
Protocol>&>().get_executor())>{}))
{ {
} }

View File

@ -438,10 +438,10 @@ using is_async_stream = std::integral_constant<bool,
@see close_socket @see close_socket
*/ */
template<class Protocol BOOST_ASIO_SVC_TPARAM> template<class Protocol>
void void
beast_close_socket( beast_close_socket(
net::basic_socket<Protocol BOOST_ASIO_SVC_TPARAM>& sock) net::basic_socket<Protocol>& sock)
{ {
boost::system::error_code ec; boost::system::error_code ec;
sock.close(ec); sock.close(ec);

View File

@ -12,20 +12,17 @@
#include <boost/beast/core/detail/config.hpp> #include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/basic_stream.hpp> #include <boost/beast/core/basic_stream.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/ip/tcp.hpp> #include <boost/asio/ip/tcp.hpp>
namespace boost { namespace boost {
namespace beast { namespace beast {
/** A TCP/IP stream socket with timeouts, rate limits, and executor. /** A TCP/IP stream socket with timeouts, rate limits, and polymorphic executor.
@tparam Executor The type of executor to use for all completion
handlers which do not already have an associated executor.
@see basic_stream @see basic_stream
*/ */
template<class Executor> using tcp_stream = basic_stream<net::ip::tcp, net::executor>;
using tcp_stream = basic_stream<net::ip::tcp, Executor>;
} // beast } // beast
} // boost } // boost

View File

@ -30,6 +30,8 @@ using file_body = basic_file_body<file>;
} // beast } // beast
} // boost } // boost
#ifndef BOOST_BEAST_NO_FILE_BODY_WIN32
#include <boost/beast/http/impl/file_body_win32.ipp> #include <boost/beast/http/impl/file_body_win32.ipp>
#endif
#endif #endif

View File

@ -32,7 +32,7 @@ namespace beast {
namespace http { namespace http {
namespace detail { namespace detail {
template<class, class, bool, class> template<class, class, class, bool, class>
class write_some_win32_op; class write_some_win32_op;
} // detail } // detail
@ -52,7 +52,7 @@ struct basic_file_body<file_win32>
friend class reader; friend class reader;
friend struct basic_file_body<file_win32>; friend struct basic_file_body<file_win32>;
template<class, class, bool, class> template<class, class, class, bool, class>
friend class detail::write_some_win32_op; friend class detail::write_some_win32_op;
template< template<
class Protocol, bool isRequest, class Fields> class Protocol, bool isRequest, class Fields>
@ -101,7 +101,7 @@ struct basic_file_body<file_win32>
class writer class writer
{ {
template<class, class, bool, class> template<class, class, class, bool, class>
friend class detail::write_some_win32_op; friend class detail::write_some_win32_op;
template< template<
class Protocol, bool isRequest, class Fields> class Protocol, bool isRequest, class Fields>
@ -276,7 +276,6 @@ reset(file_win32&& file, error_code& ec)
namespace detail { namespace detail {
template<class Unsigned> template<class Unsigned>
inline
boost::winapi::DWORD_ boost::winapi::DWORD_
lowPart(Unsigned n) lowPart(Unsigned n)
{ {
@ -286,7 +285,6 @@ lowPart(Unsigned n)
} }
template<class Unsigned> template<class Unsigned>
inline
boost::winapi::DWORD_ boost::winapi::DWORD_
highPart(Unsigned n, std::true_type) highPart(Unsigned n, std::true_type)
{ {
@ -296,7 +294,6 @@ highPart(Unsigned n, std::true_type)
} }
template<class Unsigned> template<class Unsigned>
inline
boost::winapi::DWORD_ boost::winapi::DWORD_
highPart(Unsigned, std::false_type) highPart(Unsigned, std::false_type)
{ {
@ -304,7 +301,6 @@ highPart(Unsigned, std::false_type)
} }
template<class Unsigned> template<class Unsigned>
inline
boost::winapi::DWORD_ boost::winapi::DWORD_
highPart(Unsigned n) highPart(Unsigned n)
{ {
@ -329,14 +325,17 @@ public:
#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
template< template<
class Protocol, class Handler, class Protocol, class Executor,
bool isRequest, class Fields> class Handler, bool isRequest, class Fields>
class write_some_win32_op class write_some_win32_op
: public beast::async_op_base< : public beast::async_op_base<Handler, Executor>
Handler, typename net::basic_stream_socket<
Protocol>::executor_type>
{ {
net::basic_stream_socket<Protocol>& sock_; static_assert(
std::is_same<Executor, net::io_context::executor_type>::value,
"Executor must be net::io_context::executor_type");
net::basic_stream_socket<
Protocol, Executor>& sock_;
serializer<isRequest, serializer<isRequest,
basic_file_body<file_win32>, Fields>& sr_; basic_file_body<file_win32>, Fields>& sr_;
std::size_t bytes_transferred_ = 0; std::size_t bytes_transferred_ = 0;
@ -346,14 +345,14 @@ public:
template<class Handler_> template<class Handler_>
write_some_win32_op( write_some_win32_op(
Handler_&& h, Handler_&& h,
net::basic_stream_socket<Protocol>& s, net::basic_stream_socket<
Protocol, Executor>& s,
serializer<isRequest, serializer<isRequest,
basic_file_body<file_win32>,Fields>& sr) basic_file_body<file_win32>,Fields>& sr)
: async_op_base< : async_op_base<
Handler, typename net::basic_stream_socket< Handler, Executor>(
Protocol>::executor_type>( std::forward<Handler_>(h),
std::forward<Handler_>(h), s.get_executor())
s.get_executor())
, sock_(s) , sock_(s)
, sr_(sr) , sr_(sr)
{ {
@ -442,10 +441,13 @@ public:
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class Protocol, bool isRequest, class Fields> template<
class Protocol, class Executor,
bool isRequest, class Fields>
std::size_t std::size_t
write_some( write_some(
net::basic_stream_socket<Protocol>& sock, net::basic_stream_socket<
Protocol, Executor>& sock,
serializer<isRequest, serializer<isRequest,
basic_file_body<file_win32>, Fields>& sr, basic_file_body<file_win32>, Fields>& sr,
error_code& ec) error_code& ec)
@ -509,21 +511,23 @@ write_some(
#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
template< template<
class Protocol, class Protocol, class Executor,
bool isRequest, class Fields, bool isRequest, class Fields,
class WriteHandler> class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
WriteHandler, void(error_code, std::size_t)) WriteHandler, void(error_code, std::size_t))
async_write_some( async_write_some(
net::basic_stream_socket<Protocol>& sock, net::basic_stream_socket<
Protocol, Executor>& sock,
serializer<isRequest, serializer<isRequest,
basic_file_body<file_win32>, Fields>& sr, basic_file_body<file_win32>, Fields>& sr,
WriteHandler&& handler) WriteHandler&& handler)
{ {
BOOST_BEAST_HANDLER_INIT( BOOST_BEAST_HANDLER_INIT(
WriteHandler, void(error_code, std::size_t)); WriteHandler, void(error_code, std::size_t));
detail::write_some_win32_op< detail::write_some_win32_op<
Protocol, Protocol, Executor,
BOOST_ASIO_HANDLER_TYPE(WriteHandler, BOOST_ASIO_HANDLER_TYPE(WriteHandler,
void(error_code, std::size_t)), void(error_code, std::size_t)),
isRequest, Fields>{ isRequest, Fields>{

View File

@ -19,7 +19,15 @@
#include <boost/beast/core/static_string.hpp> #include <boost/beast/core/static_string.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/assert.hpp> #include <boost/assert.hpp>
// This is for <boost/endian/buffers.hpp>
#if BOOST_WORKAROUND(BOOST_MSVC, > 0)
# pragma warning (push)
# pragma warning (disable: 4127) // conditional expression is constant
#endif
#include <boost/endian/buffers.hpp> #include <boost/endian/buffers.hpp>
#if BOOST_WORKAROUND(BOOST_MSVC, > 0)
# pragma warning (pop)
#endif
#include <cstdint> #include <cstdint>
namespace boost { namespace boost {

View File

@ -103,7 +103,7 @@ struct stream<NextLayer, deflateSupported>::impl_type
template<class... Args> template<class... Args>
impl_type(Args&&... args) impl_type(Args&&... args)
: stream(std::forward<Args>(args)...) : stream(std::forward<Args>(args)...)
, timer(stream.get_executor().context()) , timer(stream.get_executor())
{ {
timeout_opt.handshake_timeout = none(); timeout_opt.handshake_timeout = none();
timeout_opt.idle_timeout = none(); timeout_opt.idle_timeout = none();

View File

@ -25,14 +25,18 @@ namespace websocket {
namespace detail { namespace detail {
template<class Handler> template<
class Protocol, class Executor,
class Handler>
class teardown_tcp_op class teardown_tcp_op
: public beast::async_op_base< : public beast::async_op_base<
Handler, beast::executor_type< Handler, beast::executor_type<
net::ip::tcp::socket>> net::basic_stream_socket<
Protocol, Executor>>>
, public net::coroutine , public net::coroutine
{ {
using socket_type = net::ip::tcp::socket; using socket_type =
net::basic_stream_socket<Protocol, Executor>;
socket_type& s_; socket_type& s_;
role_type role_; role_type role_;
@ -46,8 +50,10 @@ public:
role_type role) role_type role)
: async_op_base<Handler, : async_op_base<Handler,
beast::executor_type< beast::executor_type<
net::ip::tcp::socket>>( net::basic_stream_socket<
std::forward<Handler_>(h), s.get_executor()) Protocol, Executor>>>(
std::forward<Handler_>(h),
s.get_executor())
, s_(s) , s_(s)
, role_(role) , role_(role)
{ {
@ -60,7 +66,6 @@ public:
std::size_t bytes_transferred = 0, std::size_t bytes_transferred = 0,
bool cont = true) bool cont = true)
{ {
using tcp = net::ip::tcp;
BOOST_ASIO_CORO_REENTER(*this) BOOST_ASIO_CORO_REENTER(*this)
{ {
nb_ = s_.non_blocking(); nb_ = s_.non_blocking();
@ -68,7 +73,7 @@ public:
if(ec) if(ec)
goto upcall; goto upcall;
if(role_ == role_type::server) if(role_ == role_type::server)
s_.shutdown(tcp::socket::shutdown_send, ec); s_.shutdown(net::socket_base::shutdown_send, ec);
if(ec) if(ec)
goto upcall; goto upcall;
for(;;) for(;;)
@ -81,7 +86,7 @@ public:
{ {
BOOST_ASIO_CORO_YIELD BOOST_ASIO_CORO_YIELD
s_.async_wait( s_.async_wait(
net::ip::tcp::socket::wait_read, net::socket_base::wait_read,
beast::detail::bind_continuation(std::move(*this))); beast::detail::bind_continuation(std::move(*this)));
continue; continue;
} }
@ -100,7 +105,7 @@ public:
} }
} }
if(role_ == role_type::client) if(role_ == role_type::client)
s_.shutdown(tcp::socket::shutdown_send, ec); s_.shutdown(net::socket_base::shutdown_send, ec);
if(ec) if(ec)
goto upcall; goto upcall;
s_.close(ec); s_.close(ec);
@ -124,15 +129,17 @@ public:
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class Protocol, class Executor>
void void
teardown( teardown(
role_type role, role_type role,
net::ip::tcp::socket& socket, net::basic_stream_socket<
Protocol, Executor>& socket,
error_code& ec) error_code& ec)
{ {
if(role == role_type::server) if(role == role_type::server)
socket.shutdown( socket.shutdown(
net::ip::tcp::socket::shutdown_send, ec); net::socket_base::shutdown_send, ec);
if(ec) if(ec)
return; return;
for(;;) for(;;)
@ -156,25 +163,32 @@ teardown(
} }
if(role == role_type::client) if(role == role_type::client)
socket.shutdown( socket.shutdown(
net::ip::tcp::socket::shutdown_send, ec); net::socket_base::shutdown_send, ec);
if(ec) if(ec)
return; return;
socket.close(ec); socket.close(ec);
} }
template<class TeardownHandler> template<
class Protocol, class Executor,
class TeardownHandler>
void void
async_teardown( async_teardown(
role_type role, role_type role,
net::ip::tcp::socket& socket, net::basic_stream_socket<
Protocol, Executor>& socket,
TeardownHandler&& handler) TeardownHandler&& handler)
{ {
static_assert(beast::detail::is_invocable< static_assert(beast::detail::is_invocable<
TeardownHandler, void(error_code)>::value, TeardownHandler, void(error_code)>::value,
"TeardownHandler requirements not met"); "TeardownHandler requirements not met");
detail::teardown_tcp_op<typename std::decay< detail::teardown_tcp_op<
TeardownHandler>::type>(std::forward< Protocol,
TeardownHandler>(handler), socket, role); Executor,
typename std::decay<TeardownHandler>::type>(
std::forward<TeardownHandler>(handler),
socket,
role);
} }
} // websocket } // websocket

View File

@ -80,13 +80,12 @@ class frame_test;
To declare the @ref stream object with a @ref tcp_stream in a To declare the @ref stream object with a @ref tcp_stream in a
multi-threaded asynchronous program using a strand, you may write: multi-threaded asynchronous program using a strand, you may write:
@code @code
websocket::stream<tcp_stream< websocket::stream<tcp_stream> ws{net::io_context::strand(ioc)};
net::io_context::strand>> ws{net::io_context::strand(ioc)};
@endcode @endcode
Alternatively, for a single-threaded or synchronous application Alternatively, for a single-threaded or synchronous application
you may write: you may write:
@code @code
websocket::stream<tcp_stream<net::io_context::executor_type>> ws(ioc); websocket::stream<tcp_stream> ws(ioc);
@endcode @endcode
@tparam NextLayer The type representing the next layer, to which @tparam NextLayer The type representing the next layer, to which

View File

@ -13,7 +13,7 @@
#include <boost/beast/core/detail/config.hpp> #include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/error.hpp> #include <boost/beast/core/error.hpp>
#include <boost/beast/websocket/role.hpp> #include <boost/beast/websocket/role.hpp>
#include <boost/asio/ip/tcp.hpp> #include <boost/asio/basic_stream_socket.hpp>
#include <type_traits> #include <type_traits>
namespace boost { namespace boost {
@ -125,11 +125,12 @@ namespace websocket {
@param ec Set to the error if any occurred. @param ec Set to the error if any occurred.
*/ */
BOOST_BEAST_DECL template<class Protocol, class Executor>
void void
teardown( teardown(
role_type role, role_type role,
net::ip::tcp::socket& socket, net::basic_stream_socket<
Protocol, Executor>& socket,
error_code& ec); error_code& ec);
/** Start tearing down a `net::ip::tcp::socket`. /** Start tearing down a `net::ip::tcp::socket`.
@ -159,11 +160,14 @@ teardown(
manner equivalent to using net::io_context::post(). manner equivalent to using net::io_context::post().
*/ */
template<class TeardownHandler> template<
class Protocol, class Executor,
class TeardownHandler>
void void
async_teardown( async_teardown(
role_type role, role_type role,
net::ip::tcp::socket& socket, net::basic_stream_socket<
Protocol, Executor>& socket,
TeardownHandler&& handler); TeardownHandler&& handler);
} // websocket } // websocket

View File

@ -24,6 +24,7 @@ add_executable (tests-beast-core
_detail_bind_continuation.cpp _detail_bind_continuation.cpp
_detail_buffer.cpp _detail_buffer.cpp
_detail_clamp.cpp _detail_clamp.cpp
_detail_get_io_context.cpp
_detail_is_invocable.cpp _detail_is_invocable.cpp
_detail_read.cpp _detail_read.cpp
_detail_sha1.cpp _detail_sha1.cpp

View File

@ -12,6 +12,7 @@ local SOURCES =
_detail_bind_continuation.cpp _detail_bind_continuation.cpp
_detail_buffer.cpp _detail_buffer.cpp
_detail_clamp.cpp _detail_clamp.cpp
_detail_get_io_context.cpp
_detail_is_invocable.cpp _detail_is_invocable.cpp
_detail_read.cpp _detail_read.cpp
_detail_sha1.cpp _detail_sha1.cpp

View File

@ -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 <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/make_strand.hpp>
#include <boost/beast/core/detail/get_io_context.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/asio/strand.hpp>
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>(
net::executor(ioc.get_executor()))) == &ioc);
#endif
}
void
run() override
{
testFunction();
}
};
BEAST_DEFINE_TESTSUITE(beast,core,get_io_context);
} // detail
} // beast
} // boost

View File

@ -303,7 +303,6 @@ private:
} }
}; };
} // (anon) } // (anon)
class basic_stream_test class basic_stream_test
@ -319,9 +318,6 @@ public:
void void
testSpecialMembers() testSpecialMembers()
{ {
using stream_type = tcp_stream<
net::io_context::executor_type>;
net::io_context ioc; net::io_context ioc;
// net::io_context::executor_type // net::io_context::executor_type
@ -332,7 +328,8 @@ public:
basic_stream<tcp, executor> s2(ex); basic_stream<tcp, executor> s2(ex);
basic_stream<tcp, executor> s3(ioc, tcp::v4()); basic_stream<tcp, executor> s3(ioc, tcp::v4());
basic_stream<tcp, executor> s4(std::move(s1)); basic_stream<tcp, executor> s4(std::move(s1));
s2.socket() = tcp::socket(ioc); s2.socket() =
net::basic_stream_socket<tcp, executor>(ioc);
BEAST_EXPECT(s1.get_executor() == ex); BEAST_EXPECT(s1.get_executor() == ex);
BEAST_EXPECT(s2.get_executor() == ex); BEAST_EXPECT(s2.get_executor() == ex);
BEAST_EXPECT(s3.get_executor() == ex); BEAST_EXPECT(s3.get_executor() == ex);
@ -358,14 +355,19 @@ public:
basic_stream<tcp, strand> s1(ex); basic_stream<tcp, strand> s1(ex);
basic_stream<tcp, strand> s2(ex, tcp::v4()); basic_stream<tcp, strand> s2(ex, tcp::v4());
basic_stream<tcp, strand> s3(std::move(s1)); basic_stream<tcp, strand> 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(s1.get_executor() == ex);
BEAST_EXPECT(s2.get_executor() == ex); BEAST_EXPECT(s2.get_executor() == ex);
BEAST_EXPECT(s3.get_executor() == ex); BEAST_EXPECT(s3.get_executor() == ex);
#if 0
BEAST_EXPECT((! static_cast< BEAST_EXPECT((! static_cast<
basic_stream<tcp, strand> const&>( basic_stream<tcp, strand> const&>(
s2).socket().is_open())); s2).socket().is_open()));
#endif
test_sync_stream< test_sync_stream<
basic_stream< basic_stream<
@ -378,6 +380,7 @@ public:
// construction from existing socket // construction from existing socket
#if 0
{ {
tcp::socket sock(ioc); tcp::socket sock(ioc);
basic_stream<tcp, executor> stream(std::move(sock)); basic_stream<tcp, executor> stream(std::move(sock));
@ -387,22 +390,13 @@ public:
tcp::socket sock(ioc); tcp::socket sock(ioc);
basic_stream<tcp, strand> stream(std::move(sock)); basic_stream<tcp, strand> stream(std::move(sock));
} }
#endif
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);
// layers // layers
{ {
net::socket_base::keep_alive opt; net::socket_base::keep_alive opt;
stream_type s(ioc); tcp_stream s(ioc);
s.socket().open(tcp::v4()); s.socket().open(tcp::v4());
s.socket().get_option(opt); s.socket().get_option(opt);
BEAST_EXPECT(! opt.value()); BEAST_EXPECT(! opt.value());
@ -450,7 +444,7 @@ public:
void void
testRead() testRead()
{ {
using stream_type = tcp_stream< using stream_type = basic_stream<tcp,
net::io_context::executor_type>; net::io_context::executor_type>;
char buf[4]; char buf[4];
@ -585,7 +579,7 @@ public:
void void
testWrite() testWrite()
{ {
using stream_type = tcp_stream< using stream_type = basic_stream<tcp,
net::io_context::executor_type>; net::io_context::executor_type>;
char buf[4]; char buf[4];
@ -667,7 +661,7 @@ public:
void void
testConnect() testConnect()
{ {
using stream_type = tcp_stream< using stream_type = basic_stream<tcp,
net::io_context::executor_type>; net::io_context::executor_type>;
struct range struct range
@ -1057,7 +1051,7 @@ public:
void void
testMembers() testMembers()
{ {
using stream_type = tcp_stream< using stream_type = basic_stream<tcp,
net::io_context::executor_type>; net::io_context::executor_type>;
class handler class handler
@ -1184,7 +1178,7 @@ public:
return {}; return {};
} }
void process_http_1 (tcp_stream<net::io_context::executor_type>& stream, net::yield_context yield) void process_http_1 (tcp_stream& stream, net::yield_context yield)
{ {
flat_buffer buffer; flat_buffer buffer;
http::request<http::empty_body> req; http::request<http::empty_body> req;
@ -1201,7 +1195,7 @@ public:
http::async_write (stream, res, yield); http::async_write (stream, res, yield);
} }
void process_http_2 (tcp_stream<net::io_context::executor_type>& stream, net::yield_context yield) void process_http_2 (tcp_stream& stream, net::yield_context yield)
{ {
flat_buffer buffer; flat_buffer buffer;
http::request<http::empty_body> req; http::request<http::empty_body> req;

View File

@ -9,3 +9,37 @@
// Test that header file is self-contained. // Test that header file is self-contained.
#include <boost/beast/core/tcp_stream.hpp> #include <boost/beast/core/tcp_stream.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
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

View File

@ -10,6 +10,7 @@
// Test that header file is self-contained. // Test that header file is self-contained.
#include <boost/beast/websocket/stream.hpp> #include <boost/beast/websocket/stream.hpp>
#include <boost/beast/core/make_strand.hpp>
#include <boost/beast/core/tcp_stream.hpp> #include <boost/beast/core/tcp_stream.hpp>
#include <boost/asio/strand.hpp> #include <boost/asio/strand.hpp>
@ -157,11 +158,10 @@ public:
{ {
net::io_context ioc; net::io_context ioc;
{ {
websocket::stream<tcp_stream< websocket::stream<tcp_stream> ws{make_strand(ioc)};
net::io_context::strand>> ws{net::io_context::strand(ioc)};
} }
{ {
websocket::stream<tcp_stream<net::io_context::executor_type>> ws(ioc); websocket::stream<tcp_stream> ws(ioc);
} }
} }

View File

@ -28,6 +28,7 @@ public:
run() override run() override
{ {
testJavadoc(); testJavadoc();
pass();
} }
}; };

View File

@ -59,8 +59,8 @@ boost::ignore_unused(ec);
{ {
//[ws_snippet_6 //[ws_snippet_6
std::string const host = "example.com"; std::string const host = "example.com";
net::ip::tcp::resolver r{ioc}; net::ip::tcp::resolver r(ioc);
stream<tcp_stream<net::io_context::executor_type>> ws{ioc}; stream<tcp_stream> ws(ioc);
auto const results = r.resolve(host, "ws"); auto const results = r.resolve(host, "ws");
connect(get_lowest_layer(ws), results.begin(), results.end()); connect(get_lowest_layer(ws), results.begin(), results.end());
//] //]
@ -69,7 +69,7 @@ boost::ignore_unused(ec);
{ {
//[ws_snippet_7 //[ws_snippet_7
net::ip::tcp::acceptor acceptor{ioc}; net::ip::tcp::acceptor acceptor{ioc};
stream<tcp_stream<net::io_context::executor_type>> ws{acceptor.get_executor().context()}; stream<tcp_stream> ws{acceptor.get_executor()};
acceptor.accept(get_lowest_layer(ws).socket()); acceptor.accept(get_lowest_layer(ws).socket());
//] //]
} }