basic_stream subsumes stranded_stream:

* All functionality of stranded_stream is folded into basic_stream
* tcp_stream is an alias for basic_stream with tcp
* The tests are expanded to produce full coverage
* Timeout implementation is simplified
This commit is contained in:
Vinnie Falco
2019-02-09 05:15:48 -08:00
parent 0ce8ebbefd
commit 9b14774ada
20 changed files with 3151 additions and 3920 deletions

View File

@@ -1,6 +1,7 @@
Version 213: Version 213:
* Fix posix_file::close handling of EINTR * Fix posix_file::close handling of EINTR
* basic_stream subsumes stranded_stream:
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@@ -46,12 +46,15 @@ facilities for authoring and working with layered streams:
[table Layered Stream Algorithms and Types [table Layered Stream Algorithms and Types
[[Name][Description]] [[Name][Description]]
[[ [[
[link beast.ref.boost__beast__timeout_stream `timeout_stream`] [link beast.ref.boost__beast__basic_stream `basic_stream`]
[link beast.ref.boost__beast__basic_timeout_stream `basic_timeout_stream`] [link beast.ref.boost__beast__tcp_stream `tcp_stream`]
][ ][
A timeout stream meets the requirements for synchronous and asynchronous This stream can be used for synchronous and asynchronous reading
read and write streams, and additionally provides configurable timeouts and writing. It allows timeouts to be set on logical operations,
for logical operations that include reading, writing, and/or connecting. and can have an executor associated with the stream which is
used to invoke completion handlers. This lets you set a strand
on the stream once, which is then used for all asynchronous
operations automatically.
]] ]]
[[ [[
[link beast.ref.boost__beast__buffered_read_stream `buffered_read_stream`] [link beast.ref.boost__beast__buffered_read_stream `buffered_read_stream`]
@@ -109,16 +112,6 @@ facilities for authoring and working with layered streams:
allows for move-construction and move-assignment, and also implements allows for move-construction and move-assignment, and also implements
a work-around for a performance limitation in the original SSL stream. a work-around for a performance limitation in the original SSL stream.
]] ]]
[[
[link beast.ref.boost__beast__stranded_socket `stranded_socket`]
][
A timeout stream meets the requirements for synchronous and asynchronous
read and write streams by passing I/O through to an underlying
`net::basic_stream_socket`, and additionally supports
[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html [P1322R0] "Networking TS enhancement to enable custom I/O executors"],
allowing a custom executor (such a strand) to be used for all
asynchronous operations.
]]
] ]
[heading Example] [heading Example]

View File

@@ -24,7 +24,7 @@
<bridgehead renderas="sect3">Classes&nbsp;<emphasis role="normal">(1 of 2)</emphasis></bridgehead> <bridgehead renderas="sect3">Classes&nbsp;<emphasis role="normal">(1 of 2)</emphasis></bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__async_op_base">async_op_base</link>&nbsp;<emphasis role="green">&#128946;</emphasis></member> <member><link linkend="beast.ref.boost__beast__async_op_base">async_op_base</link>&nbsp;<emphasis role="green">&#128946;</emphasis></member>
<member><link linkend="beast.ref.boost__beast__basic_timeout_stream">basic_timeout_stream</link>&nbsp;<emphasis role="green">&#128946;</emphasis></member> <member><link linkend="beast.ref.boost__beast__basic_stream">basic_stream</link>&nbsp;<emphasis role="green">&#128946;</emphasis></member>
<member><link linkend="beast.ref.boost__beast__file">file</link></member> <member><link linkend="beast.ref.boost__beast__file">file</link></member>
<member><link linkend="beast.ref.boost__beast__file_mode">file_mode</link></member> <member><link linkend="beast.ref.boost__beast__file_mode">file_mode</link></member>
<member><link linkend="beast.ref.boost__beast__file_posix">file_posix</link></member> <member><link linkend="beast.ref.boost__beast__file_posix">file_posix</link></member>
@@ -46,10 +46,9 @@
<member><link linkend="beast.ref.boost__beast__span">span</link></member> <member><link linkend="beast.ref.boost__beast__span">span</link></member>
<member><link linkend="beast.ref.boost__beast__static_string">static_string</link></member> <member><link linkend="beast.ref.boost__beast__static_string">static_string</link></member>
<member><link linkend="beast.ref.boost__beast__stable_async_op_base">stable_async_op_base</link>&nbsp;<emphasis role="green">&#128946;</emphasis></member> <member><link linkend="beast.ref.boost__beast__stable_async_op_base">stable_async_op_base</link>&nbsp;<emphasis role="green">&#128946;</emphasis></member>
<member><link linkend="beast.ref.boost__beast__stranded_socket">stranded_socket</link>&nbsp;<emphasis role="green">&#128946;</emphasis></member>
<member><link linkend="beast.ref.boost__beast__string_param">string_param</link></member> <member><link linkend="beast.ref.boost__beast__string_param">string_param</link></member>
<member><link linkend="beast.ref.boost__beast__string_view">string_view</link></member> <member><link linkend="beast.ref.boost__beast__string_view">string_view</link></member>
<member><link linkend="beast.ref.boost__beast__timeout_stream">timeout_stream</link>&nbsp;<emphasis role="green">&#128946;</emphasis></member> <member><link linkend="beast.ref.boost__beast__tcp_stream">tcp_stream</link>&nbsp;<emphasis role="green">&#128946;</emphasis></member>
</simplelist> </simplelist>
<bridgehead renderas="sect3">Constants</bridgehead> <bridgehead renderas="sect3">Constants</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">

View File

@@ -14,14 +14,19 @@
[heading Boost 1.70] [heading Boost 1.70]
[/ includes up to version 209] [/ includes up to version 209]
[/ [/
* Use `beast::tcp_socket` instead of `boost::asio::ip::tcp::socket`!
* subscribe to this GitHub issue to be informed!
* we need testers!
* star the repo!
Exposition: Exposition:
Enlarged scope Enlarged scope
New quality of life features: - `net` is a namespace alias for `boost::asio`
timed stream - New quality of life features:
tcp_socket
async_op_base, stable_async_op_base async_op_base, stable_async_op_base
New Networking refresher - New Networking refresher
New websocket-chat-multi example - New websocket-chat-multi example
* `net` is a namespace alias for `boost::asio`
] ]
[tip [tip

View File

@@ -12,7 +12,7 @@
#include <boost/beast/core/detail/config.hpp> #include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/basic_timeout_stream.hpp> #include <boost/beast/core/basic_stream.hpp>
#include <boost/beast/core/bind_handler.hpp> #include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/buffer_size.hpp> #include <boost/beast/core/buffer_size.hpp>
#include <boost/beast/core/buffer_traits.hpp> #include <boost/beast/core/buffer_traits.hpp>
@@ -41,10 +41,9 @@
#include <boost/beast/core/span.hpp> #include <boost/beast/core/span.hpp>
#include <boost/beast/core/static_buffer.hpp> #include <boost/beast/core/static_buffer.hpp>
#include <boost/beast/core/static_string.hpp> #include <boost/beast/core/static_string.hpp>
#include <boost/beast/core/stranded_socket.hpp>
#include <boost/beast/core/stream_traits.hpp> #include <boost/beast/core/stream_traits.hpp>
#include <boost/beast/core/string.hpp> #include <boost/beast/core/string.hpp>
#include <boost/beast/core/string_param.hpp> #include <boost/beast/core/string_param.hpp>
#include <boost/beast/core/timeout_stream.hpp> #include <boost/beast/core/tcp_stream.hpp>
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,847 +0,0 @@
//
// Copyright (c) 2018 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_CORE_BASIC_TIMEOUT_SOCKET_HPP
#define BOOST_BEAST_CORE_BASIC_TIMEOUT_SOCKET_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/timeout_stream_base.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/basic_stream_socket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/config/workaround.hpp>
#include <boost/optional.hpp>
#include <chrono>
#include <memory>
namespace boost {
namespace beast {
//------------------------------------------------------------------------------
/** A stream socket with an integrated timeout on reading, writing, and connecting.
This layered stream wrapper manages a contained `net::basic_stream_socket`
object to provide the following additional features:
@li A timeout may be specified for each logical asynchronous
operation that performs reading, writing, and/or connecting.
@li The class template is parameterized on the executor type to be
used for all asynchronous operations. This achieves partial support for
<em>"Networking TS enhancement to enable custom I/O executors"</em>
(<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html">P1322R0</a>).
Objects of this type are used in place of a regular networking socket,
where timeouts on operations are desired. In particular this class
template is designed to be used in place of `net::basic_stream_socket`
or more typically, `net:ip::tcp::socket`. Constructors are provided to
use a particular execution context or executor, subject to temporary
restrictions based on the current implementation of networking. Additional
constructors allow the timeout stream to be constructed from a networking
socket that already exists.
Although the stream supports multiple concurrent outstanding asynchronous
operations, the stream object itself is not thread-safe. The caller is
responsible for ensuring that the stream is accessed from only one thread
at a time. This includes the times when the stream, and its underlying
socket, is accessed by the networking implementation. To meet these
thread safety requirements, all asynchronous operations must be performed
by the stream within the same implicit strand (only one thread calling `run()`
on the corresponding `net::io_context`) or within the same explicit strand
such as an instance of `net::strand`.
When using explicit strands, calls to initiating functions may use
`net::bind_handler` with a suitable executor on the completion handler.
Alternatively, the executor may be specified once by passing it as a stream
class template parameter, and providing an instance of the executor upon
construction (if the executor type is not <em>DefaultConstructible</em>).
Regardless of the method chosen, the executor used with the stream must
provide the following guarantees:
@li <b>Ordering:</b>
Function objects submitted to the executor from the same thread shall
execute in the order they were submitted.
@li <b>Concurrency:</b>
Function objects submitted to the executor shall never run concurrently
with each other.
The executor type `net::strand` meets these requirements. Use of a
strand as the executor in the stream class template offers an additional
notational convenience: the strand does not need to be specified in
each individual initiating function call.
@par Usage
To use this stream declare an instance of the class. Then, before
each logical operation for which a timeout is desired, call
@ref expires_after with a duration, or call @ref expires_at with a
time point. Alternatively, call @ref expires_never to disable the
timeout for subsequent logical operations. A logical operation
is any series of one or more direct or indirect calls to the timeout
stream's read, write, or connect functions.
When a timeout is set and a mixed operation is performed (one that
includes both reads and writes, for example) the timeout applies
to all of the intermediate asynchronous operations used in the
enclosing operation. This allows timeouts to be applied to stream
algorithms which were not written specifically to allow for timeouts,
when those algorithms are passed a timeout stream with a timeout set.
When a timeout occurs the socket will be closed, canceling any
pending I/O operations. The completion handlers for these canceled
operations will be invoked with the error @ref beast::error::timeout.
@par Examples
This function reads an HTTP request with a timeout, then sends the
HTTP response with a different timeout.
@code
void process_http_1 (timeout_stream& stream, net::yield_context yield)
{
flat_buffer buffer;
http::request<http::empty_body> req;
// Read the request, with a 15 second timeout
stream.expires_after(std::chrono::seconds(15));
http::async_read(stream, buffer, req, yield);
// Calculate the response
http::response<http::string_body> res = make_response(req);
// Send the response, with a 30 second timeout.
stream.expires_after (std::chrono::seconds(30));
http::async_write (stream, res, yield);
}
@endcode
The example above could be expressed using a single timeout with a
simple modification. The function that follows first reads an HTTP
request then sends the HTTP response, with a single timeout that
applies to the entire combined operation of reading and writing:
@code
void process_http_2 (timeout_stream& stream, net::yield_context yield)
{
flat_buffer buffer;
http::request<http::empty_body> req;
// Require that the read and write combined take no longer than 30 seconds
stream.expires_after(std::chrono::seconds(30));
http::async_read(stream, buffer, req, yield);
http::response<http::string_body> res = make_response(req);
http::async_write (stream, res, yield);
}
@endcode
Some stream algorithms, such as `ssl::stream::async_handshake` perform
both reads and writes. A timeout set before calling the initiating function
of such composite stream algorithms will apply to the entire composite
operation. For example, a timeout may be set on performing the SSL handshake
thusly:
@code
void do_ssl_handshake (net::ssl::stream<timeout_stream>& stream, net::yield_context yield)
{
// Require that the SSL handshake take no longer than 10 seconds
stream.expires_after(std::chrono::seconds(10));
stream.async_handshake(net::ssl::stream_base::client, yield);
}
@endcode
@tparam Protocol A type meeting the requirements of <em>Protocol</em>
representing the protocol the protocol to use for the basic stream socket.
A common choice is `net::ip::tcp`.
@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
associated executor. This type defaults to `net::io_context::executor_type`.
@par Thread Safety
<em>Distinct objects</em>: Safe.@n
<em>Shared objects</em>: Unsafe. The application must also ensure
that all asynchronous operations are performed within the same
implicit or explicit strand.
@see <em>"Networking TS enhancement to enable custom I/O executors"</em>
(<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html">P1322R0</a>).
*/
template<
class Protocol,
class Executor = typename
net::basic_stream_socket<Protocol>::executor_type>
class basic_timeout_stream
#if ! BOOST_BEAST_DOXYGEN
: private detail::timeout_stream_base
#endif
{
using time_point = typename
std::chrono::steady_clock::time_point;
static constexpr time_point never()
{
return (time_point::max)();
}
static std::size_t constexpr no_limit =
(std::numeric_limits<std::size_t>::max)();
using tick_type = std::uint64_t;
struct op_state
{
net::steady_timer timer; // for timing out
tick_type tick = 0; // counts waits
bool pending = false; // if op is pending
bool timeout = false; // if timed out
explicit
op_state(net::io_context& ioc)
: timer(ioc)
{
}
};
// friend class template declaration in a class template is ignored
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88672
#if BOOST_WORKAROUND(BOOST_GCC, > 0)
public:
#endif
struct impl_type
: std::enable_shared_from_this<impl_type>
{
Executor ex; // must come first
op_state read;
op_state write;
net::basic_stream_socket<Protocol> socket;
template<class... Args>
explicit
impl_type(Executor const&, Args&&...);
impl_type(impl_type&&) = default;
impl_type& operator=(impl_type&&);
void reset(); // set timeouts to never
void close(); // cancel everything
};
#if BOOST_WORKAROUND(BOOST_GCC, > 0)
private:
#endif
// We use shared ownership for the state so it can
// outlive the destruction of the stream_socket object,
// in the case where there is no outstanding read or
// write but the implementation is still waiting on
// the rate timer.
std::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<class, class, class>
friend class detail::timeout_stream_connect_op;
template<class, class>
friend class basic_timeout_stream;
struct timeout_handler;
public:
/// The type of the executor associated with the object.
using executor_type = Executor;
/// The type of the next layer.
using next_layer_type = net::basic_stream_socket<Protocol>;
/// The protocol type.
using protocol_type = Protocol;
/// The endpoint type.
using endpoint_type = typename Protocol::endpoint;
/** Destructor
This function destroys the socket, cancelling any outstanding
asynchronous operations associated with the socket as if by
calling cancel.
*/
~basic_timeout_stream();
/** Construct the stream without opening it.
This constructor creates a timeout stream. The underlying socket needs
to be opened and then 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 for any asynchronous operations performed on the socket.
Currently, the only supported type for `ctx` is `net::io_context`.
@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 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html
*/
template<
class ExecutionContext
#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_timeout_stream(ExecutionContext& ctx);
/** Construct the stream without opening it.
This constructor creates a timeout stream. The underlying socket needs
to be opened and then connected or accepted before data can be sent or
received on it.
@param ex The executor which stream will use to dispatch handlers for
any asynchronous operations performed on the underlying socket.
Currently, only executors that return a `net::io_context&` from
`ex.context()` are supported.
@see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html
*/
explicit
basic_timeout_stream(executor_type const& ex);
/** Construct the stream with an existing socket.
This constructor creates a timeout stream by taking ownership of an
already existing socket. The executor will be the same as the executor
of the provided socket.
@param socket The socket object to construct with, which becomes the
next layer of the timeout stream. Ownership of this socket is
transferred by move.
@see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html
*/
explicit
basic_timeout_stream(
net::basic_stream_socket<Protocol>&& socket);
/** Construct the stream with an executor and existing socket.
This constructor creates a timeout stream by taking ownership of an
already existing socket.
@param ex The executor which stream will use to dispatch handlers for
any asynchronous operations performed on the underlying socket.
Currently, only executors that return a `net::io_context&` from
`ex.context()` are supported.
@param socket The socket object to construct with, which becomes the
next layer of the timeout stream. Ownership of this socket is
transferred by move.
@see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html
*/
basic_timeout_stream(
executor_type const& ex,
net::basic_stream_socket<Protocol>&& socket);
/** Move-construct a stream from another stream
This constructor moves a stream from one object to another.
The behavior of moving a stream while asynchronous operations
are outstanding is undefined.
@param other The other object from which the move will occur.
@note Following the move, the moved-from object is in a newly
constructed state.
*/
basic_timeout_stream(basic_timeout_stream&& other);
/** Move-assign a stream from another stream
This assignment operator moves a stream socket from one object
to another.
The behavior of move assignment while asynchronous operations
are pending is undefined.
@param other The other basic_timeout_stream object from which the
move will occur.
@note Following the move, the moved-from object is a newly
constructed state.
*/
basic_timeout_stream& operator=(basic_timeout_stream&& other);
//--------------------------------------------------------------------------
/** Get the executor associated with the object.
This function may be used to obtain the executor object that the
stream uses to dispatch handlers for asynchronous operations.
@return A copy of the executor that stream will use to dispatch handlers.
*/
executor_type
get_executor() const noexcept
{
return impl_->ex;
}
/** Get a reference to the underlying socket.
This function returns a reference to the next layer
in a stack of stream layers.
@return A reference to the next layer in the stack of
stream layers.
*/
next_layer_type&
next_layer() noexcept
{
return impl_->socket;
}
/** Get a reference to the underlying socket.
This function returns a reference to the next layer in a
stack of stream layers.
@return A reference to the next layer in the stack of
stream layers.
*/
next_layer_type const&
next_layer() const noexcept
{
return impl_->socket;
}
/** Set the timeout for the next logical operation.
This sets either the read timer, the write timer, or
both timers to expire after the specified amount of time
has elapsed. If a timer expires when the corresponding
asynchronous operation is outstanding, the stream will be
closed and any outstanding operations will complete with the
error @ref beast::error::timeout. Otherwise, if the timer
expires while no operations are outstanding, and the expiraton
is not set again, the next operation will time out immediately.
The timer applies collectively to any asynchronous reads
or writes initiated after the expiration is set, until the
expiration is set again. A call to @ref beast::async_connect
counts as both a read and a write.
@param expiry_time The amount of time after which a logical
operation should be considered timed out.
*/
void
expires_after(
std::chrono::nanoseconds expiry_time);
/** Set the timeout for the next logical operation.
This sets either the read timer, the write timer, or both
timers to expire at the specified time point. If a timer
expires when the corresponding asynchronous operation is
outstanding, the stream will be closed and any outstanding
operations will complete with the error @ref beast::error::timeout.
Otherwise, if the timer expires while no operations are outstanding,
and the expiraton is not set again, the next operation will time out
immediately.
The timer applies collectively to any asynchronous reads
or writes initiated after the expiration is set, until the
expiration is set again. A call to @ref beast::async_connect
counts as both a read and a write.
@param expiry_time The time point after which a logical
operation should be considered timed out.
*/
void
expires_at(net::steady_timer::time_point expiry_time);
/// Disable the timeout for the next logical operation.
void
expires_never();
/// Cancel all asynchronous operations associated with the socket.
void
cancel();
/** Close the timed stream.
This cancels all timers and pending I/O. The completion handlers
for any pending I/O will see an error code.
*/
void
close();
//--------------------------------------------------------------------------
/** Start an asynchronous connect.
This function is used to asynchronously connect a socket to the
specified remote endpoint. The function call always returns immediately.
The underlying socket is automatically opened if it is not already open.
If the connect fails, and the socket was automatically opened, the socket
is not returned to the closed state.
@param ep The remote endpoint to which the underlying socket will be
connected. Copies will be made of the endpoint object as required.
@param handler The handler to be called when the operation completes.
The implementation will take ownership of the handler by move construction.
The handler must be invocable with this signature:
@code
void handler(
error_code ec // Result of operation
);
@endcode
Regardless of whether the asynchronous operation completes immediately or
not, the handler will not be invoked from within this function. Invocation
of the handler will be performed in a manner equivalent to using
`net::post()`.
*/
template<class ConnectHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler,
void(error_code))
async_connect(
endpoint_type ep,
ConnectHandler&& handler);
/** Start an asynchronous read.
This function is used to asynchronously read data from the stream.
The function call always returns immediately.
@param buffers A range of zero or more buffers to read stream data into.
Although the buffers object may be copied as necessary, ownership of the
underlying memory blocks is retained by the caller, which must guarantee
that they remain valid until the handler is called.
@param handler The handler to be called when the operation completes.
The implementation will take ownership of the handler by move construction.
The handler must be invocable with this signature:
@code
void handler(
error_code error, // Result of operation.
std::size_t bytes_transferred // Number of bytes read.
);
@endcode
Regardless of whether the asynchronous operation completes immediately or
not, the handler will not be invoked from within this function. Invocation
of the handler will be performed in a manner equivalent to using
`net::post()`.
*/
template<class MutableBufferSequence, class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
void(error_code, std::size_t))
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler);
/** Start an asynchronous write.
This function is used to asynchronously write data to the stream.
The function call always returns immediately.
@param buffers A range of zero or more buffers to be written to the stream.
Although the buffers object may be copied as necessary, ownership of the
underlying memory blocks is retained by the caller, which must guarantee
that they remain valid until the handler is called.
@param handler The handler to be called when the operation completes.
The implementation will take ownership of the handler by move construction.
The handler must be invocable with this signature:
@code
void handler(
error_code error, // Result of operation.
std::size_t bytes_transferred // Number of bytes written.
);
@endcode
Regardless of whether the asynchronous operation completes immediately or
not, the handler will not be invoked from within this function. Invocation
of the handler will be performed in a manner equivalent to using
`net::post()`.
*/
template<class ConstBufferSequence, class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
void(error_code, std::size_t))
async_write_some(
ConstBufferSequence const& buffers,
WriteHandler&& handler);
};
//------------------------------------------------------------------------------
/** Asynchronously establishes a socket connection by trying each endpoint in a sequence, and terminating if a timeout occurs.
This function attempts to connect a socket to one of a sequence of
endpoints. It does this by repeated calls to the underlying socket's
@c async_connect member function, once for each endpoint in the sequence,
until a connection is successfully established or a timeout occurs.
@param stream The @ref beast::basic_timeout_stream to be connected. If the
underlying socket is already open, it will be closed.
@param endpoints A sequence of endpoints. This this object must meet
the requirements of <em>EndpointSequence</em>.
@param handler The handler to be called when the connect operation
completes. Ownership of the handler may be transferred. The function
signature of the handler must be:
@code
void handler(
// Result of operation. if the sequence is empty, set to
// net::error::not_found. Otherwise, contains the
// error from the last connection attempt.
error_code const& error,
// On success, the successfully connected endpoint.
// Otherwise, a default-constructed endpoint.
typename Protocol::endpoint const& endpoint
);
@endcode
Regardless of whether the asynchronous operation completes immediately or
not, the handler will not be invoked from within this function. Invocation
of the handler will be performed in a manner equivalent to using
`net::io_context::post()`.
*/
template<
class Protocol, class Executor,
class EndpointSequence,
class RangeConnectHandler
#if ! BOOST_BEAST_DOXYGEN
,class = typename std::enable_if<
net::is_endpoint_sequence<
EndpointSequence>::value>::type
#endif
>
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
void (error_code, typename Protocol::endpoint))
async_connect(
basic_timeout_stream<Protocol, Executor>& stream,
EndpointSequence const& endpoints,
RangeConnectHandler&& handler);
/** Asynchronously establishes a socket connection by trying each endpoint in a sequence, and terminating if a timeout occurs.
This function attempts to connect a socket to one of a sequence of
endpoints. It does this by repeated calls to the underlying socket's
@c async_connect member function, once for each endpoint in the sequence,
until a connection is successfully established or a timeout occurs.
@param stream The @ref beast::basic_timeout_stream to be connected. If the
underlying socket is already open, it will be closed.
@param endpoints A sequence of endpoints. This this object must meet
the requirements of <em>EndpointSequence</em>.
@param connect_condition A function object that is called prior to each
connection attempt. The signature of the function object must be:
@code
bool connect_condition(
error_code const& ec,
typename Protocol::endpoint const& next);
@endcode
The @c ec parameter contains the result from the most recent connect
operation. Before the first connection attempt, @c ec is always set to
indicate success. The @c next parameter is the next endpoint to be tried.
The function object should return true if the next endpoint should be tried,
and false if it should be skipped.
@param handler The handler to be called when the connect operation
completes. Ownership of the handler may be transferred. The function
signature of the handler must be:
@code
void handler(
// Result of operation. if the sequence is empty, set to
// net::error::not_found. Otherwise, contains the
// error from the last connection attempt.
error_code const& error,
// On success, the successfully connected endpoint.
// Otherwise, a default-constructed endpoint.
typename Protocol::endpoint const& endpoint
);
@endcode
Regardless of whether the asynchronous operation completes immediately or
not, the handler will not be invoked from within this function. Invocation
of the handler will be performed in a manner equivalent to using
`net::io_context::post()`.
@par Example
The following connect condition function object can be used to output
information about the individual connection attempts:
@code
struct my_connect_condition
{
bool operator()(
error_code const& ec,
net::ip::tcp::endpoint const& next)
{
if (ec)
std::cout << "Error: " << ec.message() << std::endl;
std::cout << "Trying: " << next << std::endl;
return true;
}
};
@endcode
*/
template<
class Protocol, class Executor,
class EndpointSequence,
class ConnectCondition,
class RangeConnectHandler
#if ! BOOST_BEAST_DOXYGEN
,class = typename std::enable_if<
net::is_endpoint_sequence<
EndpointSequence>::value>::type
#endif
>
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
void (error_code, typename Protocol::endpoint))
async_connect(
basic_timeout_stream<Protocol, Executor>& stream,
EndpointSequence const& endpoints,
ConnectCondition connect_condition,
RangeConnectHandler&& handler);
/** Asynchronously establishes a socket connection by trying each endpoint in a sequence, and terminating if a timeout occurs.
This function attempts to connect a socket to one of a sequence of
endpoints. It does this by repeated calls to the underlying socket's
@c async_connect member function, once for each endpoint in the sequence,
until a connection is successfully established or a timeout occurs.
@param stream The @ref beast::basic_timeout_stream to be connected. If the
underlying socket is already open, it will be closed.
@param begin An iterator pointing to the start of a sequence of endpoints.
@param end An iterator pointing to the end of a sequence of endpoints.
@param handler The handler to be called when the connect operation
completes. Ownership of the handler may be transferred. The function
signature of the handler must be:
@code
void handler(
// Result of operation. if the sequence is empty, set to
// net::error::not_found. Otherwise, contains the
// error from the last connection attempt.
error_code const& error,
// On success, an iterator denoting the successfully
// connected endpoint. Otherwise, the end iterator.
Iterator iterator
);
@endcode
Regardless of whether the asynchronous operation completes immediately or
not, the handler will not be invoked from within this function. Invocation
of the handler will be performed in a manner equivalent to using
`net::io_context::post()`.
*/
template<
class Protocol, class Executor,
class Iterator,
class IteratorConnectHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
void (error_code, Iterator))
async_connect(
basic_timeout_stream<Protocol, Executor>& stream,
Iterator begin, Iterator end,
IteratorConnectHandler&& handler);
/** Asynchronously establishes a socket connection by trying each endpoint in a sequence, and terminating if a timeout occurs.
This function attempts to connect a socket to one of a sequence of
endpoints. It does this by repeated calls to the underlying socket's
@c async_connect member function, once for each endpoint in the sequence,
until a connection is successfully established or a timeout occurs.
@param stream The @ref beast::basic_timeout_stream to be connected. If the
underlying socket is already open, it will be closed.
@param begin An iterator pointing to the start of a sequence of endpoints.
@param end An iterator pointing to the end of a sequence of endpoints.
@param connect_condition A function object that is called prior to each
connection attempt. The signature of the function object must be:
@code
bool connect_condition(
error_code const& ec,
Iterator next);
@endcode
@param handler The handler to be called when the connect operation
completes. Ownership of the handler may be transferred. The function
signature of the handler must be:
@code
void handler(
// Result of operation. if the sequence is empty, set to
// net::error::not_found. Otherwise, contains the
// error from the last connection attempt.
error_code const& error,
// On success, an iterator denoting the successfully
// connected endpoint. Otherwise, the end iterator.
Iterator iterator
);
@endcode
Regardless of whether the asynchronous operation completes immediately or
not, the handler will not be invoked from within this function. Invocation
of the handler will be performed in a manner equivalent to using
`net::io_context::post()`.
*/
template<
class Protocol, class Executor,
class Iterator,
class ConnectCondition,
class IteratorConnectHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
void (error_code, Iterator))
async_connect(
basic_timeout_stream<Protocol, Executor>& stream,
Iterator begin, Iterator end,
ConnectCondition connect_condition,
IteratorConnectHandler&& handler);
} // beast
} // boost
#include <boost/beast/core/impl/basic_timeout_stream.hpp>
#endif

View File

@@ -78,6 +78,25 @@ struct buffer_size_impl
} }
}; };
/** Return `true` if a buffer sequence is empty
This is sometimes faster than using @ref buffer_size
*/
template<class ConstBufferSequence>
bool
buffers_empty(ConstBufferSequence const& buffers)
{
auto it = net::buffer_sequence_begin(buffers);
auto end = net::buffer_sequence_end(buffers);
while(it != end)
{
if(net::const_buffer(*it).size() > 0)
return false;
++it;
}
return true;
}
} // detail } // detail
/** Return the total number of bytes in a buffer or buffer sequence /** Return the total number of bytes in a buffer or buffer sequence

View File

@@ -1,42 +0,0 @@
//
// Copyright (c) 2018 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_CORE_DETAIL_STRANDED_SOCKET_HPP
#define BOOST_BEAST_CORE_DETAIL_STRANDED_SOCKET_HPP
#include <boost/beast/core/detail/bind_default_executor.hpp>
#include <boost/asio/basic_stream_socket.hpp>
#include <utility>
namespace boost {
namespace beast {
namespace detail {
template<class Protocol>
class stranded_socket_base
{
protected:
net::basic_stream_socket<Protocol> socket_;
template<class... Args>
explicit
stranded_socket_base(Args&&... args)
: socket_(std::forward<Args>(args)...)
{
}
stranded_socket_base(stranded_socket_base&&) = default;
stranded_socket_base& operator=(stranded_socket_base&&) = delete;
};
} // detail
} // beast
} // boost
#endif

View File

@@ -7,18 +7,21 @@
// Official repository: https://github.com/boostorg/beast // Official repository: https://github.com/boostorg/beast
// //
#ifndef BOOST_BEAST_CORE_DETAIL_TIMEOUT_STREAM_BASE_HPP #ifndef BOOST_BEAST_CORE_DETAIL_STREAM_BASE_HPP
#define BOOST_BEAST_CORE_DETAIL_TIMEOUT_STREAM_BASE_HPP #define BOOST_BEAST_CORE_DETAIL_STREAM_BASE_HPP
#include <boost/asio/steady_timer.hpp>
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include <boost/core/exchange.hpp> #include <boost/core/exchange.hpp>
#include <chrono>
#include <cstdint>
namespace boost { namespace boost {
namespace beast { namespace beast {
namespace detail { namespace detail {
template<class, class, class> template<class, class, class>
class timeout_stream_connect_op; class basic_stream_connect_op;
struct any_endpoint struct any_endpoint
{ {
@@ -31,9 +34,27 @@ struct any_endpoint
} }
}; };
class timeout_stream_base struct stream_base
{ {
protected: using clock_type = std::chrono::steady_clock;
using time_point = typename
std::chrono::steady_clock::time_point;
using tick_type = std::uint64_t;
struct op_state
{
net::steady_timer timer; // for timing out
tick_type tick = 0; // counts waits
bool pending = false; // if op is pending
bool timeout = false; // if timed out
explicit
op_state(net::io_context& ioc)
: timer(ioc)
{
}
};
class pending_guard class pending_guard
{ {
bool& b_; bool& b_;
@@ -70,6 +91,14 @@ protected:
clear_ = false; clear_ = false;
} }
}; };
static constexpr time_point never()
{
return (time_point::max)();
}
static std::size_t constexpr no_limit =
(std::numeric_limits<std::size_t>::max)();
}; };
} // detail } // detail

View File

@@ -7,11 +7,13 @@
// Official repository: https://github.com/boostorg/beast // Official repository: https://github.com/boostorg/beast
// //
#ifndef BOOST_BEAST_CORE_IMPL_BASIC_TIMEOUT_STREAM_HPP #ifndef BOOST_BEAST_CORE_IMPL_BASIC_STREAM_HPP
#define BOOST_BEAST_CORE_IMPL_BASIC_TIMEOUT_STREAM_HPP #define BOOST_BEAST_CORE_IMPL_BASIC_STREAM_HPP
#include <boost/beast/core/async_op_base.hpp> #include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/buffers_prefix.hpp> #include <boost/beast/core/buffer_size.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/beast/websocket/teardown.hpp>
#include <boost/asio/bind_executor.hpp> #include <boost/asio/bind_executor.hpp>
#include <boost/asio/coroutine.hpp> #include <boost/asio/coroutine.hpp>
#include <boost/assert.hpp> #include <boost/assert.hpp>
@@ -23,8 +25,96 @@
namespace boost { namespace boost {
namespace beast { namespace beast {
//------------------------------------------------------------------------------
template<class Protocol, class Executor> template<class Protocol, class Executor>
struct basic_timeout_stream< template<class... Args>
basic_stream<Protocol, Executor>::
impl_type::
impl_type(
Executor const& ex_,
Args&&... args)
: boost::empty_value<Executor>(
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();
}
template<class Protocol, class Executor>
void
basic_stream<Protocol, Executor>::
impl_type::
reset()
{
// If assert goes off, it means that there are
// already read or write (or connect) operations
// outstanding, so there is nothing to apply
// the expiration time to!
//
BOOST_ASSERT(! read.pending || ! write.pending);
if(! read.pending)
BOOST_VERIFY(
read.timer.expires_at(never()) == 0);
if(! write.pending)
BOOST_VERIFY(
write.timer.expires_at(never()) == 0);
}
template<class Protocol, class Executor>
void
basic_stream<Protocol, Executor>::
impl_type::
close()
{
socket.close();
// have to let the read/write ops cancel the timer,
// otherwise we will get error::timeout on close when
// we actually want net::error::operation_aborted.
//
//read.timer.cancel();
//write.timer.cancel();
}
//------------------------------------------------------------------------------
template<class Protocol, class Executor>
struct basic_stream<
Protocol, Executor>::timeout_handler Protocol, Executor>::timeout_handler
{ {
op_state& state; op_state& state;
@@ -50,14 +140,8 @@ struct basic_timeout_stream<
return; return;
BOOST_ASSERT(tick == state.tick); BOOST_ASSERT(tick == state.tick);
// late completion
if(state.timeout)
{
state.timeout = false;
return;
}
// timeout // timeout
BOOST_ASSERT(! state.timeout);
sp->close(); sp->close();
state.timeout = true; state.timeout = true;
} }
@@ -75,7 +159,7 @@ struct basic_timeout_stream<
template<class Protocol, class Executor> template<class Protocol, class Executor>
template<bool isRead, class Buffers, class Handler> template<bool isRead, class Buffers, class Handler>
class basic_timeout_stream<Protocol, Executor>::async_op class basic_stream<Protocol, Executor>::async_op
: public async_op_base<Handler, Executor> : public async_op_base<Handler, Executor>
, public boost::asio::coroutine , public boost::asio::coroutine
{ {
@@ -119,7 +203,7 @@ class basic_timeout_stream<Protocol, Executor>::async_op
public: public:
template<class Handler_> template<class Handler_>
async_op( async_op(
basic_timeout_stream& s, basic_stream& s,
Buffers const& b, Buffers const& b,
Handler_&& h) Handler_&& h)
: async_op_base<Handler, Executor>( : async_op_base<Handler, Executor>(
@@ -138,44 +222,55 @@ public:
{ {
BOOST_ASIO_CORO_REENTER(*this) BOOST_ASIO_CORO_REENTER(*this)
{ {
// VFALCO TODO handle buffer size == 0 if(detail::buffers_empty(b_))
{
BOOST_ASIO_CORO_YIELD
async_perform(
std::integral_constant<bool, isRead>{});
if(state().timer.expiry() <= clock_type::now())
ec = beast::error::timeout;
goto upcall;
}
state().timer.async_wait( if(state().timer.expiry() != never())
net::bind_executor( state().timer.async_wait(
this->get_executor(), net::bind_executor(
timeout_handler{ this->get_executor(),
state(), timeout_handler{
impl_, state(),
state().tick impl_,
})); state().tick
}));
BOOST_ASIO_CORO_YIELD BOOST_ASIO_CORO_YIELD
async_perform( async_perform(
std::integral_constant<bool, isRead>{}); std::integral_constant<bool, isRead>{});
++state().tick;
// try cancelling timer if(state().timer.expiry() != never())
auto const n =
state().timer.cancel();
if(n == 0)
{ {
if(state().timeout) ++state().tick;
// try cancelling timer
auto const n =
state().timer.cancel();
if(n == 0)
{ {
// timeout handler invoked // timeout handler invoked?
ec = beast::error::timeout; if(state().timeout)
state().timeout = false; {
// yes, socket already closed
ec = beast::error::timeout;
state().timeout = false;
}
} }
else else
{ {
// timeout handler queued, stale BOOST_ASSERT(n == 1);
BOOST_ASSERT(! state().timeout);
} }
} }
else
{
BOOST_ASSERT(n == 1);
BOOST_ASSERT(! state().timeout);
}
upcall:
pg_.reset(); pg_.reset();
this->invoke(ec, bytes_transferred); this->invoke(ec, bytes_transferred);
} }
@@ -188,17 +283,17 @@ namespace detail {
template< template<
class Protocol, class Executor, class Handler> class Protocol, class Executor, class Handler>
class timeout_stream_connect_op class basic_stream_connect_op
: public async_op_base<Handler, Executor> : public async_op_base<Handler, Executor>
{ {
using stream_type = using stream_type =
beast::basic_timeout_stream<Protocol, Executor>; beast::basic_stream<Protocol, Executor>;
using timeout_handler = using timeout_handler =
typename stream_type::timeout_handler; typename stream_type::timeout_handler;
std::shared_ptr<typename basic_timeout_stream< std::shared_ptr<typename
Protocol, Executor>::impl_type> impl_; stream_type::impl_type> impl_;
typename stream_type::pending_guard pg0_; typename stream_type::pending_guard pg0_;
typename stream_type::pending_guard pg1_; typename stream_type::pending_guard pg1_;
@@ -209,64 +304,8 @@ class timeout_stream_connect_op
} }
public: public:
template<
class Endpoints, class Condition,
class Handler_>
timeout_stream_connect_op(
stream_type& s,
Endpoints const& eps,
Condition cond,
Handler_&& h)
: async_op_base<Handler, Executor>(
std::forward<Handler_>(h), s.get_executor())
, impl_(s.impl_)
, pg0_(impl_->read.pending)
, pg1_(impl_->write.pending)
{
// must come first
impl_->write.timer.async_wait(
net::bind_executor(
this->get_executor(),
timeout_handler{
state(),
impl_,
state().tick}));
net::async_connect(impl_->socket,
eps, cond, std::move(*this));
// *this is now moved-from
}
template<
class Iterator, class Condition,
class Handler_>
timeout_stream_connect_op(
stream_type& s,
Iterator begin, Iterator end,
Condition cond,
Handler_&& h)
: async_op_base<Handler, Executor>(
std::forward<Handler_>(h), s.get_executor())
, impl_(s.impl_)
, pg0_(impl_->read.pending)
, pg1_(impl_->write.pending)
{
// must come first
impl_->write.timer.async_wait(
net::bind_executor(
this->get_executor(),
timeout_handler{
state(),
impl_,
state().tick}));
net::async_connect(impl_->socket,
begin, end, cond, std::move(*this));
// *this is now moved-from
}
template<class Handler_> template<class Handler_>
timeout_stream_connect_op( basic_stream_connect_op(
stream_type& s, stream_type& s,
typename stream_type::endpoint_type ep, typename stream_type::endpoint_type ep,
Handler_&& h) Handler_&& h)
@@ -276,48 +315,104 @@ public:
, pg0_(impl_->read.pending) , pg0_(impl_->read.pending)
, pg1_(impl_->write.pending) , pg1_(impl_->write.pending)
{ {
// must come first if(state().timer.expiry() != stream_base::never())
impl_->write.timer.async_wait( impl_->write.timer.async_wait(
net::bind_executor( net::bind_executor(
this->get_executor(), this->get_executor(),
timeout_handler{ timeout_handler{
state(), state(),
impl_, impl_,
state().tick})); state().tick}));
impl_->socket.async_connect( impl_->socket.async_connect(
ep, std::move(*this)); ep, std::move(*this));
// *this is now moved-from // *this is now moved-from
} }
template<
class Endpoints, class Condition,
class Handler_>
basic_stream_connect_op(
stream_type& s,
Endpoints const& eps,
Condition const& cond,
Handler_&& h)
: async_op_base<Handler, Executor>(
std::forward<Handler_>(h), s.get_executor())
, impl_(s.impl_)
, pg0_(impl_->read.pending)
, pg1_(impl_->write.pending)
{
if(state().timer.expiry() != stream_base::never())
impl_->write.timer.async_wait(
net::bind_executor(
this->get_executor(),
timeout_handler{
state(),
impl_,
state().tick}));
net::async_connect(impl_->socket,
eps, cond, std::move(*this));
// *this is now moved-from
}
template<
class Iterator, class Condition,
class Handler_>
basic_stream_connect_op(
stream_type& s,
Iterator begin, Iterator end,
Condition const& cond,
Handler_&& h)
: async_op_base<Handler, Executor>(
std::forward<Handler_>(h), s.get_executor())
, impl_(s.impl_)
, pg0_(impl_->read.pending)
, pg1_(impl_->write.pending)
{
if(state().timer.expiry() != stream_base::never())
impl_->write.timer.async_wait(
net::bind_executor(
this->get_executor(),
timeout_handler{
state(),
impl_,
state().tick}));
net::async_connect(impl_->socket,
begin, end, cond, std::move(*this));
// *this is now moved-from
}
template<class... Args> template<class... Args>
void void
operator()(error_code ec, Args&&... args) operator()(error_code ec, Args&&... args)
{ {
++state().tick; if(state().timer.expiry() != stream_base::never())
// try cancelling timer
auto const n =
impl_->write.timer.cancel();
if(n == 0)
{ {
if(state().timeout) ++state().tick;
// try cancelling timer
auto const n =
impl_->write.timer.cancel();
if(n == 0)
{ {
// timeout handler invoked // timeout handler invoked?
ec = beast::error::timeout; if(state().timeout)
state().timeout = false; {
// yes, socket already closed
ec = beast::error::timeout;
state().timeout = false;
}
} }
else else
{ {
// timeout handler queued, stale BOOST_ASSERT(n == 1);
BOOST_ASSERT(! state().timeout);
} }
} }
else
{
BOOST_ASSERT(n == 1);
BOOST_ASSERT(! state().timeout);
}
pg0_.reset(); pg0_.reset();
pg1_.reset(); pg1_.reset();
this->invoke(ec, std::forward<Args>(args)...); this->invoke(ec, std::forward<Args>(args)...);
@@ -329,82 +424,8 @@ public:
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class Protocol, class Executor> template<class Protocol, class Executor>
template<class... Args> basic_stream<Protocol, Executor>::
basic_timeout_stream<Protocol, Executor>:: ~basic_stream()
impl_type::
impl_type(
Executor const& ex_,
Args&&... args)
: ex(ex_)
, read(ex_.context())
, write(ex_.context())
, socket(std::forward<Args>(args)...)
{
reset();
}
template<class Protocol, class Executor>
auto
basic_timeout_stream<Protocol, Executor>::
impl_type::
operator=(impl_type&& other) -> impl_type&
{
// VFALCO This hack is because legacy io_context::strand
// doesn't support operator=. Don't worry, constructing
// an executor cannot throw.
ex.~Executor();
::new(&ex) Executor(other.ex);
socket = std::move(other.socket);
read = std::move(other.read);
write = std::move(other.write);
return *this;
}
template<class Protocol, class Executor>
void
basic_timeout_stream<Protocol, Executor>::
impl_type::
reset()
{
// If assert goes off, it means that there are
// already read or write (or connect) operations
// outstanding, so there is nothing to apply
// the expiration time to!
//
BOOST_ASSERT(! read.pending || ! write.pending);
if(! read.pending)
BOOST_VERIFY(
read.timer.expires_at(never()) == 0);
if(! write.pending)
BOOST_VERIFY(
write.timer.expires_at(never()) == 0);
}
template<class Protocol, class Executor>
void
basic_timeout_stream<Protocol, Executor>::
impl_type::
close()
{
socket.close();
// have to let the read/write ops cancel the timer,
// otherwise we will get error::timeout on close when
// we actually want net::error::operation_aborted.
//
//read.timer.cancel();
//write.timer.cancel();
}
//------------------------------------------------------------------------------
template<class Protocol, class Executor>
basic_timeout_stream<Protocol, Executor>::
~basic_timeout_stream()
{ {
// the shared object can outlive *this, // the shared object can outlive *this,
// cancel any operations so the shared // cancel any operations so the shared
@@ -413,12 +434,12 @@ basic_timeout_stream<Protocol, Executor>::
} }
template<class Protocol, class Executor> template<class Protocol, class Executor>
template<class ExecutionContext, class> template<class ExecutionContext, class... Args, class>
basic_timeout_stream<Protocol, Executor>:: basic_stream<Protocol, Executor>::
basic_timeout_stream(ExecutionContext& ctx) basic_stream(ExecutionContext& ctx, Args&&... args)
: impl_(std::make_shared<impl_type>( : impl_(std::make_shared<impl_type>(
ctx.get_executor(), ctx.get_executor(),
ctx)) ctx, std::forward<Args>(args)...))
{ {
// Restriction is necessary until Asio fully supports P1322R0 // Restriction is necessary until Asio fully supports P1322R0
static_assert( static_assert(
@@ -427,67 +448,52 @@ basic_timeout_stream(ExecutionContext& ctx)
} }
template<class Protocol, class Executor> template<class Protocol, class Executor>
basic_timeout_stream<Protocol, Executor>:: template<class... Args>
basic_timeout_stream(executor_type const& ex) basic_stream<Protocol, Executor>::
basic_stream(
executor_type const& ex, Args&&... args)
: impl_(std::make_shared<impl_type>( : impl_(std::make_shared<impl_type>(
ex, ex.context())) ex,
ex.context(), std::forward<Args>(args)...))
{ {
} }
template<class Protocol, class Executor> template<class Protocol, class Executor>
basic_timeout_stream<Protocol, Executor>:: template<class OtherProtocol, class>
basic_timeout_stream( basic_stream<Protocol, Executor>::
net::basic_stream_socket<Protocol>&& socket) basic_stream(net::basic_stream_socket<OtherProtocol>&& socket)
: impl_(std::make_shared<impl_type>( : impl_(std::make_shared<impl_type>(
socket.get_executor(), std::move(socket))) std::move(socket),
std::is_constructible<Executor,
decltype(std::declval<net::basic_stream_socket<
Protocol>&>().get_executor())>{}))
{ {
} }
template<class Protocol, class Executor> template<class Protocol, class Executor>
basic_timeout_stream<Protocol, Executor>:: basic_stream<Protocol, Executor>::
basic_timeout_stream( basic_stream(basic_stream&& other)
executor_type const& ex,
net::basic_stream_socket<Protocol>&& socket)
: impl_(std::make_shared<impl_type>(
ex, std::move(socket)))
{
// Restriction is necessary until Asio fully supports P1322R0
if(ex.context().get_executor() != socket.get_executor())
throw std::invalid_argument(
"basic_timeout_stream currently requires ctx.get_executor() == socket.get_executor()");
}
template<class Protocol, class Executor>
basic_timeout_stream<Protocol, Executor>::
basic_timeout_stream(basic_timeout_stream&& other)
: impl_(std::make_shared<impl_type>( : impl_(std::make_shared<impl_type>(
std::move(*other.impl_))) std::move(*other.impl_)))
{ {
// Can't move while operations are pending! // VFALCO I'm not sure this implementation is correct...
BOOST_ASSERT(! impl_->read.pending);
BOOST_ASSERT(! impl_->write.pending);
}
template<class Protocol, class Executor>
auto
basic_timeout_stream<Protocol, Executor>::
operator=(basic_timeout_stream&& other) ->
basic_timeout_stream&
{
// Can't move while operations are pending!
BOOST_ASSERT(! impl_->read.pending);
BOOST_ASSERT(! impl_->write.pending);
BOOST_ASSERT(! other.impl_->read.pending);
BOOST_ASSERT(! other.impl_->write.pending);
*impl_ = std::move(*other.impl_);
return *this;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class Protocol, class Executor>
auto
basic_stream<Protocol, Executor>::
release_socket() ->
socket_type
{
this->cancel();
return std::move(impl_->socket);
}
template<class Protocol, class Executor> template<class Protocol, class Executor>
void void
basic_timeout_stream<Protocol, Executor>:: basic_stream<Protocol, Executor>::
expires_after(std::chrono::nanoseconds expiry_time) expires_after(std::chrono::nanoseconds expiry_time)
{ {
// If assert goes off, it means that there are // If assert goes off, it means that there are
@@ -512,7 +518,7 @@ expires_after(std::chrono::nanoseconds expiry_time)
template<class Protocol, class Executor> template<class Protocol, class Executor>
void void
basic_timeout_stream<Protocol, Executor>:: basic_stream<Protocol, Executor>::
expires_at( expires_at(
net::steady_timer::time_point expiry_time) net::steady_timer::time_point expiry_time)
{ {
@@ -538,7 +544,7 @@ expires_at(
template<class Protocol, class Executor> template<class Protocol, class Executor>
void void
basic_timeout_stream<Protocol, Executor>:: basic_stream<Protocol, Executor>::
expires_never() expires_never()
{ {
impl_->reset(); impl_->reset();
@@ -546,7 +552,7 @@ expires_never()
template<class Protocol, class Executor> template<class Protocol, class Executor>
void void
basic_timeout_stream<Protocol, Executor>:: basic_stream<Protocol, Executor>::
cancel() cancel()
{ {
error_code ec; error_code ec;
@@ -555,7 +561,7 @@ cancel()
template<class Protocol, class Executor> template<class Protocol, class Executor>
void void
basic_timeout_stream<Protocol, Executor>:: basic_stream<Protocol, Executor>::
close() close()
{ {
impl_->close(); impl_->close();
@@ -565,14 +571,14 @@ template<class Protocol, class Executor>
template<class ConnectHandler> template<class ConnectHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler,
void(error_code)) void(error_code))
basic_timeout_stream<Protocol, Executor>:: basic_stream<Protocol, Executor>::
async_connect( async_connect(
endpoint_type ep, endpoint_type const& ep,
ConnectHandler&& handler) ConnectHandler&& handler)
{ {
BOOST_BEAST_HANDLER_INIT( BOOST_BEAST_HANDLER_INIT(
ConnectHandler, void(error_code)); ConnectHandler, void(error_code));
detail::timeout_stream_connect_op< detail::basic_stream_connect_op<
Protocol, Executor, BOOST_ASIO_HANDLER_TYPE( Protocol, Executor, BOOST_ASIO_HANDLER_TYPE(
ConnectHandler, void(error_code))>(*this, ConnectHandler, void(error_code))>(*this,
ep, std::forward<ConnectHandler>(handler)); ep, std::forward<ConnectHandler>(handler));
@@ -583,7 +589,7 @@ template<class Protocol, class Executor>
template<class MutableBufferSequence, class ReadHandler> template<class MutableBufferSequence, class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
void(error_code, std::size_t)) void(error_code, std::size_t))
basic_timeout_stream<Protocol, Executor>:: basic_stream<Protocol, Executor>::
async_read_some( async_read_some(
MutableBufferSequence const& buffers, MutableBufferSequence const& buffers,
ReadHandler&& handler) ReadHandler&& handler)
@@ -603,7 +609,7 @@ template<class Protocol, class Executor>
template<class ConstBufferSequence, class WriteHandler> template<class ConstBufferSequence, class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
void(error_code, std::size_t)) void(error_code, std::size_t))
basic_timeout_stream<Protocol, Executor>:: basic_stream<Protocol, Executor>::
async_write_some( async_write_some(
ConstBufferSequence const& buffers, ConstBufferSequence const& buffers,
WriteHandler&& handler) WriteHandler&& handler)
@@ -629,13 +635,13 @@ template<
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler, BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
void(error_code, typename Protocol::endpoint)) void(error_code, typename Protocol::endpoint))
async_connect( async_connect(
basic_timeout_stream<Protocol, Executor>& stream, basic_stream<Protocol, Executor>& stream,
EndpointSequence const& endpoints, EndpointSequence const& endpoints,
RangeConnectHandler&& handler) RangeConnectHandler&& handler)
{ {
BOOST_BEAST_HANDLER_INIT(RangeConnectHandler, BOOST_BEAST_HANDLER_INIT(RangeConnectHandler,
void(error_code, typename Protocol::endpoint)); void(error_code, typename Protocol::endpoint));
detail::timeout_stream_connect_op<Protocol, Executor, detail::basic_stream_connect_op<Protocol, Executor,
BOOST_ASIO_HANDLER_TYPE(RangeConnectHandler, BOOST_ASIO_HANDLER_TYPE(RangeConnectHandler,
void(error_code, typename Protocol::endpoint))>( void(error_code, typename Protocol::endpoint))>(
stream, endpoints, detail::any_endpoint{}, stream, endpoints, detail::any_endpoint{},
@@ -652,14 +658,14 @@ template<
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler, BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
void (error_code, typename Protocol::endpoint)) void (error_code, typename Protocol::endpoint))
async_connect( async_connect(
basic_timeout_stream<Protocol, Executor>& stream, basic_stream<Protocol, Executor>& stream,
EndpointSequence const& endpoints, EndpointSequence const& endpoints,
ConnectCondition connect_condition, ConnectCondition connect_condition,
RangeConnectHandler&& handler) RangeConnectHandler&& handler)
{ {
BOOST_BEAST_HANDLER_INIT(RangeConnectHandler, BOOST_BEAST_HANDLER_INIT(RangeConnectHandler,
void(error_code, typename Protocol::endpoint)); void(error_code, typename Protocol::endpoint));
detail::timeout_stream_connect_op<Protocol, Executor, detail::basic_stream_connect_op<Protocol, Executor,
BOOST_ASIO_HANDLER_TYPE(RangeConnectHandler, BOOST_ASIO_HANDLER_TYPE(RangeConnectHandler,
void(error_code, typename Protocol::endpoint))>( void(error_code, typename Protocol::endpoint))>(
stream, endpoints, connect_condition, stream, endpoints, connect_condition,
@@ -674,13 +680,13 @@ template<
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
void (error_code, Iterator)) void (error_code, Iterator))
async_connect( async_connect(
basic_timeout_stream<Protocol, Executor>& stream, basic_stream<Protocol, Executor>& stream,
Iterator begin, Iterator end, Iterator begin, Iterator end,
IteratorConnectHandler&& handler) IteratorConnectHandler&& handler)
{ {
BOOST_BEAST_HANDLER_INIT(IteratorConnectHandler, BOOST_BEAST_HANDLER_INIT(IteratorConnectHandler,
void(error_code, Iterator)); void(error_code, Iterator));
detail::timeout_stream_connect_op<Protocol, Executor, detail::basic_stream_connect_op<Protocol, Executor,
BOOST_ASIO_HANDLER_TYPE(IteratorConnectHandler, BOOST_ASIO_HANDLER_TYPE(IteratorConnectHandler,
void(error_code, Iterator))>( void(error_code, Iterator))>(
stream, begin, end, detail::any_endpoint{}, stream, begin, end, detail::any_endpoint{},
@@ -696,14 +702,14 @@ template<
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
void (error_code, Iterator)) void (error_code, Iterator))
async_connect( async_connect(
basic_timeout_stream<Protocol, Executor>& stream, basic_stream<Protocol, Executor>& stream,
Iterator begin, Iterator end, Iterator begin, Iterator end,
ConnectCondition connect_condition, ConnectCondition connect_condition,
IteratorConnectHandler&& handler) IteratorConnectHandler&& handler)
{ {
BOOST_BEAST_HANDLER_INIT(IteratorConnectHandler, BOOST_BEAST_HANDLER_INIT(IteratorConnectHandler,
void(error_code, Iterator)); void(error_code, Iterator));
detail::timeout_stream_connect_op<Protocol, Executor, detail::basic_stream_connect_op<Protocol, Executor,
BOOST_ASIO_HANDLER_TYPE(IteratorConnectHandler, BOOST_ASIO_HANDLER_TYPE(IteratorConnectHandler,
void(error_code, Iterator))>( void(error_code, Iterator))>(
stream, begin, end, connect_condition, stream, begin, end, connect_condition,
@@ -711,6 +717,46 @@ async_connect(
return init.result.get(); return init.result.get();
} }
//------------------------------------------------------------------------------
#if ! BOOST_BEAST_DOXYGEN
template<class Protocol, class Executor>
void
beast_close_socket(
basic_stream<Protocol, Executor>& stream)
{
error_code ec;
stream.socket().close(ec);
}
template<class Protocol, class Executor>
void
teardown(
websocket::role_type role,
basic_stream<Protocol, Executor>& stream,
error_code& ec)
{
using beast::websocket::teardown;
teardown(role, stream.socket(), ec);
}
template<
class Protocol, class Executor,
class TeardownHandler>
void
async_teardown(
websocket::role_type role,
basic_stream<Protocol, Executor>& stream,
TeardownHandler&& handler)
{
using beast::websocket::async_teardown;
async_teardown(role, stream.socket(),
std::forward<TeardownHandler>(handler));
}
#endif
} // beast } // beast
} // boost } // boost

File diff suppressed because it is too large Load Diff

View File

@@ -7,22 +7,25 @@
// Official repository: https://github.com/boostorg/beast // Official repository: https://github.com/boostorg/beast
// //
#ifndef BOOST_BEAST_CORE_TIMEOUT_STREAM_HPP #ifndef BOOST_BEAST_CORE_TCP_STREAM_HPP
#define BOOST_BEAST_CORE_TIMEOUT_STREAM_HPP #define BOOST_BEAST_CORE_TCP_STREAM_HPP
#include <boost/beast/core/detail/config.hpp> #include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/basic_timeout_stream.hpp> #include <boost/beast/core/basic_stream.hpp>
#include <boost/beast/core/error.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 which supports timeouts and rate limits /** A TCP/IP stream socket with timeouts, rate limits, and executor.
@tparam Executor The type of executor to use for all completion
handlers which do not already have an associated executor.
@see basic_stream
*/ */
using timeout_stream = basic_timeout_stream< template<class Executor>
net::ip::tcp, using tcp_stream = basic_stream<net::ip::tcp, Executor>;
net::io_context::executor_type>;
} // beast } // beast
} // boost } // boost

View File

@@ -599,7 +599,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -646,7 +646,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -700,7 +700,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -764,7 +764,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -835,7 +835,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -884,7 +884,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -941,7 +941,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -1008,7 +1008,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -1428,7 +1428,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -1467,7 +1467,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -1518,7 +1518,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -1557,7 +1557,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -1610,7 +1610,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -1659,7 +1659,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -1722,7 +1722,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -1773,7 +1773,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -1836,7 +1836,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -1874,7 +1874,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -1925,7 +1925,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -1964,7 +1964,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -2424,7 +2424,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `write_some` function. in terms of calls to the next layer's `write_some` function.
After beginning the closing handshake, the program should not write After beginning the closing handshake, the program should not write
@@ -2459,7 +2459,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `write_some` function. in terms of calls to the next layer's `write_some` function.
After beginning the closing handshake, the program should not write After beginning the closing handshake, the program should not write
@@ -2551,7 +2551,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `write_some` function. in terms of calls to the next layer's `write_some` function.
@param payload The payload of the ping message, which may be empty. @param payload The payload of the ping message, which may be empty.
@@ -2574,7 +2574,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `write_some` function. in terms of calls to the next layer's `write_some` function.
@param payload The payload of the ping message, which may be empty. @param payload The payload of the ping message, which may be empty.
@@ -2640,7 +2640,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `write_some` function. in terms of calls to the next layer's `write_some` function.
WebSocket allows pong frames to be sent at any time, without first WebSocket allows pong frames to be sent at any time, without first
@@ -2667,7 +2667,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `write_some` function. in terms of calls to the next layer's `write_some` function.
WebSocket allows pong frames to be sent at any time, without first WebSocket allows pong frames to be sent at any time, without first
@@ -2747,7 +2747,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -2790,7 +2790,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -2899,7 +2899,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -2950,7 +2950,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -3032,6 +3032,10 @@ public:
@param buffer A dynamic buffer to append message data to. @param buffer A dynamic buffer to append message data to.
@param limit An upper limit on the number of bytes this function
will append into the buffer. If this value is zero, then a reasonable
size will be chosen automatically.
@param handler Invoked when the operation completes. Ownership @param handler Invoked when the operation completes. Ownership
of the handler will be transferred by move-construction as needed. of the handler will be transferred by move-construction as needed.
The equivalent function signature of the handler must be: The equivalent function signature of the handler must be:
@@ -3069,7 +3073,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -3096,10 +3100,6 @@ public:
The previous contents of the buffers will be overwritten, starting The previous contents of the buffers will be overwritten, starting
from the beginning. from the beginning.
@param limit An upper limit on the number of bytes this function
will append into the buffer. If this value is zero, then a reasonable
size will be chosen automatically.
@throws system_error Thrown on failure. @throws system_error Thrown on failure.
*/ */
template<class MutableBufferSequence> template<class MutableBufferSequence>
@@ -3120,7 +3120,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` and `write_some` in terms of calls to the next layer's `read_some` and `write_some`
functions. functions.
@@ -3147,10 +3147,6 @@ public:
The previous contents of the buffers will be overwritten, starting The previous contents of the buffers will be overwritten, starting
from the beginning. from the beginning.
@param limit An upper limit on the number of bytes this function
will append into the buffer. If this value is zero, then a reasonable
size will be chosen automatically.
@param ec Set to indicate what error occurred, if any. @param ec Set to indicate what error occurred, if any.
*/ */
template<class MutableBufferSequence> template<class MutableBufferSequence>
@@ -3246,7 +3242,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `write_some` function. in terms of calls to the next layer's `write_some` function.
The current setting of the @ref binary option controls The current setting of the @ref binary option controls
@@ -3275,7 +3271,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `write_some` function. in terms of calls to the next layer's `write_some` function.
The current setting of the @ref binary option controls The current setting of the @ref binary option controls
@@ -3359,7 +3355,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `write_some` function. in terms of calls to the next layer's `write_some` function.
If this is the beginning of a new message, the message opcode If this is the beginning of a new message, the message opcode
@@ -3389,7 +3385,7 @@ public:
@li An error occurs. @li An error occurs.
The algorithm, known as a <em>composed operation</em> is implemented The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `write_some` function. in terms of calls to the next layer's `write_some` function.
If this is the beginning of a new message, the message opcode If this is the beginning of a new message, the message opcode
@@ -3401,6 +3397,8 @@ public:
@param buffers The buffers containing the message part to send. @param buffers The buffers containing the message part to send.
@param ec Set to indicate what error occurred, if any.
@return The number of bytes sent from the buffers. @return The number of bytes sent from the buffers.
@return The number of bytes consumed in the input buffers. @return The number of bytes consumed in the input buffers.

View File

@@ -30,7 +30,7 @@ add_executable (tests-beast-core
_detail_variant.cpp _detail_variant.cpp
_detail_varint.cpp _detail_varint.cpp
async_op_base.cpp async_op_base.cpp
basic_timeout_stream.cpp basic_stream.cpp
bind_handler.cpp bind_handler.cpp
buffer_size.cpp buffer_size.cpp
buffer_traits.cpp buffer_traits.cpp
@@ -61,11 +61,10 @@ add_executable (tests-beast-core
span.cpp span.cpp
static_buffer.cpp static_buffer.cpp
static_string.cpp static_string.cpp
stranded_socket.cpp
stream_traits.cpp stream_traits.cpp
string.cpp string.cpp
string_param.cpp string_param.cpp
timeout_stream.cpp tcp_stream.cpp
) )
set_property(TARGET tests-beast-core PROPERTY FOLDER "tests") set_property(TARGET tests-beast-core PROPERTY FOLDER "tests")

View File

@@ -18,7 +18,7 @@ local SOURCES =
_detail_variant.cpp _detail_variant.cpp
_detail_varint.cpp _detail_varint.cpp
async_op_base.cpp async_op_base.cpp
basic_timeout_stream.cpp basic_stream.cpp
bind_handler.cpp bind_handler.cpp
buffer_size.cpp buffer_size.cpp
buffer_traits.cpp buffer_traits.cpp
@@ -49,11 +49,10 @@ local SOURCES =
span.cpp span.cpp
static_buffer.cpp static_buffer.cpp
static_string.cpp static_string.cpp
stranded_socket.cpp
stream_traits.cpp stream_traits.cpp
string.cpp string.cpp
string_param.cpp string_param.cpp
timeout_stream.cpp tcp_stream.cpp
; ;
local RUN_TESTS ; local RUN_TESTS ;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,519 +0,0 @@
//
// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
// Test that header file is self-contained.
#include <boost/beast/core/stranded_socket.hpp>
#include "stream_tests.hpp"
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <memory>
namespace boost {
namespace beast {
namespace {
template<class Executor = net::io_context::executor_type>
class test_executor
{
public:
// VFALCO These need to be atomic or something
struct info
{
int dispatch = 0;
int post = 0;
int defer = 0;
int work = 0;
int total = 0;
};
private:
struct state
{
Executor ex;
info info_;
state(Executor const& ex_)
: ex(ex_)
{
}
};
std::shared_ptr<state> sp_;
public:
test_executor(test_executor const&) = default;
test_executor& operator=(test_executor const&) = default;
explicit
test_executor(Executor const& ex)
: sp_(std::make_shared<state>(ex))
{
}
decltype(sp_->ex.context())
context() const noexcept
{
return sp_->ex.context();
}
info&
operator*() noexcept
{
return sp_->info_;
}
info*
operator->() noexcept
{
return &sp_->info_;
}
void
on_work_started() const noexcept
{
++sp_->info_.work;
}
void
on_work_finished() const noexcept
{
}
template<class F, class A>
void
dispatch(F&& f, A const& a)
{
++sp_->info_.dispatch;
++sp_->info_.total;
sp_->ex.dispatch(
std::forward<F>(f), a);
}
template<class F, class A>
void
post(F&& f, A const& a)
{
++sp_->info_.post;
++sp_->info_.total;
sp_->ex.post(
std::forward<F>(f), a);
}
template<class F, class A>
void
defer(F&& f, A const& a)
{
++sp_->info_.defer;
++sp_->info_.total;
sp_->ex.defer(
std::forward<F>(f), a);
}
};
struct test_handler
{
int& flags;
void
operator()()
{
flags |= 1;
}
template<class F>
friend
void
asio_handler_invoke(F&& f, test_handler* p)
{
p->flags |= 2;
std::move(f)();
}
};
struct test_acceptor
{
net::io_context ioc;
net::ip::tcp::acceptor a;
net::ip::tcp::endpoint ep;
test_acceptor()
: a(ioc)
, ep(net::ip::make_address_v4("127.0.0.1"), 0)
{
a.open(ep.protocol());
a.set_option(
net::socket_base::reuse_address(true));
a.bind(ep);
a.listen(1);
ep = a.local_endpoint();
a.async_accept(
[](error_code, net::ip::tcp::socket)
{
});
}
};
} // (anon)
//------------------------------------------------------------------------------
class stranded_socket_test
: public beast::unit_test::suite
{
public:
using tcp = net::ip::tcp;
using strand = net::io_context::strand;
using executor = net::io_context::executor_type;
void
testStream()
{
net::io_context ioc;
// default Executor
{
stranded_socket<tcp> s1{strand(ioc)};
stranded_socket<tcp> s2{strand{ioc}};
//stranded_socket<tcp> s3{strand{ioc}}; // ambiguous parse
}
// explicit Executor
{
auto ex = ioc.get_executor();
stranded_socket<tcp, executor> s1(ioc);
stranded_socket<tcp, executor> s2(ex);
stranded_socket<tcp, executor> s3(ioc, tcp::v4());
stranded_socket<tcp, executor> s4(std::move(s1));
s2.socket() = tcp::socket(ioc);
BEAST_EXPECT(s1.get_executor() == ex);
BEAST_EXPECT(s2.get_executor() == ex);
BEAST_EXPECT(s3.get_executor() == ex);
BEAST_EXPECT(s4.get_executor() == ex);
BEAST_EXPECT((! static_cast<
stranded_socket<tcp, executor> const&>(
s2).socket().is_open()));
}
{
auto ex = strand{ioc};
stranded_socket<tcp, strand> s1(ex);
stranded_socket<tcp, strand> s2(ex, tcp::v4());
stranded_socket<tcp, strand> s3(std::move(s1));
s2.socket() = tcp::socket(ioc);
BEAST_EXPECT(s1.get_executor() == ex);
BEAST_EXPECT(s2.get_executor() == ex);
BEAST_EXPECT(s3.get_executor() == ex);
BEAST_EXPECT((! static_cast<
stranded_socket<tcp, strand> const&>(
s2).socket().is_open()));
}
{
test_sync_stream<stranded_socket<tcp, executor>>();
test_async_stream<stranded_socket<tcp, executor>>();
test_sync_stream<stranded_socket<tcp, strand>>();
test_async_stream<stranded_socket<tcp, strand>>();
}
}
void
testMembers()
{
net::io_context ioc;
// connect (member)
auto const cond =
[](error_code, tcp::endpoint)
{
return true;
};
{
stranded_socket<tcp, executor> s(ioc);
error_code ec;
test_acceptor a;
try
{
s.connect(a.ep);
BEAST_PASS();
}
catch(std::exception const&)
{
BEAST_FAIL();
}
}
{
stranded_socket<tcp, executor> s(ioc);
error_code ec;
test_acceptor a;
s.connect(a.ep, ec);
BEAST_EXPECT(! ec);
}
// connect
{
test_acceptor a;
std::array<tcp::endpoint, 1> epa;
epa[0] = a.ep;
stranded_socket<tcp, executor> s(ioc);
error_code ec;
connect(s, epa);
connect(s, epa, ec);
}
{
test_acceptor a;
std::array<tcp::endpoint, 1> epa;
epa[0] = a.ep;
stranded_socket<tcp, executor> s(ioc);
error_code ec;
connect(s, epa, cond);
connect(s, epa, cond, ec);
}
{
test_acceptor a;
std::array<tcp::endpoint, 1> epa;
epa[0] = a.ep;
stranded_socket<tcp, executor> s(ioc);
error_code ec;
connect(s, epa.begin(), epa.end());
connect(s, epa.begin(), epa.end(), ec);
}
{
test_acceptor a;
std::array<tcp::endpoint, 1> epa;
epa[0] = a.ep;
stranded_socket<tcp, executor> s(ioc);
error_code ec;
connect(s, epa.begin(), epa.end(), cond);
connect(s, epa.begin(), epa.end(), cond, ec);
}
// async_connect
{
stranded_socket<tcp, executor> s(ioc);
test_acceptor a;
error_code ec;
s.async_connect(a. ep,
[](error_code ec)
{
BEAST_EXPECT(! ec);
});
ioc.run();
ioc.restart();
}
{
std::array<tcp::endpoint, 1> epa;
epa[0] = tcp::endpoint(
net::ip::make_address_v4("127.0.0.1"), 0);
stranded_socket<tcp, executor> s(ioc);
async_connect(s, epa,
[](error_code, tcp::endpoint)
{
});
}
{
std::array<tcp::endpoint, 1> epa;
epa[0] = tcp::endpoint(
net::ip::make_address_v4("127.0.0.1"), 0);
stranded_socket<tcp, executor> s(ioc);
async_connect(s, epa, cond,
[](error_code, tcp::endpoint)
{
});
}
{
std::array<tcp::endpoint, 1> epa;
epa[0] = tcp::endpoint(
net::ip::make_address_v4("127.0.0.1"), 0);
using iter_type = decltype(epa)::const_iterator;
stranded_socket<tcp, executor> s(ioc);
async_connect(s, epa.begin(), epa.end(),
[](error_code, iter_type)
{
});
}
{
std::array<tcp::endpoint, 1> epa;
epa[0] = tcp::endpoint(
net::ip::make_address_v4("127.0.0.1"), 0);
using iter_type = decltype(epa)::const_iterator;
stranded_socket<tcp, executor> s(ioc);
async_connect(s, epa.begin(), epa.end(), cond,
[](error_code, iter_type)
{
});
}
// read/write
{
error_code ec;
stranded_socket<tcp, executor> s(ioc, tcp::v4());
BEAST_EXPECT(s.read_some(net::mutable_buffer{}) == 0);
BEAST_EXPECT(s.read_some(net::mutable_buffer{}, ec) == 0);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(s.write_some(net::const_buffer{}) == 0);
BEAST_EXPECT(s.write_some(net::const_buffer{}, ec) == 0);
BEAST_EXPECTS(! ec, ec.message());
bool invoked;
invoked = false;
s.async_read_some(net::mutable_buffer{},
[&](error_code ec, std::size_t)
{
invoked = true;
BEAST_EXPECTS(! ec, ec.message());
});
ioc.run();
ioc.restart();
BEAST_EXPECT(invoked);
invoked = false;
s.async_write_some(net::const_buffer{},
[&](error_code ec, std::size_t)
{
invoked = true;
BEAST_EXPECTS(! ec, ec.message());
});
ioc.run();
ioc.restart();
BEAST_EXPECT(invoked);
}
// stranded
{
error_code ec;
stranded_socket<tcp, strand> s(strand(ioc), tcp::v4());
bool invoked;
invoked = false;
s.async_read_some(net::mutable_buffer{},
[&](error_code ec, std::size_t)
{
invoked = true;
BEAST_EXPECTS(! ec, ec.message());
});
ioc.run();
ioc.restart();
BEAST_EXPECT(invoked);
invoked = false;
s.async_write_some(net::const_buffer{},
[&](error_code ec, std::size_t)
{
invoked = true;
BEAST_EXPECTS(! ec, ec.message());
});
ioc.run();
ioc.restart();
BEAST_EXPECT(invoked);
}
// test_executor
{
error_code ec;
stranded_socket<tcp, test_executor<>> s(
test_executor<>(ioc.get_executor()), tcp::v4());
bool invoked;
invoked = false;
s.async_read_some(net::mutable_buffer{},
[&](error_code ec, std::size_t)
{
invoked = true;
BEAST_EXPECTS(! ec, ec.message());
});
ioc.run();
ioc.restart();
BEAST_EXPECT(invoked);
BEAST_EXPECT(s.get_executor()->total > 0);
s.get_executor()->total = 0;
invoked = false;
s.async_write_some(net::const_buffer{},
[&](error_code ec, std::size_t)
{
invoked = true;
BEAST_EXPECTS(! ec, ec.message());
});
ioc.run();
ioc.restart();
BEAST_EXPECT(invoked);
BEAST_EXPECT(s.get_executor()->total > 0);
s.get_executor()->total = 0;
}
// bind_default_executor::asio_handler_invoke
#if 0
// VFALCO This test fails, because it is unclear how
// asio_handler_invoke interacts with the wrapper.
// Need to ask Chris Kohlhoff about this one.
{
int flags = 0;
net::post(
ioc,
detail::bind_default_executor(
strand(ioc),
test_handler{flags}));
ioc.run();
ioc.restart();
BEAST_EXPECT(flags == 3);
}
#endif
}
//--------------------------------------------------------------------------
void
testJavadocs()
{
}
//--------------------------------------------------------------------------
void
run()
{
testStream();
testJavadocs();
testMembers();
}
};
BEAST_DEFINE_TESTSUITE(beast,core,stranded_socket);
} // beast
} // boost

View File

@@ -8,4 +8,4 @@
// //
// Test that header file is self-contained. // Test that header file is self-contained.
#include <boost/beast/core/timeout_stream.hpp> #include <boost/beast/core/tcp_stream.hpp>