mirror of
https://github.com/boostorg/beast.git
synced 2025-07-29 20:37:31 +02:00
Add basic_timeout_stream:
This stream wrapper replaces basic_stream_socket and provides just the timeout functionality. * basic_stream_socket is removed
This commit is contained in:
@ -1,3 +1,9 @@
|
||||
Version 204
|
||||
|
||||
* Add basic_timeout_stream
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Version 203
|
||||
|
||||
* Update networking refresher doc
|
||||
|
@ -41,6 +41,7 @@
|
||||
|
||||
[def __asio_handler_allocate__ [@boost:/doc/html/boost_asio/reference/asio_handler_allocate.html `asio_handler_allocate`]]
|
||||
[def __asio_handler_invoke__ [@boost:/doc/html/boost_asio/reference/asio_handler_invoke.html `asio_handler_invoke`]]
|
||||
[def __basic_stream_socket__ [@boost:/doc/html/boost_asio/reference/basic_stream_socket.html `basic_stream_socket`]]
|
||||
[def __const_buffer__ [@boost:/doc/html/boost_asio/reference/const_buffer.html `const_buffer`]]
|
||||
[def __deduced__ [@boost:/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.automatic_deduction_of_initiating_function_return_type ['DEDUCED]]]
|
||||
[def __executor_work_guard__ [@boost:/doc/html/boost_asio/reference/executor_work_guard.html `net::executor_work_guard`]]
|
||||
@ -64,6 +65,7 @@
|
||||
[def __CompletionHandler__ [@boost:/doc/html/boost_asio/reference/CompletionHandler.html ['CompletionHandler]]]
|
||||
[def __CompletionCondition__ [@boost:/doc/html/boost_asio/reference/CompletionCondition.html ['CompletionCondition]]]
|
||||
[def __ConnectCondition__ [@boost:/doc/html/boost_asio/reference/ConnectCondition.html ['ConnectCondition]]]
|
||||
[def __ConnectHandler__ [@boost:/doc/html/boost_asio/reference/ConnectHandler.html ['ConnectHandler]]]
|
||||
[def __ConstBufferSequence__ [@boost:/doc/html/boost_asio/reference/ConstBufferSequence.html ['ConstBufferSequence]]]
|
||||
[def __EndpointSequence__ [@boost:/doc/html/boost_asio/reference/EndpointSequence.html ['EndpointSequence]]]
|
||||
[def __Executor__ [@boost:/doc/html/boost_asio/reference/Executor1.html ['Executor]]]
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
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
|
||||
|
||||
-->
|
||||
@ -180,7 +180,7 @@
|
||||
<member><link linkend="beast.ref.boost__beast__async_op_base">async_op_base</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__basic_flat_buffer">basic_flat_buffer</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__basic_multi_buffer">basic_multi_buffer</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__basic_stream_socket">basic_stream_socket</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__basic_timeout_stream">basic_timeout_stream</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__buffered_read_stream">buffered_read_stream</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__buffers_adaptor">buffers_adaptor</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__buffers_cat_view">buffers_cat_view</link></member>
|
||||
@ -209,9 +209,9 @@
|
||||
<member><link linkend="beast.ref.boost__beast__static_buffer_base">static_buffer_base</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></member>
|
||||
<member><link linkend="beast.ref.boost__beast__stream_socket">stream_socket</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__timeout_stream">timeout_stream</link></member>
|
||||
</simplelist>
|
||||
<bridgehead renderas="sect3">Constants</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
|
@ -27,6 +27,9 @@
|
||||
<xsl:when test="type = 'class ConnectCondition'">
|
||||
<xsl:text>class __ConnectCondition__</xsl:text>
|
||||
</xsl:when>
|
||||
<xsl:when test="type = 'class ConnectHandler'">
|
||||
<xsl:text>class __ConnectHandler__</xsl:text>
|
||||
</xsl:when>
|
||||
<xsl:when test="declname = 'ConstBufferSequence' or type = 'class ConstBufferSequence'">
|
||||
<xsl:text>class __ConstBufferSequence__</xsl:text>
|
||||
</xsl:when>
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
|
||||
#include <boost/beast/core/basic_stream_socket.hpp>
|
||||
#include <boost/beast/core/basic_timeout_stream.hpp>
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/buffer_traits.hpp>
|
||||
#include <boost/beast/core/buffered_read_stream.hpp>
|
||||
@ -38,9 +38,9 @@
|
||||
#include <boost/beast/core/span.hpp>
|
||||
#include <boost/beast/core/static_buffer.hpp>
|
||||
#include <boost/beast/core/static_string.hpp>
|
||||
#include <boost/beast/core/stream_socket.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/core/string_param.hpp>
|
||||
#include <boost/beast/core/timeout_stream.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
844
include/boost/beast/core/basic_timeout_stream.hpp
Normal file
844
include/boost/beast/core/basic_timeout_stream.hpp
Normal file
@ -0,0 +1,844 @@
|
||||
//
|
||||
// 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/beast/core/type_traits.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)();
|
||||
|
||||
struct op_state
|
||||
{
|
||||
net::steady_timer timer; // for timing out
|
||||
bool pending = false; // if op is pending
|
||||
bool closed = 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
|
@ -7,8 +7,8 @@
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_DETAIL_STREAM_SOCKET_BASE_HPP
|
||||
#define BOOST_BEAST_CORE_DETAIL_STREAM_SOCKET_BASE_HPP
|
||||
#ifndef BOOST_BEAST_CORE_DETAIL_TIMEOUT_STREAM_BASE_HPP
|
||||
#define BOOST_BEAST_CORE_DETAIL_TIMEOUT_STREAM_BASE_HPP
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/exchange.hpp>
|
||||
@ -18,9 +18,20 @@ namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class, class, class>
|
||||
class stream_socket_connect_op;
|
||||
class timeout_stream_connect_op;
|
||||
|
||||
class stream_socket_base
|
||||
struct any_endpoint
|
||||
{
|
||||
template<class Error, class Endpoint>
|
||||
bool
|
||||
operator()(
|
||||
Error const&, Endpoint const&) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class timeout_stream_base
|
||||
{
|
||||
protected:
|
||||
class pending_guard
|
||||
@ -43,9 +54,11 @@ protected:
|
||||
b_ = true;
|
||||
}
|
||||
|
||||
pending_guard(pending_guard&& other) noexcept
|
||||
pending_guard(
|
||||
pending_guard&& other) noexcept
|
||||
: b_(other.b_)
|
||||
, clear_(boost::exchange(other.clear_, false))
|
||||
, clear_(boost::exchange(
|
||||
other.clear_, false))
|
||||
{
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
699
include/boost/beast/core/impl/basic_timeout_stream.hpp
Normal file
699
include/boost/beast/core/impl/basic_timeout_stream.hpp
Normal file
@ -0,0 +1,699 @@
|
||||
//
|
||||
// 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_IMPL_BASIC_TIMEOUT_STREAM_HPP
|
||||
#define BOOST_BEAST_CORE_IMPL_BASIC_TIMEOUT_STREAM_HPP
|
||||
|
||||
#include <boost/beast/core/async_op_base.hpp>
|
||||
#include <boost/beast/core/buffers_prefix.hpp>
|
||||
#include <boost/asio/bind_executor.hpp>
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/exchange.hpp>
|
||||
#include <cstdlib>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
struct basic_timeout_stream<
|
||||
Protocol, Executor>::timeout_handler
|
||||
{
|
||||
op_state& state;
|
||||
std::shared_ptr<impl_type> impl;
|
||||
|
||||
void
|
||||
operator()(error_code ec)
|
||||
{
|
||||
// timer canceled
|
||||
if(ec == net::error::operation_aborted)
|
||||
return;
|
||||
|
||||
BOOST_ASSERT(! ec);
|
||||
|
||||
if(! state.closed)
|
||||
{
|
||||
// timeout
|
||||
impl->close();
|
||||
state.closed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// late completion
|
||||
state.closed = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
The algorithm for implementing the timeout depends
|
||||
on the executor providing ordered execution guarantee.
|
||||
`net::strand` automatically provides this, and we assume
|
||||
that an implicit strand (one thread calling io_context::run)
|
||||
also provides this.
|
||||
*/
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
template<bool isRead, class Buffers, class Handler>
|
||||
class basic_timeout_stream<Protocol, Executor>::async_op
|
||||
: public async_op_base<Handler, Executor>
|
||||
, public boost::asio::coroutine
|
||||
{
|
||||
std::shared_ptr<impl_type> impl_;
|
||||
pending_guard pg_;
|
||||
Buffers b_;
|
||||
|
||||
op_state&
|
||||
state(std::true_type)
|
||||
{
|
||||
return impl_->read;
|
||||
}
|
||||
|
||||
op_state&
|
||||
state(std::false_type)
|
||||
{
|
||||
return impl_->write;
|
||||
}
|
||||
|
||||
op_state&
|
||||
state()
|
||||
{
|
||||
return state(
|
||||
std::integral_constant<bool, isRead>{});
|
||||
}
|
||||
|
||||
void
|
||||
async_perform(std::true_type)
|
||||
{
|
||||
impl_->socket.async_read_some(
|
||||
b_, std::move(*this));
|
||||
}
|
||||
|
||||
void
|
||||
async_perform(std::false_type)
|
||||
{
|
||||
impl_->socket.async_write_some(
|
||||
b_, std::move(*this));
|
||||
}
|
||||
|
||||
public:
|
||||
template<class Handler_>
|
||||
async_op(
|
||||
basic_timeout_stream& s,
|
||||
Buffers const& b,
|
||||
Handler_&& h)
|
||||
: async_op_base<Handler, Executor>(
|
||||
std::forward<Handler_>(h), s.get_executor())
|
||||
, impl_(s.impl_)
|
||||
, pg_(state().pending)
|
||||
, b_(b)
|
||||
{
|
||||
(*this)({});
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec,
|
||||
std::size_t bytes_transferred = 0)
|
||||
{
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
// VFALCO TODO handle buffer size == 0
|
||||
|
||||
// must come first
|
||||
state().timer.async_wait(
|
||||
net::bind_executor(
|
||||
this->get_executor(),
|
||||
timeout_handler{state(),
|
||||
impl_->shared_from_this()}));
|
||||
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
async_perform(
|
||||
std::integral_constant<bool, isRead>{});
|
||||
|
||||
// try cancelling timer
|
||||
auto const n =
|
||||
state().timer.cancel();
|
||||
|
||||
if(state().closed)
|
||||
{
|
||||
// timeout handler already invoked
|
||||
BOOST_ASSERT(n == 0);
|
||||
ec = beast::error::timeout;
|
||||
state().closed = false;
|
||||
}
|
||||
else if(n == 0)
|
||||
{
|
||||
// timeout handler already queued
|
||||
ec = beast::error::timeout;
|
||||
|
||||
impl_->close();
|
||||
state().closed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// timeout was canceled
|
||||
BOOST_ASSERT(n == 1);
|
||||
}
|
||||
|
||||
pg_.reset();
|
||||
this->invoke(ec, bytes_transferred);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<
|
||||
class Protocol, class Executor, class Handler>
|
||||
class timeout_stream_connect_op
|
||||
: public async_op_base<Handler, Executor>
|
||||
{
|
||||
using stream_type =
|
||||
beast::basic_timeout_stream<Protocol, Executor>;
|
||||
|
||||
using timeout_handler =
|
||||
typename stream_type::timeout_handler;
|
||||
|
||||
std::shared_ptr<typename basic_timeout_stream<
|
||||
Protocol, Executor>::impl_type> impl_;
|
||||
typename stream_type::pending_guard pg0_;
|
||||
typename stream_type::pending_guard pg1_;
|
||||
|
||||
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
|
||||
// VFALCO TODO what about the handler's allocator?
|
||||
impl_->write.timer.async_wait(
|
||||
net::bind_executor(
|
||||
this->get_executor(),
|
||||
timeout_handler{impl_->write,
|
||||
impl_->shared_from_this()}));
|
||||
|
||||
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{impl_->write,
|
||||
impl_->shared_from_this()}));
|
||||
|
||||
net::async_connect(impl_->socket,
|
||||
begin, end, cond, std::move(*this));
|
||||
// *this is now moved-from
|
||||
}
|
||||
|
||||
template<class Handler_>
|
||||
timeout_stream_connect_op(
|
||||
stream_type& s,
|
||||
typename stream_type::endpoint_type ep,
|
||||
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{impl_->write,
|
||||
impl_->shared_from_this()}));
|
||||
|
||||
impl_->socket.async_connect(
|
||||
ep, std::move(*this));
|
||||
// *this is now moved-from
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
void
|
||||
operator()(error_code ec, Args&&... args)
|
||||
{
|
||||
// try to cancel the timer
|
||||
auto const n =
|
||||
impl_->write.timer.cancel();
|
||||
|
||||
if(impl_->write.closed)
|
||||
{
|
||||
// timeout handler already invoked
|
||||
BOOST_ASSERT(n == 0);
|
||||
ec = beast::error::timeout;
|
||||
impl_->write.closed = false;
|
||||
}
|
||||
else if(n == 0)
|
||||
{
|
||||
// timeout handler already queued
|
||||
ec = beast::error::timeout;
|
||||
|
||||
impl_->close();
|
||||
impl_->write.closed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// timeout was canceled
|
||||
BOOST_ASSERT(n == 1);
|
||||
}
|
||||
|
||||
pg0_.reset();
|
||||
pg1_.reset();
|
||||
this->invoke(ec, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
template<class... Args>
|
||||
basic_timeout_stream<Protocol, Executor>::
|
||||
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,
|
||||
// cancel any operations so the shared
|
||||
// object is destroyed as soon as possible.
|
||||
impl_->close();
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
template<class ExecutionContext, class>
|
||||
basic_timeout_stream<Protocol, Executor>::
|
||||
basic_timeout_stream(ExecutionContext& ctx)
|
||||
: impl_(std::make_shared<impl_type>(
|
||||
ctx.get_executor(),
|
||||
ctx))
|
||||
{
|
||||
// Restriction is necessary until Asio fully supports P1322R0
|
||||
static_assert(
|
||||
std::is_same<ExecutionContext, net::io_context>::value,
|
||||
"Only net::io_context is currently supported for ExecutionContext");
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
basic_timeout_stream<Protocol, Executor>::
|
||||
basic_timeout_stream(executor_type const& ex)
|
||||
: impl_(std::make_shared<impl_type>(
|
||||
ex, ex.context()))
|
||||
{
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
basic_timeout_stream<Protocol, Executor>::
|
||||
basic_timeout_stream(
|
||||
net::basic_stream_socket<Protocol>&& socket)
|
||||
: impl_(std::make_shared<impl_type>(
|
||||
socket.get_executor(), std::move(socket)))
|
||||
{
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
basic_timeout_stream<Protocol, Executor>::
|
||||
basic_timeout_stream(
|
||||
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>(
|
||||
std::move(*other.impl_)))
|
||||
{
|
||||
// Can't move while operations are pending!
|
||||
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>
|
||||
void
|
||||
basic_timeout_stream<Protocol, Executor>::
|
||||
expires_after(std::chrono::nanoseconds expiry_time)
|
||||
{
|
||||
// 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(
|
||||
! impl_->read.pending ||
|
||||
! impl_->write.pending);
|
||||
|
||||
if(! impl_->read.pending)
|
||||
BOOST_VERIFY(
|
||||
impl_->read.timer.expires_after(
|
||||
expiry_time) == 0);
|
||||
|
||||
if(! impl_->write.pending)
|
||||
BOOST_VERIFY(
|
||||
impl_->write.timer.expires_after(
|
||||
expiry_time) == 0);
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
void
|
||||
basic_timeout_stream<Protocol, Executor>::
|
||||
expires_at(
|
||||
net::steady_timer::time_point expiry_time)
|
||||
{
|
||||
// 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(
|
||||
! impl_->read.pending ||
|
||||
! impl_->write.pending);
|
||||
|
||||
if(! impl_->read.pending)
|
||||
BOOST_VERIFY(
|
||||
impl_->read.timer.expires_at(
|
||||
expiry_time) == 0);
|
||||
|
||||
if(! impl_->write.pending)
|
||||
BOOST_VERIFY(
|
||||
impl_->write.timer.expires_at(
|
||||
expiry_time) == 0);
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
void
|
||||
basic_timeout_stream<Protocol, Executor>::
|
||||
expires_never()
|
||||
{
|
||||
impl_->reset();
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
void
|
||||
basic_timeout_stream<Protocol, Executor>::
|
||||
cancel()
|
||||
{
|
||||
error_code ec;
|
||||
impl_->socket.cancel(ec);
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
void
|
||||
basic_timeout_stream<Protocol, Executor>::
|
||||
close()
|
||||
{
|
||||
impl_->close();
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
template<class ConnectHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler,
|
||||
void(error_code))
|
||||
basic_timeout_stream<Protocol, Executor>::
|
||||
async_connect(
|
||||
endpoint_type ep,
|
||||
ConnectHandler&& handler)
|
||||
{
|
||||
BOOST_BEAST_HANDLER_INIT(
|
||||
ConnectHandler, void(error_code));
|
||||
detail::timeout_stream_connect_op<
|
||||
Protocol, Executor, BOOST_ASIO_HANDLER_TYPE(
|
||||
ConnectHandler, void(error_code))>(*this,
|
||||
ep, std::forward<ConnectHandler>(handler));
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
template<class MutableBufferSequence, class ReadHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||
void(error_code, std::size_t))
|
||||
basic_timeout_stream<Protocol, Executor>::
|
||||
async_read_some(
|
||||
MutableBufferSequence const& buffers,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(net::is_mutable_buffer_sequence<
|
||||
MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
BOOST_BEAST_HANDLER_INIT(
|
||||
ReadHandler, void(error_code, std::size_t));
|
||||
async_op<true, MutableBufferSequence, BOOST_ASIO_HANDLER_TYPE(
|
||||
ReadHandler, void(error_code, std::size_t))>(
|
||||
*this, buffers, std::forward<ReadHandler>(handler));
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
|
||||
void(error_code, std::size_t))
|
||||
basic_timeout_stream<Protocol, Executor>::
|
||||
async_write_some(
|
||||
ConstBufferSequence const& buffers,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(net::is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
BOOST_BEAST_HANDLER_INIT(
|
||||
WriteHandler, void(error_code, std::size_t));
|
||||
async_op<false, ConstBufferSequence, BOOST_ASIO_HANDLER_TYPE(
|
||||
WriteHandler, void(error_code, std::size_t))>(
|
||||
*this, buffers, std::forward<WriteHandler>(handler));
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
class EndpointSequence,
|
||||
class RangeConnectHandler,
|
||||
class>
|
||||
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)
|
||||
{
|
||||
BOOST_BEAST_HANDLER_INIT(RangeConnectHandler,
|
||||
void(error_code, typename Protocol::endpoint));
|
||||
detail::timeout_stream_connect_op<Protocol, Executor,
|
||||
BOOST_ASIO_HANDLER_TYPE(RangeConnectHandler,
|
||||
void(error_code,
|
||||
typename Protocol::endpoint))>(
|
||||
stream, endpoints, detail::any_endpoint{},
|
||||
std::forward<RangeConnectHandler>(handler));
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
class EndpointSequence,
|
||||
class ConnectCondition,
|
||||
class RangeConnectHandler,
|
||||
class>
|
||||
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)
|
||||
{
|
||||
BOOST_BEAST_HANDLER_INIT(RangeConnectHandler,
|
||||
void(error_code, typename Protocol::endpoint));
|
||||
detail::timeout_stream_connect_op<Protocol, Executor,
|
||||
BOOST_ASIO_HANDLER_TYPE(RangeConnectHandler,
|
||||
void(error_code,
|
||||
typename Protocol::endpoint))>(
|
||||
stream, endpoints, connect_condition,
|
||||
std::forward<RangeConnectHandler>(handler));
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
BOOST_BEAST_HANDLER_INIT(IteratorConnectHandler,
|
||||
void(error_code, Iterator));
|
||||
detail::timeout_stream_connect_op<Protocol, Executor,
|
||||
BOOST_ASIO_HANDLER_TYPE(IteratorConnectHandler,
|
||||
void(error_code, Iterator))>(
|
||||
stream, begin, end, detail::any_endpoint{},
|
||||
std::forward<IteratorConnectHandler>(handler));
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
BOOST_BEAST_HANDLER_INIT(IteratorConnectHandler,
|
||||
void(error_code, Iterator));
|
||||
detail::timeout_stream_connect_op<Protocol, Executor,
|
||||
BOOST_ASIO_HANDLER_TYPE(IteratorConnectHandler,
|
||||
void(error_code, Iterator))>(
|
||||
stream, begin, end, connect_condition,
|
||||
std::forward<IteratorConnectHandler>(handler));
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
@ -7,11 +7,11 @@
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_STREAM_SOCKET_HPP
|
||||
#define BOOST_BEAST_CORE_STREAM_SOCKET_HPP
|
||||
#ifndef BOOST_BEAST_CORE_TIMEOUT_STREAM_HPP
|
||||
#define BOOST_BEAST_CORE_TIMEOUT_STREAM_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/basic_stream_socket.hpp>
|
||||
#include <boost/beast/core/basic_timeout_stream.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
@ -20,7 +20,7 @@ namespace beast {
|
||||
|
||||
/** A TCP/IP stream socket which supports timeouts and rate limits
|
||||
*/
|
||||
using stream_socket = basic_stream_socket<
|
||||
using timeout_stream = basic_timeout_stream<
|
||||
net::ip::tcp,
|
||||
net::io_context::executor_type>;
|
||||
|
@ -28,7 +28,7 @@ add_executable (tests-beast-core
|
||||
_detail_variant.cpp
|
||||
_detail_varint.cpp
|
||||
async_op_base.cpp
|
||||
basic_stream_socket.cpp
|
||||
basic_timeout_stream.cpp
|
||||
bind_handler.cpp
|
||||
buffer_traits.cpp
|
||||
buffered_read_stream.cpp
|
||||
@ -55,9 +55,9 @@ add_executable (tests-beast-core
|
||||
span.cpp
|
||||
static_buffer.cpp
|
||||
static_string.cpp
|
||||
stream_socket.cpp
|
||||
string.cpp
|
||||
string_param.cpp
|
||||
timeout_stream.cpp
|
||||
type_traits.cpp
|
||||
)
|
||||
|
||||
|
@ -17,7 +17,7 @@ local SOURCES =
|
||||
_detail_variant.cpp
|
||||
_detail_varint.cpp
|
||||
async_op_base.cpp
|
||||
basic_stream_socket.cpp
|
||||
basic_timeout_stream.cpp
|
||||
bind_handler.cpp
|
||||
buffer_traits.cpp
|
||||
buffered_read_stream.cpp
|
||||
@ -44,9 +44,9 @@ local SOURCES =
|
||||
span.cpp
|
||||
static_buffer.cpp
|
||||
static_string.cpp
|
||||
stream_socket.cpp
|
||||
string.cpp
|
||||
string_param.cpp
|
||||
timeout_stream.cpp
|
||||
type_traits.cpp
|
||||
;
|
||||
|
||||
|
@ -1,132 +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/basic_stream_socket.hpp>
|
||||
|
||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||
#include <boost/beast/core/stream_socket.hpp>
|
||||
#include <boost/asio/read_until.hpp>
|
||||
#include <boost/asio/streambuf.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/ip/udp.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
class basic_stream_socket_test
|
||||
: public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
struct read_handler
|
||||
{
|
||||
template<class... Args>
|
||||
void operator()(Args&&...)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <class Protocol, class Executor, class ReadHandler>
|
||||
void async_read_line (
|
||||
basic_stream_socket<Protocol, Executor>& stream,
|
||||
net::streambuf& buffer, ReadHandler&& handler)
|
||||
{
|
||||
stream.expires_after(std::chrono::seconds(30));
|
||||
|
||||
net::async_read_until(stream, buffer, "\r\n", std::forward<ReadHandler>(handler));
|
||||
}
|
||||
|
||||
void
|
||||
testJavadocs()
|
||||
{
|
||||
BEAST_EXPECT((&basic_stream_socket_test::async_read_line<
|
||||
net::ip::tcp, net::io_context::executor_type, read_handler>));
|
||||
}
|
||||
|
||||
struct other_t
|
||||
{
|
||||
};
|
||||
|
||||
void
|
||||
testMembers()
|
||||
{
|
||||
using tcp = net::ip::tcp;
|
||||
using ep_t = tcp::endpoint;
|
||||
using ioc_t = net::io_context;
|
||||
using ex_t = ioc_t::executor_type;
|
||||
using stream_t = basic_stream_socket<tcp, ex_t>;
|
||||
|
||||
net::io_context ioc;
|
||||
auto ex = ioc.get_executor();
|
||||
|
||||
// construction
|
||||
|
||||
{
|
||||
stream_t{ioc};
|
||||
stream_t{ex};
|
||||
BOOST_STATIC_ASSERT(! std::is_constructible<
|
||||
stream_t, other_t>::value);
|
||||
}
|
||||
{
|
||||
stream_t{ioc, tcp::v4()};
|
||||
stream_t{ex, tcp::v4()};
|
||||
BOOST_STATIC_ASSERT(! std::is_constructible<
|
||||
stream_t, other_t, tcp>::value);
|
||||
}
|
||||
{
|
||||
stream_t{ioc, ep_t{}};
|
||||
stream_t{ex, ep_t{}};
|
||||
BOOST_STATIC_ASSERT(! std::is_constructible<
|
||||
stream_t, other_t, ep_t>::value);
|
||||
}
|
||||
{
|
||||
tcp::socket sock(ioc);
|
||||
stream_t{ioc, std::move(sock)};
|
||||
stream_t{ex, std::move(sock)};
|
||||
BOOST_STATIC_ASSERT(! std::is_constructible<
|
||||
stream_t, other_t, tcp::socket>::value);
|
||||
}
|
||||
|
||||
// move
|
||||
|
||||
{
|
||||
stream_t s1(ioc);
|
||||
stream_t s2(std::move(s1));
|
||||
}
|
||||
{
|
||||
stream_t s1(ioc);
|
||||
stream_t s2(ioc);
|
||||
s2 = std::move(s1);
|
||||
}
|
||||
|
||||
// converting move
|
||||
|
||||
{
|
||||
// We don't have any convertible protocol types
|
||||
#if 0
|
||||
basic_stream_socket<net::ip::udp, ex_t> s1(ioc);
|
||||
stream_t s2(std::move(s1));
|
||||
|
||||
stream_t s3 = std::move(s1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
testJavadocs();
|
||||
testMembers();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(beast,core,basic_stream_socket);
|
||||
|
||||
} // beast
|
||||
} // boost
|
932
test/beast/core/basic_timeout_stream.cpp
Normal file
932
test/beast/core/basic_timeout_stream.cpp
Normal file
@ -0,0 +1,932 @@
|
||||
//
|
||||
// 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/basic_timeout_stream.hpp>
|
||||
|
||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||
#include <boost/beast/core/flat_buffer.hpp>
|
||||
#include <boost/beast/core/timeout_stream.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/empty_body.hpp>
|
||||
#include <boost/beast/http/read.hpp>
|
||||
#include <boost/beast/http/string_body.hpp>
|
||||
#include <boost/beast/http/write.hpp>
|
||||
#include <boost/beast/websocket/stream.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <array>
|
||||
#include <thread>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
class basic_timeout_stream_test
|
||||
: public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
struct socket_pair
|
||||
{
|
||||
net::io_context ioc1;
|
||||
net::ip::tcp::socket s1;
|
||||
|
||||
net::io_context ioc2;
|
||||
net::ip::tcp::socket s2;
|
||||
|
||||
socket_pair()
|
||||
: s1(ioc1)
|
||||
, s2(ioc2)
|
||||
{
|
||||
net::io_context ioc;
|
||||
net::ip::tcp::acceptor a(ioc);
|
||||
net::ip::tcp::endpoint 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);
|
||||
a.async_accept(s2,
|
||||
[](error_code ec)
|
||||
{
|
||||
#if 0
|
||||
if(ec == net::error::operation_aborted)
|
||||
return;
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
#endif
|
||||
});
|
||||
s1.async_connect(a.local_endpoint(),
|
||||
[](error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
system_error{ec});
|
||||
});
|
||||
for(;;)
|
||||
if(
|
||||
ioc.poll() +
|
||||
ioc1.poll() +
|
||||
ioc2.poll() == 0)
|
||||
break;
|
||||
BOOST_ASSERT(s1.is_open());
|
||||
#if 0
|
||||
BOOST_ASSERT(s2.is_open()); // VFALCO Fails on Travis for some reason
|
||||
BOOST_ASSERT(
|
||||
s1.remote_endpoint() ==
|
||||
s2.local_endpoint());
|
||||
BOOST_ASSERT(
|
||||
s2.remote_endpoint() ==
|
||||
s1.local_endpoint());
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class server
|
||||
{
|
||||
string_view s_;
|
||||
std::ostream& log_;
|
||||
net::io_context ioc_;
|
||||
net::ip::tcp::acceptor acceptor_;
|
||||
net::ip::tcp::socket socket_;
|
||||
std::thread t_;
|
||||
|
||||
void
|
||||
fail(error_code ec, string_view what)
|
||||
{
|
||||
if(ec != net::error::operation_aborted)
|
||||
log_ << what << ": " << ec.message() << "\n";
|
||||
}
|
||||
|
||||
public:
|
||||
server(
|
||||
string_view s,
|
||||
net::ip::tcp::endpoint ep,
|
||||
std::ostream& log)
|
||||
: s_(s)
|
||||
, log_(log)
|
||||
, ioc_(1)
|
||||
, acceptor_(ioc_)
|
||||
, socket_(ioc_)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
|
||||
acceptor_.open(ep.protocol(), ec);
|
||||
if(ec)
|
||||
{
|
||||
fail(ec, "open");
|
||||
return;
|
||||
}
|
||||
|
||||
acceptor_.set_option(
|
||||
net::socket_base::reuse_address(true), ec);
|
||||
if(ec)
|
||||
{
|
||||
fail(ec, "set_option");
|
||||
return;
|
||||
}
|
||||
|
||||
acceptor_.bind(ep, ec);
|
||||
if(ec)
|
||||
{
|
||||
fail(ec, "bind");
|
||||
return;
|
||||
}
|
||||
|
||||
acceptor_.listen(
|
||||
net::socket_base::max_listen_connections, ec);
|
||||
if(ec)
|
||||
{
|
||||
fail(ec, "listen");
|
||||
return;
|
||||
}
|
||||
|
||||
acceptor_.async_accept(socket_,
|
||||
[this](error_code ec)
|
||||
{
|
||||
this->on_accept(ec);
|
||||
});
|
||||
|
||||
t_ = std::thread(
|
||||
[this]
|
||||
{
|
||||
ioc_.run();
|
||||
});
|
||||
}
|
||||
|
||||
~server()
|
||||
{
|
||||
ioc_.stop();
|
||||
t_.join();
|
||||
}
|
||||
|
||||
net::ip::tcp::endpoint
|
||||
local_endpoint() const noexcept
|
||||
{
|
||||
return acceptor_.local_endpoint();
|
||||
}
|
||||
|
||||
private:
|
||||
class session
|
||||
: public std::enable_shared_from_this<session>
|
||||
{
|
||||
string_view s_;
|
||||
net::ip::tcp::socket socket_;
|
||||
|
||||
public:
|
||||
session(
|
||||
string_view s,
|
||||
net::ip::tcp::socket sock,
|
||||
std::ostream&)
|
||||
: s_(s)
|
||||
, socket_(std::move(sock))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
if(s_.empty())
|
||||
socket_.async_wait(
|
||||
net::socket_base::wait_read,
|
||||
std::bind(
|
||||
&session::on_read,
|
||||
shared_from_this(),
|
||||
std::placeholders::_1));
|
||||
else
|
||||
net::async_write(
|
||||
socket_,
|
||||
net::const_buffer(s_.data(), s_.size()),
|
||||
std::bind(
|
||||
&session::on_write,
|
||||
shared_from_this(),
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2));
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
on_read(error_code ec)
|
||||
{
|
||||
boost::ignore_unused(ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_write(error_code, std::size_t)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
on_accept(error_code ec)
|
||||
{
|
||||
if(! acceptor_.is_open())
|
||||
return;
|
||||
if(ec)
|
||||
fail(ec, "accept");
|
||||
else
|
||||
std::make_shared<session>(
|
||||
s_, std::move(socket_), log_)->run();
|
||||
acceptor_.async_accept(socket_,
|
||||
[this](error_code ec)
|
||||
{
|
||||
this->on_accept(ec);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
testStrand()
|
||||
{
|
||||
{
|
||||
using strand_type = net::io_context::strand;
|
||||
net::io_context ioc;
|
||||
strand_type st(ioc);
|
||||
basic_timeout_stream<
|
||||
net::ip::tcp, strand_type> s(st);
|
||||
BEAST_EXPECT(s.get_executor() == st);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// VFALCO This is disallowed until Asio implements P1322R0
|
||||
{
|
||||
using strand_type = net::strand<
|
||||
net::io_context::executor_type>;
|
||||
net::io_context ioc;
|
||||
strand_type st(ioc.get_executor());
|
||||
basic_timeout_stream<
|
||||
net::ip::tcp, strand_type> s(st);
|
||||
BEAST_EXPECT(s.get_executor() == st);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
struct other_t
|
||||
{
|
||||
};
|
||||
|
||||
void
|
||||
testMembers()
|
||||
{
|
||||
using tcp = net::ip::tcp;
|
||||
using stream_t = basic_timeout_stream<tcp>;
|
||||
|
||||
net::io_context ioc;
|
||||
auto ex = ioc.get_executor();
|
||||
|
||||
// construction
|
||||
|
||||
BOOST_STATIC_ASSERT(! std::is_constructible<
|
||||
stream_t, other_t>::value);
|
||||
|
||||
BOOST_STATIC_ASSERT(! std::is_constructible<
|
||||
stream_t, other_t, tcp::socket>::value);
|
||||
|
||||
{
|
||||
stream_t s(ioc);
|
||||
}
|
||||
|
||||
{
|
||||
stream_t s(ex);
|
||||
}
|
||||
|
||||
{
|
||||
stream_t s((tcp::socket(ioc)));
|
||||
}
|
||||
|
||||
{
|
||||
stream_t s(ex, tcp::socket(ioc));
|
||||
}
|
||||
|
||||
{
|
||||
net::io_context ioc2;
|
||||
try
|
||||
{
|
||||
// mismatched execution contexts
|
||||
stream_t s(
|
||||
ioc2.get_executor(),
|
||||
tcp::socket(ioc));
|
||||
fail("mismatched execution context", __FILE__, __LINE__);
|
||||
}
|
||||
catch(std::invalid_argument const&)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
}
|
||||
|
||||
// move
|
||||
|
||||
{
|
||||
stream_t s1(ioc);
|
||||
stream_t s2(std::move(s1));
|
||||
}
|
||||
|
||||
// assign
|
||||
|
||||
{
|
||||
stream_t s1(ioc);
|
||||
stream_t s2(ioc);
|
||||
s2 = std::move(s1);
|
||||
}
|
||||
|
||||
// get_executor
|
||||
|
||||
{
|
||||
stream_t s(ioc);
|
||||
BEAST_EXPECT(
|
||||
s.get_executor() == ioc.get_executor());
|
||||
}
|
||||
|
||||
// layers
|
||||
|
||||
{
|
||||
net::socket_base::keep_alive opt;
|
||||
tcp::socket sock(ioc);
|
||||
sock.open(tcp::v4());
|
||||
sock.get_option(opt);
|
||||
BEAST_EXPECT(! opt.value());
|
||||
stream_t s(ioc);
|
||||
s.next_layer().open(tcp::v4());
|
||||
s.next_layer().get_option(opt);
|
||||
BEAST_EXPECT(! opt.value());
|
||||
opt = true;
|
||||
sock.set_option(opt);
|
||||
opt = false;
|
||||
BEAST_EXPECT(! opt.value());
|
||||
s = stream_t(std::move(sock));
|
||||
static_cast<stream_t const&>(s).next_layer().get_option(opt);
|
||||
BEAST_EXPECT(opt.value());
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
struct match
|
||||
{
|
||||
suite& suite_;
|
||||
error_code ec_;
|
||||
std::size_t n_;
|
||||
|
||||
match(suite& s, error_code ec, std::size_t n)
|
||||
: suite_(s)
|
||||
, ec_(ec)
|
||||
, n_(n)
|
||||
{
|
||||
}
|
||||
|
||||
match(match&& other)
|
||||
: suite_(other.suite_)
|
||||
, ec_(other.ec_)
|
||||
, n_(boost::exchange(other.n_,
|
||||
(std::numeric_limits<std::size_t>::max)()))
|
||||
{
|
||||
}
|
||||
|
||||
~match()
|
||||
{
|
||||
suite_.BEAST_EXPECT(
|
||||
n_ == (std::numeric_limits<std::size_t>::max)());
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, std::size_t n)
|
||||
{
|
||||
suite_.expect(ec == ec_, ec.message(), __FILE__, __LINE__);
|
||||
suite_.BEAST_EXPECT(n == n_);
|
||||
n_ = (std::numeric_limits<std::size_t>::max)();
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
testRead()
|
||||
{
|
||||
using tcp = net::ip::tcp;
|
||||
using stream_t = basic_timeout_stream<tcp>;
|
||||
|
||||
char buf[4];
|
||||
std::memset(buf, 0, 4);
|
||||
net::mutable_buffer mb(buf, sizeof(buf));
|
||||
auto const ep = net::ip::tcp::endpoint(
|
||||
net::ip::make_address("127.0.0.1"), 0);
|
||||
|
||||
// success
|
||||
{
|
||||
server srv("*", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
s.next_layer().connect(srv.local_endpoint());
|
||||
s.async_read_some(mb, match{*this, {}, 1});
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
// success, with timeout
|
||||
{
|
||||
server srv("*", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
s.next_layer().connect(srv.local_endpoint());
|
||||
s.expires_after(std::chrono::milliseconds(100));
|
||||
s.async_read_some(mb, match{*this, {}, 1});
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
s.expires_never();
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
// close
|
||||
{
|
||||
server srv("", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
s.next_layer().connect(srv.local_endpoint());
|
||||
s.async_read_some(mb, match{*this,
|
||||
net::error::operation_aborted, 0});
|
||||
{
|
||||
error_code ec;
|
||||
s.next_layer().shutdown(
|
||||
net::socket_base::shutdown_both,
|
||||
ec);
|
||||
}
|
||||
s.close();
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
// cancel
|
||||
{
|
||||
server srv("", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
s.next_layer().connect(srv.local_endpoint());
|
||||
s.async_read_some(mb, match{*this,
|
||||
net::error::operation_aborted, 0});
|
||||
ioc.run_for(std::chrono::milliseconds(100));
|
||||
s.cancel();
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
// immediate timeout
|
||||
{
|
||||
server srv("*", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
s.next_layer().connect(srv.local_endpoint());
|
||||
s.expires_after(std::chrono::seconds(-1));
|
||||
s.async_read_some(mb,
|
||||
[&](error_code ec, std::size_t n)
|
||||
{
|
||||
#if 0
|
||||
// Unreliable on epoll impls
|
||||
BEAST_EXPECT(
|
||||
(ec == error::timeout && n == 0) ||
|
||||
(! ec && n == 1));
|
||||
#else
|
||||
boost::ignore_unused(ec, n);
|
||||
pass();
|
||||
#endif
|
||||
});
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
// fail, with timeout
|
||||
{
|
||||
server srv("", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
s.next_layer().connect(srv.local_endpoint());
|
||||
s.expires_after(std::chrono::milliseconds(100));
|
||||
s.async_read_some(mb,
|
||||
match{*this, error::timeout, 0});
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
// success, with timeout
|
||||
{
|
||||
server srv("*", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
s.next_layer().connect(srv.local_endpoint());
|
||||
s.expires_at(
|
||||
std::chrono::steady_clock::now() +
|
||||
std::chrono::milliseconds(100));
|
||||
s.async_read_some(mb,
|
||||
match{*this, {}, 1});
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
// abandoned ops
|
||||
{
|
||||
server srv("*", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
s.next_layer().connect(srv.local_endpoint());
|
||||
s.async_read_some(mb, [&](error_code, std::size_t){});
|
||||
}
|
||||
{
|
||||
server srv("*", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
s.next_layer().connect(srv.local_endpoint());
|
||||
s.expires_after(std::chrono::seconds(1));
|
||||
s.async_read_some(mb, [&](error_code, std::size_t){});
|
||||
}
|
||||
|
||||
// edge case:
|
||||
// timer completion becomes queued before
|
||||
// the I/O completion handler is invoked
|
||||
// VFALCO Fails on OSX Travis
|
||||
#if 0
|
||||
{
|
||||
socket_pair p;
|
||||
bool invoked = false;
|
||||
stream_t s(std::move(p.s1));
|
||||
s.expires_after(std::chrono::seconds(0));
|
||||
s.async_read_some(mb,
|
||||
[&](error_code ec, std::size_t)
|
||||
{
|
||||
invoked = true;
|
||||
BEAST_EXPECTS(ec == error::timeout,
|
||||
ec.message());
|
||||
});
|
||||
p.s2.async_write_some(
|
||||
net::const_buffer("*", 1),
|
||||
[&](error_code ec, std::size_t n)
|
||||
{
|
||||
boost::ignore_unused(ec, n);
|
||||
});
|
||||
p.ioc1.run();
|
||||
p.ioc1.restart();
|
||||
p.ioc2.run();
|
||||
p.ioc2.restart();
|
||||
p.ioc1.run();
|
||||
BEAST_EXPECT(invoked);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
testWrite()
|
||||
{
|
||||
using tcp = net::ip::tcp;
|
||||
using stream_t = basic_timeout_stream<tcp>;
|
||||
|
||||
char buf[4];
|
||||
std::memset(buf, 0, 4);
|
||||
net::mutable_buffer mb(buf, sizeof(buf));
|
||||
auto const ep = net::ip::tcp::endpoint(
|
||||
net::ip::make_address("127.0.0.1"), 0);
|
||||
|
||||
// write
|
||||
{
|
||||
server srv("", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
s.next_layer().connect(srv.local_endpoint());
|
||||
s.async_write_some(mb,
|
||||
match{*this, {}, mb.size()});
|
||||
{
|
||||
error_code ec;
|
||||
s.next_layer().shutdown(
|
||||
net::socket_base::shutdown_both,
|
||||
ec);
|
||||
}
|
||||
s.close();
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
// write abandoned
|
||||
{
|
||||
server srv("*", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
s.next_layer().connect(srv.local_endpoint());
|
||||
s.async_write_some(mb, [&](error_code, std::size_t){});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testConnect()
|
||||
{
|
||||
using tcp = net::ip::tcp;
|
||||
using stream_t = basic_timeout_stream<tcp>;
|
||||
|
||||
auto const ep = net::ip::tcp::endpoint(
|
||||
net::ip::make_address("127.0.0.1"), 0);
|
||||
|
||||
// overload 1
|
||||
{
|
||||
server srv("", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
bool invoked = false;
|
||||
std::array<tcp::endpoint, 1> epa{{
|
||||
srv.local_endpoint()}};
|
||||
beast::async_connect(s, epa,
|
||||
[&](error_code ec, tcp::endpoint)
|
||||
{
|
||||
invoked = true;
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
});
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
BEAST_EXPECT(invoked);
|
||||
}
|
||||
|
||||
// overload 2
|
||||
{
|
||||
server srv("", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
bool invoked = false;
|
||||
std::array<tcp::endpoint, 1> epa{{
|
||||
srv.local_endpoint()}};
|
||||
beast::async_connect(s, epa,
|
||||
[](error_code, tcp::endpoint)
|
||||
{
|
||||
return true;
|
||||
},
|
||||
[&](error_code ec, tcp::endpoint)
|
||||
{
|
||||
invoked = true;
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
});
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
BEAST_EXPECT(invoked);
|
||||
}
|
||||
|
||||
// overload 3
|
||||
{
|
||||
server srv("", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
bool invoked = false;
|
||||
std::array<tcp::endpoint, 1> epa{{
|
||||
srv.local_endpoint()}};
|
||||
using iter_type =
|
||||
std::array<tcp::endpoint, 1>::const_iterator;
|
||||
beast::async_connect(s, epa.begin(), epa.end(),
|
||||
[&](error_code ec, iter_type)
|
||||
{
|
||||
invoked = true;
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
});
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
BEAST_EXPECT(invoked);
|
||||
}
|
||||
|
||||
// overload 4
|
||||
{
|
||||
server srv("", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
bool invoked = false;
|
||||
std::array<tcp::endpoint, 1> epa{{
|
||||
srv.local_endpoint()}};
|
||||
using iter_type =
|
||||
std::array<tcp::endpoint, 1>::const_iterator;
|
||||
beast::async_connect(s, epa.begin(), epa.end(),
|
||||
[](error_code, tcp::endpoint)
|
||||
{
|
||||
return true;
|
||||
},
|
||||
[&](error_code ec, iter_type)
|
||||
{
|
||||
invoked = true;
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
});
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
BEAST_EXPECT(invoked);
|
||||
}
|
||||
|
||||
// success
|
||||
{
|
||||
server srv("", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
bool invoked = false;
|
||||
std::array<tcp::endpoint, 1> epa{{
|
||||
srv.local_endpoint()}};
|
||||
beast::async_connect(s, epa,
|
||||
[&](error_code ec, tcp::endpoint)
|
||||
{
|
||||
invoked = true;
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
});
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
BEAST_EXPECT(invoked);
|
||||
}
|
||||
|
||||
// success, with timeout
|
||||
{
|
||||
server srv("", ep, log);
|
||||
net::io_context ioc;
|
||||
stream_t s(ioc);
|
||||
bool invoked = false;
|
||||
std::array<tcp::endpoint, 1> epa{{
|
||||
srv.local_endpoint()}};
|
||||
s.expires_after(std::chrono::milliseconds(100));
|
||||
beast::async_connect(s, epa,
|
||||
[&](error_code ec, tcp::endpoint)
|
||||
{
|
||||
invoked = true;
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
});
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
BEAST_EXPECT(invoked);
|
||||
}
|
||||
|
||||
// immediate timeout
|
||||
{
|
||||
net::io_context ioc;
|
||||
stream_t s(tcp::socket(ioc, tcp::v6()));
|
||||
bool invoked = false;
|
||||
std::array<tcp::endpoint, 1> epa{{
|
||||
net::ip::tcp::endpoint(
|
||||
net::ip::make_address("192.168.0.254"), 1)}};
|
||||
s.expires_after(std::chrono::seconds(-1));
|
||||
beast::async_connect(s, epa,
|
||||
[&](error_code ec, tcp::endpoint)
|
||||
{
|
||||
invoked = true;
|
||||
BEAST_EXPECTS(
|
||||
ec == error::timeout, ec.message());
|
||||
});
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
BEAST_EXPECT(invoked);
|
||||
}
|
||||
|
||||
// edge case:
|
||||
// timer completion becomes queued before
|
||||
// the I/O completion handler is invoked
|
||||
// VFALCO Seems to hang on OSX Travis
|
||||
#if 0
|
||||
{
|
||||
net::io_context ioc1;
|
||||
stream_t s1(ioc1);
|
||||
net::io_context ioc2;
|
||||
net::ip::tcp::acceptor a(ioc2);
|
||||
a.open(ep.protocol());
|
||||
a.set_option(
|
||||
net::socket_base::reuse_address(true));
|
||||
a.bind(ep);
|
||||
a.listen(1);
|
||||
a.async_accept([](error_code, tcp::socket){});
|
||||
bool invoked = false;
|
||||
s1.expires_after(std::chrono::seconds(0));
|
||||
s1.async_connect(
|
||||
a.local_endpoint(),
|
||||
[&](error_code ec)
|
||||
{
|
||||
invoked = true;
|
||||
BEAST_EXPECTS(ec == error::timeout,
|
||||
ec.message());
|
||||
});
|
||||
ioc1.run();
|
||||
ioc1.restart();
|
||||
ioc2.run();
|
||||
ioc2.restart();
|
||||
ioc1.run();
|
||||
BEAST_EXPECT(invoked);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* VFALCO
|
||||
We need a reliable way of causing a real
|
||||
timeout, for example a stable IP address
|
||||
for which connections are never established,
|
||||
but that also do not cause immediate failure.
|
||||
*/
|
||||
#if 0
|
||||
// timeout (unreachable ipv4 host)
|
||||
{
|
||||
net::io_context ioc;
|
||||
stream_t s(tcp::socket(ioc, tcp::v6()));
|
||||
bool invoked = false;
|
||||
std::array<tcp::endpoint, 1> epa{{
|
||||
net::ip::tcp::endpoint(
|
||||
net::ip::make_address("192.168.0.254"), 1)}};
|
||||
s.expires_after(std::chrono::milliseconds(100));
|
||||
beast::async_connect(s, epa,
|
||||
[&](error_code ec, tcp::endpoint)
|
||||
{
|
||||
invoked = true;
|
||||
BEAST_EXPECTS(
|
||||
ec == error::timeout, ec.message());
|
||||
});
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
BEAST_EXPECT(invoked);
|
||||
}
|
||||
|
||||
// timeout (ipv6 black hole)
|
||||
{
|
||||
net::io_context ioc;
|
||||
stream_t s(tcp::socket(ioc, tcp::v6()));
|
||||
bool invoked = false;
|
||||
std::array<tcp::endpoint, 1> epa{{
|
||||
tcp::endpoint(
|
||||
net::ip::address(
|
||||
net::ip::make_address_v6("100::")),
|
||||
1)
|
||||
}};
|
||||
s.expires_after(std::chrono::milliseconds(100));
|
||||
beast::async_connect(s, epa,
|
||||
[&](error_code ec, tcp::endpoint)
|
||||
{
|
||||
invoked = true;
|
||||
BEAST_EXPECTS(
|
||||
ec == error::timeout, ec.message());
|
||||
});
|
||||
ioc.run_for(std::chrono::seconds(1));
|
||||
BEAST_EXPECT(invoked);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
http::response<http::string_body>
|
||||
make_response(http::request<http::empty_body>)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
websocket::stream<timeout_stream>
|
||||
process_websocket(timeout_stream&& stream, net::yield_context yield)
|
||||
{
|
||||
websocket::stream<timeout_stream> ws(std::move(stream));
|
||||
|
||||
// Require that the entire websocket handshake take no longer than 10 seconds
|
||||
ws.next_layer().expires_after(std::chrono::seconds(10));
|
||||
ws.async_accept(yield);
|
||||
|
||||
return ws;
|
||||
}
|
||||
|
||||
void
|
||||
testJavadocs()
|
||||
{
|
||||
BEAST_EXPECT(&basic_timeout_stream_test::process_http_1);
|
||||
BEAST_EXPECT(&basic_timeout_stream_test::process_http_2);
|
||||
BEAST_EXPECT(&basic_timeout_stream_test::process_websocket);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
testStrand();
|
||||
testMembers();
|
||||
testRead();
|
||||
testWrite();
|
||||
testConnect();
|
||||
testJavadocs();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(beast,core,basic_timeout_stream);
|
||||
|
||||
} // beast
|
||||
} // boost
|
@ -1,279 +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/stream_socket.hpp>
|
||||
|
||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||
#include <boost/beast/test/yield_to.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <array>
|
||||
#include <thread>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
class stream_socket_test
|
||||
: public beast::unit_test::suite
|
||||
, public test::enable_yield_to
|
||||
{
|
||||
public:
|
||||
class server
|
||||
{
|
||||
std::ostream& log_;
|
||||
net::io_context ioc_;
|
||||
net::ip::tcp::acceptor acceptor_;
|
||||
net::ip::tcp::socket socket_;
|
||||
std::thread t_;
|
||||
|
||||
void
|
||||
fail(error_code ec, string_view what)
|
||||
{
|
||||
if(ec != net::error::operation_aborted)
|
||||
log_ << what << ": " << ec.message() << "\n";
|
||||
}
|
||||
|
||||
public:
|
||||
server(
|
||||
net::ip::tcp::endpoint ep,
|
||||
std::ostream& log)
|
||||
: log_(log)
|
||||
, ioc_(1)
|
||||
, acceptor_(ioc_)
|
||||
, socket_(ioc_)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
|
||||
acceptor_.open(ep.protocol(), ec);
|
||||
if(ec)
|
||||
{
|
||||
fail(ec, "open");
|
||||
return;
|
||||
}
|
||||
|
||||
acceptor_.set_option(
|
||||
net::socket_base::reuse_address(true), ec);
|
||||
if(ec)
|
||||
{
|
||||
fail(ec, "set_option");
|
||||
return;
|
||||
}
|
||||
|
||||
acceptor_.bind(ep, ec);
|
||||
if(ec)
|
||||
{
|
||||
fail(ec, "bind");
|
||||
return;
|
||||
}
|
||||
|
||||
acceptor_.listen(
|
||||
net::socket_base::max_listen_connections, ec);
|
||||
if(ec)
|
||||
{
|
||||
fail(ec, "listen");
|
||||
return;
|
||||
}
|
||||
|
||||
acceptor_.async_accept(socket_,
|
||||
[this](error_code ec){ this->on_accept(ec); });
|
||||
|
||||
t_ = std::thread([this]{ ioc_.run(); });
|
||||
}
|
||||
|
||||
~server()
|
||||
{
|
||||
ioc_.stop();
|
||||
t_.join();
|
||||
}
|
||||
|
||||
private:
|
||||
class session
|
||||
: public std::enable_shared_from_this<session>
|
||||
{
|
||||
net::ip::tcp::socket socket_;
|
||||
|
||||
public:
|
||||
session(
|
||||
net::ip::tcp::socket sock,
|
||||
std::ostream&)
|
||||
: socket_(std::move(sock))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
socket_.async_wait(
|
||||
net::socket_base::wait_read,
|
||||
std::bind(
|
||||
&session::on_read,
|
||||
shared_from_this(),
|
||||
std::placeholders::_1));
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
on_read(error_code ec)
|
||||
{
|
||||
boost::ignore_unused(ec);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
on_accept(error_code ec)
|
||||
{
|
||||
if(! acceptor_.is_open())
|
||||
return;
|
||||
if(ec)
|
||||
fail(ec, "accept");
|
||||
else
|
||||
std::make_shared<session>(
|
||||
std::move(socket_), log_)->run();
|
||||
acceptor_.async_accept(socket_,
|
||||
[this](error_code ec)
|
||||
{
|
||||
this->on_accept(ec);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
testAsync()
|
||||
{
|
||||
net::ip::tcp::endpoint ep(
|
||||
net::ip::make_address("127.0.0.1"), 8080);
|
||||
server srv(ep, log);
|
||||
{
|
||||
net::io_context ioc;
|
||||
stream_socket s(ioc);
|
||||
s.next_layer().connect(ep);
|
||||
char buf[32];
|
||||
error_code ec;
|
||||
s.expires_after(std::chrono::seconds(1));
|
||||
s.async_read_some(net::buffer(buf),
|
||||
[&ec](error_code ec_, std::size_t)
|
||||
{
|
||||
ec = ec_;
|
||||
});
|
||||
ioc.run();
|
||||
BEAST_EXPECTS(
|
||||
ec == error::timeout, ec.message());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testConnect()
|
||||
{
|
||||
using es_t =
|
||||
std::array<net::ip::tcp::endpoint, 2>;
|
||||
{
|
||||
net::io_context ioc;
|
||||
stream_socket s(ioc);
|
||||
beast::async_connect(s, es_t{},
|
||||
[](error_code, es_t::value_type)
|
||||
{
|
||||
});
|
||||
}
|
||||
{
|
||||
net::io_context ioc;
|
||||
stream_socket s(ioc);
|
||||
beast::async_connect(s, es_t{},
|
||||
[](error_code, es_t::value_type)
|
||||
{
|
||||
return true;
|
||||
},
|
||||
[](error_code, es_t::value_type)
|
||||
{
|
||||
});
|
||||
}
|
||||
{
|
||||
es_t es;
|
||||
net::io_context ioc;
|
||||
stream_socket s(ioc);
|
||||
beast::async_connect(s, es.begin(), es.end(),
|
||||
[](error_code, es_t::iterator)
|
||||
{
|
||||
});
|
||||
}
|
||||
{
|
||||
es_t es;
|
||||
net::io_context ioc;
|
||||
stream_socket s(ioc);
|
||||
beast::async_connect(s, es.begin(), es.end(),
|
||||
[](error_code, es_t::value_type)
|
||||
{
|
||||
return true;
|
||||
},
|
||||
[](error_code, es_t::iterator)
|
||||
{
|
||||
});
|
||||
}
|
||||
pass();
|
||||
}
|
||||
|
||||
void callConnects()
|
||||
{
|
||||
using es_t =
|
||||
std::array<net::ip::tcp::endpoint, 2>;
|
||||
{
|
||||
net::io_context ioc;
|
||||
stream_socket s(ioc);
|
||||
async_connect(s, es_t{},
|
||||
[](error_code, es_t::value_type)
|
||||
{
|
||||
});
|
||||
}
|
||||
{
|
||||
net::io_context ioc;
|
||||
stream_socket s(ioc);
|
||||
async_connect(s, es_t{},
|
||||
[](error_code, es_t::value_type)
|
||||
{
|
||||
return true;
|
||||
},
|
||||
[](error_code, es_t::value_type)
|
||||
{
|
||||
});
|
||||
}
|
||||
{
|
||||
es_t es;
|
||||
net::io_context ioc;
|
||||
stream_socket s(ioc);
|
||||
async_connect(s, es.begin(), es.end(),
|
||||
[](error_code, es_t::iterator)
|
||||
{
|
||||
});
|
||||
}
|
||||
{
|
||||
es_t es;
|
||||
net::io_context ioc;
|
||||
stream_socket s(ioc);
|
||||
async_connect(s, es.begin(), es.end(),
|
||||
[](error_code, es_t::value_type)
|
||||
{
|
||||
return true;
|
||||
},
|
||||
[](error_code, es_t::iterator)
|
||||
{
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
testAsync();
|
||||
testConnect();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(beast,core,stream_socket);
|
||||
|
||||
} // beast
|
||||
} // boost
|
@ -215,8 +215,7 @@ template<class Executor>
|
||||
struct associated_executor<
|
||||
boost::beast::legacy_handler, Executor>
|
||||
{
|
||||
using type = typename
|
||||
boost::beast::simple_executor;
|
||||
using type = boost::beast::simple_executor;
|
||||
|
||||
static type get(
|
||||
boost::beast::legacy_handler const&,
|
||||
|
11
test/beast/core/timeout_stream.cpp
Normal file
11
test/beast/core/timeout_stream.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
//
|
||||
// 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/timeout_stream.hpp>
|
Reference in New Issue
Block a user