mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 05:17:26 +02:00
beast support per-op cancellation
- websocket supports cancellation. - Iterating test for ws cancellation. - Only terminal cancellation is forwarded by default. - basic_stream supports cancellation. - supported cancellation is documented. - http cancellation additions. - Added cancellation_slot tests to http, utils and saved_handler. - Added post to write.cpp, to avoid SIGSEV in test. - Refresher describes cancellation in more detail.
This commit is contained in:
committed by
Klemens Morgenstern
parent
0bf3d971a0
commit
3ebff60b1a
@ -1,3 +1,9 @@
|
|||||||
|
Version 342:
|
||||||
|
|
||||||
|
* Support per operation cancellation
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
Version 341:
|
Version 341:
|
||||||
|
|
||||||
* Expect header field with the "100-continue" is handled in upgrade.
|
* Expect header field with the "100-continue" is handled in upgrade.
|
||||||
@ -19,7 +25,6 @@ Version 339:
|
|||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
Version 338:
|
Version 338:
|
||||||
|
|
||||||
* Added per message compression options.
|
* Added per message compression options.
|
||||||
|
@ -199,6 +199,10 @@ has both an
|
|||||||
[@boost:/doc/html/boost_asio/overview/core/allocation.html ['associated allocator]]
|
[@boost:/doc/html/boost_asio/overview/core/allocation.html ['associated allocator]]
|
||||||
returned by
|
returned by
|
||||||
[@boost:/doc/html/boost_asio/reference/get_associated_allocator.html `net::get_associated_allocator`],
|
[@boost:/doc/html/boost_asio/reference/get_associated_allocator.html `net::get_associated_allocator`],
|
||||||
|
, an
|
||||||
|
[@boost:/doc/html/boost_asio/reference/associated_cancellation_slot.html ['associated cancellation slot]]
|
||||||
|
returned by
|
||||||
|
[@boost:/doc/html/boost_asio/reference/associated_cancellation_slot.html `net::get_associated_cancellation_slot`].
|
||||||
and an
|
and an
|
||||||
[@boost:/doc/html/boost_asio/reference/associated_executor.html ['associated executor]]
|
[@boost:/doc/html/boost_asio/reference/associated_executor.html ['associated executor]]
|
||||||
returned by
|
returned by
|
||||||
@ -210,6 +214,8 @@ These associations may be specified intrusively:
|
|||||||
Or these associations may be specified non-intrusively, by specializing
|
Or these associations may be specified non-intrusively, by specializing
|
||||||
the class templates
|
the class templates
|
||||||
[@boost:/doc/html/boost_asio/reference/associated_allocator.html `net::associated_allocator`]
|
[@boost:/doc/html/boost_asio/reference/associated_allocator.html `net::associated_allocator`]
|
||||||
|
,
|
||||||
|
[@boost:/doc/html/boost_asio/reference/associated_cancellation_slot.html `net::associated_cancellation_slot`]
|
||||||
and
|
and
|
||||||
[@boost:/doc/html/boost_asio/reference/associated_executor.html `net::associated_executor`]:
|
[@boost:/doc/html/boost_asio/reference/associated_executor.html `net::associated_executor`]:
|
||||||
|
|
||||||
@ -227,6 +233,36 @@ object providing the algorithm used to invoke the completion handler. Unless
|
|||||||
customized by the caller, a completion handler defaults to using
|
customized by the caller, a completion handler defaults to using
|
||||||
`std::allocator<void>` and the executor of the corresponding I/O object.
|
`std::allocator<void>` and the executor of the corresponding I/O object.
|
||||||
|
|
||||||
|
The function
|
||||||
|
[@boost:/doc/html/boost_asio/reference/bind_allocator.html `net::bind_allocator`]
|
||||||
|
can be used whent he caller wants to assign a custom allocator to the operation.
|
||||||
|
|
||||||
|
|
||||||
|
A completion token's associated cancellation_slot can be used to cancel single
|
||||||
|
operations. This is often passed through by the completion token such as
|
||||||
|
[@boost:/doc/html/boost_asio/reference/use_awaitable.html `net::use_awaitable`]
|
||||||
|
or
|
||||||
|
[@boost:/doc/html/boost_asio/reference/yield_context.html `net::yield_context`]
|
||||||
|
.
|
||||||
|
|
||||||
|
The available [@boost:/doc/html/boost_asio/reference/cancellation_type.html cancellation types] are listed below.
|
||||||
|
|
||||||
|
# `terminal`
|
||||||
|
Requests cancellation where, following a successful cancellation,
|
||||||
|
the only safe operations on the I/O object are closure or destruction.
|
||||||
|
|
||||||
|
# `partial`
|
||||||
|
Requests cancellation where a successful cancellation may result in partial
|
||||||
|
side effects or no side effects. Following cancellation,
|
||||||
|
the I/O object is in a well-known state, and may be used for further operations.
|
||||||
|
|
||||||
|
# `total`
|
||||||
|
Requests cancellation where a successful cancellation results in no apparent side effects.
|
||||||
|
Following cancellation, the I/O object is in the same observable state as it was prior to the operation.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Networking prescribes facilities to determine the context in which
|
Networking prescribes facilities to determine the context in which
|
||||||
handlers run. Every I/O object refers to an __ExecutionContext__ for
|
handlers run. Every I/O object refers to an __ExecutionContext__ for
|
||||||
obtaining the __Executor__ instance used to invoke completion handlers.
|
obtaining the __Executor__ instance used to invoke completion handlers.
|
||||||
|
@ -77,6 +77,16 @@ class basic_stream<Executor>::read_op : public detail::stream_read_op_base
|
|||||||
return net::get_associated_allocator(h_);
|
return net::get_associated_allocator(h_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using cancellation_slot_type =
|
||||||
|
net::associated_cancellation_slot_t<Handler>;
|
||||||
|
|
||||||
|
cancellation_slot_type
|
||||||
|
get_cancellation_slot() const noexcept
|
||||||
|
{
|
||||||
|
return net::get_associated_cancellation_slot(h_,
|
||||||
|
net::cancellation_slot());
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
operator()(error_code ec)
|
operator()(error_code ec)
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <boost/beast/core/bind_handler.hpp>
|
#include <boost/beast/core/bind_handler.hpp>
|
||||||
#include <boost/beast/core/detail/allocator.hpp>
|
#include <boost/beast/core/detail/allocator.hpp>
|
||||||
#include <boost/beast/core/detail/async_base.hpp>
|
#include <boost/beast/core/detail/async_base.hpp>
|
||||||
|
#include <boost/beast/core/detail/filtering_cancellation_slot.hpp>
|
||||||
#include <boost/beast/core/detail/work_guard.hpp>
|
#include <boost/beast/core/detail/work_guard.hpp>
|
||||||
#include <boost/asio/associated_allocator.hpp>
|
#include <boost/asio/associated_allocator.hpp>
|
||||||
#include <boost/asio/associated_executor.hpp>
|
#include <boost/asio/associated_executor.hpp>
|
||||||
@ -187,7 +188,7 @@ class async_base
|
|||||||
|
|
||||||
Handler h_;
|
Handler h_;
|
||||||
detail::select_work_guard_t<Executor1> wg1_;
|
detail::select_work_guard_t<Executor1> wg1_;
|
||||||
|
net::cancellation_type act_{net::cancellation_type::terminal};
|
||||||
public:
|
public:
|
||||||
/** The type of executor associated with this object.
|
/** The type of executor associated with this object.
|
||||||
|
|
||||||
@ -308,6 +309,38 @@ public:
|
|||||||
h_, wg1_.get_executor());
|
h_, wg1_.get_executor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The type of cancellation_slot associated with this object.
|
||||||
|
|
||||||
|
If a class derived from @ref async_base is a completion
|
||||||
|
handler, then the associated cancellation_slot of the
|
||||||
|
derived class will be this type.
|
||||||
|
|
||||||
|
The default type is a filtering cancellation slot,
|
||||||
|
that only allows terminal cancellation.
|
||||||
|
*/
|
||||||
|
using cancellation_slot_type =
|
||||||
|
beast::detail::filtering_cancellation_slot<net::associated_cancellation_slot_t<Handler>>;
|
||||||
|
|
||||||
|
/** Returns the cancellation_slot associated with this object.
|
||||||
|
|
||||||
|
If a class derived from @ref async_base is a completion
|
||||||
|
handler, then the object returned from this function will be used
|
||||||
|
as the associated cancellation_slot of the derived class.
|
||||||
|
*/
|
||||||
|
cancellation_slot_type
|
||||||
|
get_cancellation_slot() const noexcept
|
||||||
|
{
|
||||||
|
return cancellation_slot_type(act_, net::get_associated_cancellation_slot(h_,
|
||||||
|
net::cancellation_slot()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the allowed cancellation types, default is `terminal`.
|
||||||
|
void set_allowed_cancellation(
|
||||||
|
net::cancellation_type allowed_cancellation_types = net::cancellation_type::terminal)
|
||||||
|
{
|
||||||
|
act_ = allowed_cancellation_types;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the handler associated with this object
|
/// Returns the handler associated with this object
|
||||||
Handler const&
|
Handler const&
|
||||||
handler() const noexcept
|
handler() const noexcept
|
||||||
|
@ -919,6 +919,17 @@ public:
|
|||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::partial
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
if they are also supported by the socket's @c async_connect operation.
|
||||||
|
|
||||||
@see async_connect
|
@see async_connect
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
@ -972,6 +983,18 @@ public:
|
|||||||
immediately or not, the handler will not be invoked from within
|
immediately or not, the handler will not be invoked from within
|
||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::partial
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
if they are also supported by the socket's @c async_connect operation.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class EndpointSequence,
|
class EndpointSequence,
|
||||||
@ -1064,6 +1087,17 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
@endcode
|
@endcode
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::partial
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
if they are also supported by the socket's @c async_connect operation.
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class EndpointSequence,
|
class EndpointSequence,
|
||||||
@ -1129,6 +1163,17 @@ public:
|
|||||||
immediately or not, the handler will not be invoked from within
|
immediately or not, the handler will not be invoked from within
|
||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::partial
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
if they are also supported by the socket's @c async_connect operation.
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class Iterator,
|
class Iterator,
|
||||||
@ -1189,6 +1234,17 @@ public:
|
|||||||
immediately or not, the handler will not be invoked from within
|
immediately or not, the handler will not be invoked from within
|
||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::partial
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
if they are also supported by the socket's @c async_connect operation.
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class Iterator,
|
class Iterator,
|
||||||
@ -1316,6 +1372,17 @@ public:
|
|||||||
number of bytes. Consider using the function `net::async_read` if you need
|
number of bytes. Consider using the function `net::async_read` if you need
|
||||||
to ensure that the requested amount of data is read before the asynchronous
|
to ensure that the requested amount of data is read before the asynchronous
|
||||||
operation completes.
|
operation completes.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::partial
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
if they are also supported by the socket's @c async_read_some operation.
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class MutableBufferSequence,
|
class MutableBufferSequence,
|
||||||
@ -1439,6 +1506,17 @@ public:
|
|||||||
number of bytes. Consider using the function `net::async_write` if you need
|
number of bytes. Consider using the function `net::async_write` if you need
|
||||||
to ensure that the requested amount of data is sent before the asynchronous
|
to ensure that the requested amount of data is sent before the asynchronous
|
||||||
operation completes.
|
operation completes.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::partial
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
if they are also supported by the socket's @c async_write_some operation.
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class ConstBufferSequence,
|
class ConstBufferSequence,
|
||||||
|
@ -67,6 +67,16 @@ public:
|
|||||||
h_, this->get());
|
h_, this->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using cancellation_slot_type =
|
||||||
|
net::associated_cancellation_slot_t<Handler>;
|
||||||
|
|
||||||
|
cancellation_slot_type
|
||||||
|
get_cancellation_slot() const noexcept
|
||||||
|
{
|
||||||
|
return net::get_associated_cancellation_slot(h_,
|
||||||
|
net::cancellation_slot());
|
||||||
|
}
|
||||||
|
|
||||||
// The allocation hooks are still defined because they trivially forward to
|
// The allocation hooks are still defined because they trivially forward to
|
||||||
// user hooks. Forward here ensures that the user will get a compile error
|
// user hooks. Forward here ensures that the user will get a compile error
|
||||||
// if they build their code with BOOST_ASIO_NO_DEPRECATED.
|
// if they build their code with BOOST_ASIO_NO_DEPRECATED.
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <boost/beast/core/error.hpp>
|
#include <boost/beast/core/error.hpp>
|
||||||
#include <boost/beast/core/detail/tuple.hpp>
|
#include <boost/beast/core/detail/tuple.hpp>
|
||||||
#include <boost/asio/associated_allocator.hpp>
|
#include <boost/asio/associated_allocator.hpp>
|
||||||
|
#include <boost/asio/associated_cancellation_slot.hpp>
|
||||||
#include <boost/asio/associated_executor.hpp>
|
#include <boost/asio/associated_executor.hpp>
|
||||||
#include <boost/asio/handler_alloc_hook.hpp>
|
#include <boost/asio/handler_alloc_hook.hpp>
|
||||||
#include <boost/asio/handler_continuation_hook.hpp>
|
#include <boost/asio/handler_continuation_hook.hpp>
|
||||||
@ -48,6 +49,9 @@ class bind_wrapper
|
|||||||
template<class T, class Allocator>
|
template<class T, class Allocator>
|
||||||
friend struct net::associated_allocator;
|
friend struct net::associated_allocator;
|
||||||
|
|
||||||
|
template<class T, class CancellationSlot>
|
||||||
|
friend struct net::associated_cancellation_slot;
|
||||||
|
|
||||||
template<class Arg, class Vals>
|
template<class Arg, class Vals>
|
||||||
static
|
static
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
@ -217,6 +221,10 @@ class bind_front_wrapper
|
|||||||
template<class T, class Allocator>
|
template<class T, class Allocator>
|
||||||
friend struct net::associated_allocator;
|
friend struct net::associated_allocator;
|
||||||
|
|
||||||
|
template<class T, class CancellationSlot>
|
||||||
|
friend struct net::associated_cancellation_slot;
|
||||||
|
|
||||||
|
|
||||||
template<std::size_t... I, class... Ts>
|
template<std::size_t... I, class... Ts>
|
||||||
void
|
void
|
||||||
invoke(
|
invoke(
|
||||||
@ -385,6 +393,42 @@ struct associated_allocator<
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class Handler, class... Args, class CancellationSlot>
|
||||||
|
struct associated_cancellation_slot<
|
||||||
|
beast::detail::bind_wrapper<Handler, Args...>, CancellationSlot>
|
||||||
|
{
|
||||||
|
using type = typename
|
||||||
|
associated_cancellation_slot<Handler>::type;
|
||||||
|
|
||||||
|
static
|
||||||
|
type
|
||||||
|
get(beast::detail::bind_wrapper<Handler, Args...> const& op,
|
||||||
|
CancellationSlot const& slot = CancellationSlot{}) noexcept
|
||||||
|
{
|
||||||
|
return associated_cancellation_slot<
|
||||||
|
Handler, CancellationSlot>::get(op.h_, slot);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Handler, class... Args, class CancellationSlot>
|
||||||
|
struct associated_cancellation_slot<
|
||||||
|
beast::detail::bind_front_wrapper<Handler, Args...>, CancellationSlot>
|
||||||
|
{
|
||||||
|
using type = typename
|
||||||
|
associated_cancellation_slot<Handler>::type;
|
||||||
|
|
||||||
|
static
|
||||||
|
type
|
||||||
|
get(beast::detail::bind_front_wrapper<Handler, Args...> const& op,
|
||||||
|
CancellationSlot const& slot = CancellationSlot{}) noexcept
|
||||||
|
{
|
||||||
|
return associated_cancellation_slot<
|
||||||
|
Handler, CancellationSlot>::get(op.h_, slot);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // asio
|
} // asio
|
||||||
} // boost
|
} // boost
|
||||||
|
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BOOST_BEAST_CORE_DETAIL_FILTERING_CANCELLATION_SLOT_HPP
|
||||||
|
#define BOOST_BEAST_CORE_DETAIL_FILTERING_CANCELLATION_SLOT_HPP
|
||||||
|
|
||||||
|
#include <boost/beast/core/detail/config.hpp>
|
||||||
|
#include <boost/asio/cancellation_signal.hpp>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<typename CancellationSlot = net::cancellation_slot>
|
||||||
|
struct filtering_cancellation_slot : CancellationSlot
|
||||||
|
{
|
||||||
|
template<typename ... Args>
|
||||||
|
filtering_cancellation_slot(net::cancellation_type type, Args && ... args)
|
||||||
|
: CancellationSlot(std::forward<Args>(args)...), type(type) {}
|
||||||
|
|
||||||
|
net::cancellation_type type = net::cancellation_type::terminal;
|
||||||
|
|
||||||
|
using CancellationSlot::operator=;
|
||||||
|
|
||||||
|
template<typename Handler>
|
||||||
|
struct handler_wrapper
|
||||||
|
{
|
||||||
|
Handler handler;
|
||||||
|
const net::cancellation_type type;
|
||||||
|
|
||||||
|
template<typename ... Args>
|
||||||
|
handler_wrapper(net::cancellation_type type, Args && ... args)
|
||||||
|
: handler(std::forward<Args>(args)...),
|
||||||
|
type(type) {}
|
||||||
|
|
||||||
|
void operator()(net::cancellation_type tp)
|
||||||
|
{
|
||||||
|
if ((tp & type) != net::cancellation_type::none)
|
||||||
|
handler(tp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename CancellationHandler, typename ... Args>
|
||||||
|
CancellationHandler& emplace(Args && ... args)
|
||||||
|
{
|
||||||
|
return CancellationSlot::template emplace<handler_wrapper<CancellationHandler>>(
|
||||||
|
type, std::forward<Args>(args)...).handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CancellationHandler>
|
||||||
|
CancellationHandler& assign(CancellationHandler && ch)
|
||||||
|
{
|
||||||
|
return CancellationSlot::template emplace<handler_wrapper<CancellationHandler>>(
|
||||||
|
type, std::forward<CancellationHandler>(ch)).handler;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //BOOST_BEAST_CORE_DETAIL_FILTERING_CANCELLATION_SLOT_HPP
|
@ -283,6 +283,7 @@ public:
|
|||||||
, pg_()
|
, pg_()
|
||||||
, b_(b)
|
, b_(b)
|
||||||
{
|
{
|
||||||
|
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||||
if (buffer_bytes(b_) == 0 && state().pending)
|
if (buffer_bytes(b_) == 0 && state().pending)
|
||||||
{
|
{
|
||||||
// Workaround:
|
// Workaround:
|
||||||
@ -452,6 +453,7 @@ public:
|
|||||||
, pg0_(impl_->read.pending)
|
, pg0_(impl_->read.pending)
|
||||||
, pg1_(impl_->write.pending)
|
, pg1_(impl_->write.pending)
|
||||||
{
|
{
|
||||||
|
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||||
if(state().timer.expiry() != stream_base::never())
|
if(state().timer.expiry() != stream_base::never())
|
||||||
{
|
{
|
||||||
BOOST_ASIO_HANDLER_LOCATION((
|
BOOST_ASIO_HANDLER_LOCATION((
|
||||||
@ -489,6 +491,7 @@ public:
|
|||||||
, pg0_(impl_->read.pending)
|
, pg0_(impl_->read.pending)
|
||||||
, pg1_(impl_->write.pending)
|
, pg1_(impl_->write.pending)
|
||||||
{
|
{
|
||||||
|
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||||
if(state().timer.expiry() != stream_base::never())
|
if(state().timer.expiry() != stream_base::never())
|
||||||
{
|
{
|
||||||
BOOST_ASIO_HANDLER_LOCATION((
|
BOOST_ASIO_HANDLER_LOCATION((
|
||||||
@ -526,6 +529,7 @@ public:
|
|||||||
, pg0_(impl_->read.pending)
|
, pg0_(impl_->read.pending)
|
||||||
, pg1_(impl_->write.pending)
|
, pg1_(impl_->write.pending)
|
||||||
{
|
{
|
||||||
|
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||||
if(state().timer.expiry() != stream_base::never())
|
if(state().timer.expiry() != stream_base::never())
|
||||||
{
|
{
|
||||||
BOOST_ASIO_HANDLER_LOCATION((
|
BOOST_ASIO_HANDLER_LOCATION((
|
||||||
|
@ -12,7 +12,9 @@
|
|||||||
|
|
||||||
#include <boost/beast/core/detail/allocator.hpp>
|
#include <boost/beast/core/detail/allocator.hpp>
|
||||||
#include <boost/asio/associated_allocator.hpp>
|
#include <boost/asio/associated_allocator.hpp>
|
||||||
|
#include <boost/asio/associated_cancellation_slot.hpp>
|
||||||
#include <boost/asio/associated_executor.hpp>
|
#include <boost/asio/associated_executor.hpp>
|
||||||
|
#include <boost/asio/error.hpp>
|
||||||
#include <boost/asio/executor_work_guard.hpp>
|
#include <boost/asio/executor_work_guard.hpp>
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
#include <boost/core/empty_value.hpp>
|
#include <boost/core/empty_value.hpp>
|
||||||
@ -28,9 +30,10 @@ class saved_handler::base
|
|||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
~base() = default;
|
~base() = default;
|
||||||
|
saved_handler * owner_;
|
||||||
public:
|
public:
|
||||||
base() = default;
|
base(saved_handler * owner) : owner_(owner){}
|
||||||
|
void set_owner(saved_handler * new_owner) { owner_ = new_owner;}
|
||||||
virtual void destroy() = 0;
|
virtual void destroy() = 0;
|
||||||
virtual void invoke() = 0;
|
virtual void invoke() = 0;
|
||||||
};
|
};
|
||||||
@ -72,11 +75,12 @@ class saved_handler::impl final : public base
|
|||||||
net::executor_work_guard<
|
net::executor_work_guard<
|
||||||
net::associated_executor_t<Handler>> wg2_;
|
net::associated_executor_t<Handler>> wg2_;
|
||||||
#endif // defined(BOOST_ASIO_NO_TS_EXECUTORS)
|
#endif // defined(BOOST_ASIO_NO_TS_EXECUTORS)
|
||||||
|
net::cancellation_slot slot_{net::get_associated_cancellation_slot(v_.h)};
|
||||||
public:
|
public:
|
||||||
template<class Handler_>
|
template<class Handler_>
|
||||||
impl(alloc_type const& a, Handler_&& h)
|
impl(alloc_type const& a, Handler_&& h,
|
||||||
: v_(a, std::forward<Handler_>(h))
|
saved_handler * owner)
|
||||||
|
: base(owner), v_(a, std::forward<Handler_>(h))
|
||||||
#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
|
#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
|
||||||
, wg2_(net::prefer(
|
, wg2_(net::prefer(
|
||||||
net::get_associated_executor(v_.h),
|
net::get_associated_executor(v_.h),
|
||||||
@ -87,10 +91,15 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~impl()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
destroy() override
|
destroy() override
|
||||||
{
|
{
|
||||||
auto v = std::move(v_);
|
auto v = std::move(v_);
|
||||||
|
slot_.clear();
|
||||||
alloc_traits::destroy(v.get(), this);
|
alloc_traits::destroy(v.get(), this);
|
||||||
alloc_traits::deallocate(v.get(), this, 1);
|
alloc_traits::deallocate(v.get(), this, 1);
|
||||||
}
|
}
|
||||||
@ -98,11 +107,22 @@ public:
|
|||||||
void
|
void
|
||||||
invoke() override
|
invoke() override
|
||||||
{
|
{
|
||||||
|
slot_.clear();
|
||||||
auto v = std::move(v_);
|
auto v = std::move(v_);
|
||||||
alloc_traits::destroy(v.get(), this);
|
alloc_traits::destroy(v.get(), this);
|
||||||
alloc_traits::deallocate(v.get(), this, 1);
|
alloc_traits::deallocate(v.get(), this, 1);
|
||||||
v.h();
|
v.h();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void self_complete()
|
||||||
|
{
|
||||||
|
slot_.clear();
|
||||||
|
owner_->p_ = nullptr;
|
||||||
|
auto v = std::move(v_);
|
||||||
|
alloc_traits::destroy(v.get(), this);
|
||||||
|
alloc_traits::deallocate(v.get(), this, 1);
|
||||||
|
v.h(net::error::operation_aborted);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@ -110,7 +130,8 @@ public:
|
|||||||
template<class Handler, class Allocator>
|
template<class Handler, class Allocator>
|
||||||
void
|
void
|
||||||
saved_handler::
|
saved_handler::
|
||||||
emplace(Handler&& handler, Allocator const& alloc)
|
emplace(Handler&& handler, Allocator const& alloc,
|
||||||
|
net::cancellation_type cancel_type)
|
||||||
{
|
{
|
||||||
// Can't delete a handler before invoking
|
// Can't delete a handler before invoking
|
||||||
BOOST_ASSERT(! has_value());
|
BOOST_ASSERT(! has_value());
|
||||||
@ -140,22 +161,47 @@ emplace(Handler&& handler, Allocator const& alloc)
|
|||||||
alloc_traits::deallocate(a, p, 1);
|
alloc_traits::deallocate(a, p, 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
auto cancel_slot = net::get_associated_cancellation_slot(handler);
|
||||||
storage s(alloc);
|
storage s(alloc);
|
||||||
alloc_traits::construct(s.a, s.p,
|
alloc_traits::construct(s.a, s.p,
|
||||||
s.a, std::forward<Handler>(handler));
|
s.a, std::forward<Handler>(handler), this);
|
||||||
p_ = boost::exchange(s.p, nullptr);
|
|
||||||
|
auto tmp = boost::exchange(s.p, nullptr);
|
||||||
|
p_ = tmp;
|
||||||
|
|
||||||
|
if (cancel_slot.is_connected())
|
||||||
|
{
|
||||||
|
struct cancel_op
|
||||||
|
{
|
||||||
|
impl<Handler, Allocator>* p;
|
||||||
|
net::cancellation_type accepted_ct;
|
||||||
|
cancel_op(impl<Handler, Allocator>* p,
|
||||||
|
net::cancellation_type accepted_ct)
|
||||||
|
: p(p), accepted_ct(accepted_ct) {}
|
||||||
|
|
||||||
|
void operator()(net::cancellation_type ct)
|
||||||
|
{
|
||||||
|
if ((ct & accepted_ct) != net::cancellation_type::none)
|
||||||
|
p->self_complete();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cancel_slot.template emplace<cancel_op>(tmp, cancel_type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Handler>
|
template<class Handler>
|
||||||
void
|
void
|
||||||
saved_handler::
|
saved_handler::
|
||||||
emplace(Handler&& handler)
|
emplace(Handler&& handler, net::cancellation_type cancel_type)
|
||||||
{
|
{
|
||||||
// Can't delete a handler before invoking
|
// Can't delete a handler before invoking
|
||||||
BOOST_ASSERT(! has_value());
|
BOOST_ASSERT(! has_value());
|
||||||
emplace(
|
emplace(
|
||||||
std::forward<Handler>(handler),
|
std::forward<Handler>(handler),
|
||||||
net::get_associated_allocator(handler));
|
net::get_associated_allocator(handler),
|
||||||
|
cancel_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // beast
|
} // beast
|
||||||
|
@ -27,6 +27,7 @@ saved_handler::
|
|||||||
saved_handler(saved_handler&& other) noexcept
|
saved_handler(saved_handler&& other) noexcept
|
||||||
: p_(boost::exchange(other.p_, nullptr))
|
: p_(boost::exchange(other.p_, nullptr))
|
||||||
{
|
{
|
||||||
|
p_->set_owner(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
saved_handler&
|
saved_handler&
|
||||||
@ -36,6 +37,7 @@ operator=(saved_handler&& other) noexcept
|
|||||||
// Can't delete a handler before invoking
|
// Can't delete a handler before invoking
|
||||||
BOOST_ASSERT(! has_value());
|
BOOST_ASSERT(! has_value());
|
||||||
p_ = boost::exchange(other.p_, nullptr);
|
p_ = boost::exchange(other.p_, nullptr);
|
||||||
|
p_->set_owner(this);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#define BOOST_BEAST_CORE_SAVED_HANDLER_HPP
|
#define BOOST_BEAST_CORE_SAVED_HANDLER_HPP
|
||||||
|
|
||||||
#include <boost/beast/core/detail/config.hpp>
|
#include <boost/beast/core/detail/config.hpp>
|
||||||
|
#include <boost/asio/cancellation_type.hpp>
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
namespace beast {
|
namespace beast {
|
||||||
@ -72,10 +73,13 @@ public:
|
|||||||
The implementation takes ownership of the handler by performing a decay-copy.
|
The implementation takes ownership of the handler by performing a decay-copy.
|
||||||
|
|
||||||
@param alloc The allocator to use.
|
@param alloc The allocator to use.
|
||||||
|
|
||||||
|
@param cancel_type The type of cancellation allowed to complete this op.
|
||||||
*/
|
*/
|
||||||
template<class Handler, class Allocator>
|
template<class Handler, class Allocator>
|
||||||
void
|
void
|
||||||
emplace(Handler&& handler, Allocator const& alloc);
|
emplace(Handler&& handler, Allocator const& alloc,
|
||||||
|
net::cancellation_type cancel_type = net::cancellation_type::terminal);
|
||||||
|
|
||||||
/** Store a completion handler in the container.
|
/** Store a completion handler in the container.
|
||||||
|
|
||||||
@ -85,10 +89,13 @@ public:
|
|||||||
|
|
||||||
@param handler The completion handler to store.
|
@param handler The completion handler to store.
|
||||||
The implementation takes ownership of the handler by performing a decay-copy.
|
The implementation takes ownership of the handler by performing a decay-copy.
|
||||||
|
|
||||||
|
@param cancel_type The type of cancellation allowed to complete this op.
|
||||||
*/
|
*/
|
||||||
template<class Handler>
|
template<class Handler>
|
||||||
void
|
void
|
||||||
emplace(Handler&& handler);
|
emplace(Handler&& handler,
|
||||||
|
net::cancellation_type cancel_type = net::cancellation_type::terminal);
|
||||||
|
|
||||||
/** Discard the saved handler, if one exists.
|
/** Discard the saved handler, if one exists.
|
||||||
|
|
||||||
|
@ -178,8 +178,18 @@ class write_op
|
|||||||
Stream& s_;
|
Stream& s_;
|
||||||
serializer<isRequest, Body, Fields>& sr_;
|
serializer<isRequest, Body, Fields>& sr_;
|
||||||
std::size_t bytes_transferred_ = 0;
|
std::size_t bytes_transferred_ = 0;
|
||||||
|
net::cancellation_state st_{this->
|
||||||
|
beast::async_base<Handler, beast::executor_type<Stream>>
|
||||||
|
::get_cancellation_slot()};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
using cancellation_slot_type = net::cancellation_slot;
|
||||||
|
cancellation_slot_type get_cancellation_slot() const noexcept
|
||||||
|
{
|
||||||
|
return st_.slot();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template<class Handler_>
|
template<class Handler_>
|
||||||
write_op(
|
write_op(
|
||||||
Handler_&& h,
|
Handler_&& h,
|
||||||
@ -227,6 +237,8 @@ public:
|
|||||||
s_, sr_, std::move(*this));
|
s_, sr_, std::move(*this));
|
||||||
}
|
}
|
||||||
bytes_transferred_ += bytes_transferred;
|
bytes_transferred_ += bytes_transferred;
|
||||||
|
if (!ec && st_.cancelled() != net::cancellation_type::none)
|
||||||
|
ec = net::error::operation_aborted;
|
||||||
if(ec)
|
if(ec)
|
||||||
goto upcall;
|
goto upcall;
|
||||||
if(Predicate{}(sr_))
|
if(Predicate{}(sr_))
|
||||||
|
@ -200,6 +200,19 @@ read_some(
|
|||||||
@note The completion handler will receive as a parameter the total number
|
@note The completion handler will receive as a parameter the total number
|
||||||
of bytes transferred from the stream. This may be zero for the case where
|
of bytes transferred from the stream. This may be zero for the case where
|
||||||
there is sufficient pre-existing message data in the dynamic buffer.
|
there is sufficient pre-existing message data in the dynamic buffer.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
|
||||||
|
if the `stream` also supports terminal cancellation.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class AsyncReadStream,
|
class AsyncReadStream,
|
||||||
@ -397,6 +410,19 @@ read_header(
|
|||||||
there is sufficient pre-existing message data in the dynamic buffer. The
|
there is sufficient pre-existing message data in the dynamic buffer. The
|
||||||
implementation will call @ref basic_parser::eager with the value `false`
|
implementation will call @ref basic_parser::eager with the value `false`
|
||||||
on the parser passed in.
|
on the parser passed in.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
|
||||||
|
if the `stream` also supports terminal cancellation.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class AsyncReadStream,
|
class AsyncReadStream,
|
||||||
@ -594,6 +620,19 @@ read(
|
|||||||
there is sufficient pre-existing message data in the dynamic buffer. The
|
there is sufficient pre-existing message data in the dynamic buffer. The
|
||||||
implementation will call @ref basic_parser::eager with the value `true`
|
implementation will call @ref basic_parser::eager with the value `true`
|
||||||
on the parser passed in.
|
on the parser passed in.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
|
||||||
|
if the `stream` also supports terminal cancellation.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class AsyncReadStream,
|
class AsyncReadStream,
|
||||||
@ -800,6 +839,19 @@ read(
|
|||||||
there is sufficient pre-existing message data in the dynamic buffer. The
|
there is sufficient pre-existing message data in the dynamic buffer. The
|
||||||
implementation will call @ref basic_parser::eager with the value `true`
|
implementation will call @ref basic_parser::eager with the value `true`
|
||||||
on the parser passed in.
|
on the parser passed in.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
|
||||||
|
if the `stream` also supports terminal cancellation.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class AsyncReadStream,
|
class AsyncReadStream,
|
||||||
|
@ -161,6 +161,18 @@ write_some(
|
|||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
|
||||||
|
if the `stream` also supports terminal cancellation.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
|
|
||||||
@see serializer
|
@see serializer
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
@ -291,6 +303,18 @@ write_header(
|
|||||||
@note The implementation will call @ref serializer::split with
|
@note The implementation will call @ref serializer::split with
|
||||||
the value `true` on the serializer passed in.
|
the value `true` on the serializer passed in.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
|
||||||
|
if the `stream` also supports terminal cancellation.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
|
|
||||||
@see serializer
|
@see serializer
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
@ -412,6 +436,18 @@ write(
|
|||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
|
||||||
|
if the `stream` also supports terminal cancellation.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
|
|
||||||
@see serializer
|
@see serializer
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
@ -637,6 +673,18 @@ write(
|
|||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
|
||||||
|
if the `stream` also supports terminal cancellation.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
|
|
||||||
@see message
|
@see message
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
@ -699,6 +747,18 @@ async_write(
|
|||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
|
||||||
|
if the `stream` also supports terminal cancellation.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
|
|
||||||
@see message
|
@see message
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
|
@ -266,6 +266,9 @@ public:
|
|||||||
|
|
||||||
// read and respond to an upgrade request
|
// read and respond to an upgrade request
|
||||||
//
|
//
|
||||||
|
// Cancellation: the async_accept cancellation can be terminal
|
||||||
|
// because it will just interrupt the reading of the header.
|
||||||
|
//
|
||||||
template<class NextLayer, bool deflateSupported>
|
template<class NextLayer, bool deflateSupported>
|
||||||
template<class Handler, class Decorator>
|
template<class Handler, class Decorator>
|
||||||
class stream<NextLayer, deflateSupported>::accept_op
|
class stream<NextLayer, deflateSupported>::accept_op
|
||||||
|
@ -90,9 +90,15 @@ public:
|
|||||||
BOOST_ASIO_HANDLER_LOCATION((
|
BOOST_ASIO_HANDLER_LOCATION((
|
||||||
__FILE__, __LINE__,
|
__FILE__, __LINE__,
|
||||||
"websocket::async_close"));
|
"websocket::async_close"));
|
||||||
|
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||||
impl.op_close.emplace(std::move(*this));
|
impl.op_close.emplace(std::move(*this),
|
||||||
|
net::cancellation_type::all);
|
||||||
}
|
}
|
||||||
|
// cancel fired before we could do anything.
|
||||||
|
if (ec == net::error::operation_aborted)
|
||||||
|
return this->complete(cont, ec);
|
||||||
|
this->set_allowed_cancellation(net::cancellation_type::terminal);
|
||||||
|
|
||||||
impl.wr_block.lock(this);
|
impl.wr_block.lock(this);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
{
|
{
|
||||||
@ -143,9 +149,17 @@ public:
|
|||||||
BOOST_ASIO_HANDLER_LOCATION((
|
BOOST_ASIO_HANDLER_LOCATION((
|
||||||
__FILE__, __LINE__,
|
__FILE__, __LINE__,
|
||||||
"websocket::async_close"));
|
"websocket::async_close"));
|
||||||
|
// terminal only, that's the default
|
||||||
impl.op_r_close.emplace(std::move(*this));
|
impl.op_r_close.emplace(std::move(*this));
|
||||||
}
|
}
|
||||||
|
if (ec == net::error::operation_aborted)
|
||||||
|
{
|
||||||
|
// if a cancellation fires here, we do a dirty shutdown
|
||||||
|
impl.change_status(status::closed);
|
||||||
|
close_socket(get_lowest_layer(impl.stream()));
|
||||||
|
return this->complete(cont, ec);
|
||||||
|
}
|
||||||
|
|
||||||
impl.rd_block.lock(this);
|
impl.rd_block.lock(this);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
{
|
{
|
||||||
@ -185,7 +199,7 @@ public:
|
|||||||
beast::detail::bind_continuation(std::move(*this)));
|
beast::detail::bind_continuation(std::move(*this)));
|
||||||
}
|
}
|
||||||
impl.rd_buf.commit(bytes_transferred);
|
impl.rd_buf.commit(bytes_transferred);
|
||||||
if(impl.check_stop_now(ec))
|
if(impl.check_stop_now(ec)) //< this catches cancellation
|
||||||
goto upcall;
|
goto upcall;
|
||||||
}
|
}
|
||||||
if(detail::is_control(impl.rd_fh.op))
|
if(detail::is_control(impl.rd_fh.op))
|
||||||
|
@ -86,9 +86,12 @@ public:
|
|||||||
BOOST_ASIO_HANDLER_LOCATION((
|
BOOST_ASIO_HANDLER_LOCATION((
|
||||||
__FILE__, __LINE__,
|
__FILE__, __LINE__,
|
||||||
"websocket::async_ping"));
|
"websocket::async_ping"));
|
||||||
|
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||||
impl.op_ping.emplace(std::move(*this));
|
impl.op_ping.emplace(std::move(*this), net::cancellation_type::all);
|
||||||
}
|
}
|
||||||
|
if (ec)
|
||||||
|
return this->complete(cont, ec);
|
||||||
|
this->set_allowed_cancellation(net::cancellation_type::terminal);
|
||||||
impl.wr_block.lock(this);
|
impl.wr_block.lock(this);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
{
|
{
|
||||||
|
@ -107,8 +107,14 @@ public:
|
|||||||
__FILE__, __LINE__,
|
__FILE__, __LINE__,
|
||||||
"websocket::async_read_some"));
|
"websocket::async_read_some"));
|
||||||
|
|
||||||
impl.op_r_rd.emplace(std::move(*this));
|
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||||
|
impl.op_r_rd.emplace(std::move(*this), net::cancellation_type::all);
|
||||||
}
|
}
|
||||||
|
if (ec)
|
||||||
|
return this->complete(cont, ec, bytes_written_);
|
||||||
|
|
||||||
|
this->set_allowed_cancellation(net::cancellation_type::terminal);
|
||||||
|
|
||||||
impl.rd_block.lock(this);
|
impl.rd_block.lock(this);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
{
|
{
|
||||||
@ -275,6 +281,9 @@ public:
|
|||||||
|
|
||||||
impl.op_rd.emplace(std::move(*this));
|
impl.op_rd.emplace(std::move(*this));
|
||||||
}
|
}
|
||||||
|
if (ec)
|
||||||
|
return this->complete(cont, ec, bytes_written_);
|
||||||
|
|
||||||
impl.wr_block.lock(this);
|
impl.wr_block.lock(this);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
{
|
{
|
||||||
@ -629,6 +638,9 @@ public:
|
|||||||
|
|
||||||
impl.op_rd.emplace(std::move(*this));
|
impl.op_rd.emplace(std::move(*this));
|
||||||
}
|
}
|
||||||
|
if (ec)
|
||||||
|
return this->complete(cont, ec, bytes_written_);
|
||||||
|
|
||||||
impl.wr_block.lock(this);
|
impl.wr_block.lock(this);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
{
|
{
|
||||||
|
@ -66,6 +66,8 @@ struct ssl_shutdown_op
|
|||||||
{
|
{
|
||||||
BOOST_ASIO_CORO_REENTER(*this)
|
BOOST_ASIO_CORO_REENTER(*this)
|
||||||
{
|
{
|
||||||
|
self.reset_cancellation_state(net::enable_total_cancellation());
|
||||||
|
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
s_.async_shutdown(std::move(self));
|
s_.async_shutdown(std::move(self));
|
||||||
ec_ = ec;
|
ec_ = ec;
|
||||||
|
@ -59,6 +59,7 @@ public:
|
|||||||
, nb_(false)
|
, nb_(false)
|
||||||
{
|
{
|
||||||
(*this)({}, 0, false);
|
(*this)({}, 0, false);
|
||||||
|
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -181,9 +181,14 @@ operator()(
|
|||||||
"websocket::async_write" :
|
"websocket::async_write" :
|
||||||
"websocket::async_write_some"
|
"websocket::async_write_some"
|
||||||
));
|
));
|
||||||
|
this->set_allowed_cancellation(net::cancellation_type::all);
|
||||||
impl.op_wr.emplace(std::move(*this));
|
impl.op_wr.emplace(std::move(*this),
|
||||||
|
net::cancellation_type::all);
|
||||||
}
|
}
|
||||||
|
if (ec)
|
||||||
|
return this->complete(cont, ec, bytes_transferred_);
|
||||||
|
|
||||||
|
this->set_allowed_cancellation(net::cancellation_type::terminal);
|
||||||
impl.wr_block.lock(this);
|
impl.wr_block.lock(this);
|
||||||
BOOST_ASIO_CORO_YIELD
|
BOOST_ASIO_CORO_YIELD
|
||||||
{
|
{
|
||||||
|
@ -68,6 +68,18 @@ teardown(
|
|||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::partial
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
if they are also supported by the socket's @c async_teardown
|
||||||
|
and @c async_shutdown operation.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
template<class AsyncStream, class TeardownHandler>
|
template<class AsyncStream, class TeardownHandler>
|
||||||
void
|
void
|
||||||
|
@ -1602,6 +1602,20 @@ public:
|
|||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||||
|
control operations such as a ping/pong.
|
||||||
|
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||||
|
|
||||||
|
@note `terminal` cancellation will may close the underlying socket.
|
||||||
|
|
||||||
@see
|
@see
|
||||||
@li <a href="https://tools.ietf.org/html/rfc6455#section-7.1.2">Websocket Closing Handshake (RFC6455)</a>
|
@li <a href="https://tools.ietf.org/html/rfc6455#section-7.1.2">Websocket Closing Handshake (RFC6455)</a>
|
||||||
*/
|
*/
|
||||||
@ -1706,6 +1720,21 @@ public:
|
|||||||
immediately or not, the handler will not be invoked from within
|
immediately or not, the handler will not be invoked from within
|
||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||||
|
control operations such as a ping/pong.
|
||||||
|
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
BOOST_BEAST_ASYNC_TPARAM1 WriteHandler =
|
BOOST_BEAST_ASYNC_TPARAM1 WriteHandler =
|
||||||
@ -1814,6 +1843,21 @@ public:
|
|||||||
immediately or not, the handler will not be invoked from within
|
immediately or not, the handler will not be invoked from within
|
||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||||
|
control operations such as a ping/pong.
|
||||||
|
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
BOOST_BEAST_ASYNC_TPARAM1 WriteHandler =
|
BOOST_BEAST_ASYNC_TPARAM1 WriteHandler =
|
||||||
@ -1975,6 +2019,21 @@ public:
|
|||||||
immediately or not, the handler will not be invoked from within
|
immediately or not, the handler will not be invoked from within
|
||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||||
|
control operations such as a ping/pong.
|
||||||
|
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class DynamicBuffer,
|
class DynamicBuffer,
|
||||||
@ -2154,6 +2213,21 @@ public:
|
|||||||
immediately or not, the handler will not be invoked from within
|
immediately or not, the handler will not be invoked from within
|
||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||||
|
control operations such as a ping/pong.
|
||||||
|
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class DynamicBuffer,
|
class DynamicBuffer,
|
||||||
@ -2258,6 +2332,21 @@ public:
|
|||||||
from the beginning.
|
from the beginning.
|
||||||
|
|
||||||
@param ec Set to indicate what error occurred, if any.
|
@param ec Set to indicate what error occurred, if any.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||||
|
control operations such as a ping/pong.
|
||||||
|
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
*/
|
*/
|
||||||
template<class MutableBufferSequence>
|
template<class MutableBufferSequence>
|
||||||
std::size_t
|
std::size_t
|
||||||
@ -2329,6 +2418,22 @@ public:
|
|||||||
immediately or not, the handler will not be invoked from within
|
immediately or not, the handler will not be invoked from within
|
||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||||
|
control operations such as a ping/pong.
|
||||||
|
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class MutableBufferSequence,
|
class MutableBufferSequence,
|
||||||
@ -2452,6 +2557,21 @@ public:
|
|||||||
immediately or not, the handler will not be invoked from within
|
immediately or not, the handler will not be invoked from within
|
||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||||
|
control operations such as a ping/pong.
|
||||||
|
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class ConstBufferSequence,
|
class ConstBufferSequence,
|
||||||
@ -2490,6 +2610,7 @@ public:
|
|||||||
@return The number of bytes sent from the buffers.
|
@return The number of bytes sent from the buffers.
|
||||||
|
|
||||||
@throws system_error Thrown on failure.
|
@throws system_error Thrown on failure.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
template<class ConstBufferSequence>
|
template<class ConstBufferSequence>
|
||||||
std::size_t
|
std::size_t
|
||||||
@ -2575,6 +2696,21 @@ public:
|
|||||||
immediately or not, the handler will not be invoked from within
|
immediately or not, the handler will not be invoked from within
|
||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
`total` cancellation succeeds if the operation is suspended due to ongoing
|
||||||
|
control operations such as a ping/pong.
|
||||||
|
`terminal` cancellation succeeds when supported by the underlying stream.
|
||||||
|
|
||||||
|
`terminal` cancellation leaves the stream in an undefined state,
|
||||||
|
so that only closing it is guaranteed to succeed.
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class ConstBufferSequence,
|
class ConstBufferSequence,
|
||||||
|
@ -163,6 +163,17 @@ teardown(
|
|||||||
this function. Invocation of the handler will be performed in a
|
this function. Invocation of the handler will be performed in a
|
||||||
manner equivalent to using `net::post`.
|
manner equivalent to using `net::post`.
|
||||||
|
|
||||||
|
@par Per-Operation Cancellation
|
||||||
|
|
||||||
|
This asynchronous operation supports cancellation for the following
|
||||||
|
net::cancellation_type values:
|
||||||
|
|
||||||
|
@li @c net::cancellation_type::terminal
|
||||||
|
@li @c net::cancellation_type::partial
|
||||||
|
@li @c net::cancellation_type::total
|
||||||
|
|
||||||
|
if they are also supported by the socket's @c async_wait operation.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
template<
|
template<
|
||||||
class Protocol, class Executor,
|
class Protocol, class Executor,
|
||||||
|
@ -48,6 +48,7 @@ add_executable (tests-beast-core
|
|||||||
file_posix.cpp
|
file_posix.cpp
|
||||||
file_stdio.cpp
|
file_stdio.cpp
|
||||||
file_win32.cpp
|
file_win32.cpp
|
||||||
|
filtering_cancellation_slot.cpp
|
||||||
flat_buffer.cpp
|
flat_buffer.cpp
|
||||||
flat_static_buffer.cpp
|
flat_static_buffer.cpp
|
||||||
flat_stream.cpp
|
flat_stream.cpp
|
||||||
|
@ -39,6 +39,7 @@ local SOURCES =
|
|||||||
file_posix.cpp
|
file_posix.cpp
|
||||||
file_stdio.cpp
|
file_stdio.cpp
|
||||||
file_win32.cpp
|
file_win32.cpp
|
||||||
|
filtering_cancellation_slot.cpp
|
||||||
flat_buffer.cpp
|
flat_buffer.cpp
|
||||||
flat_static_buffer.cpp
|
flat_static_buffer.cpp
|
||||||
flat_stream.cpp
|
flat_stream.cpp
|
||||||
|
48
test/beast/core/filtering_cancellation_slot.cpp
Normal file
48
test/beast/core/filtering_cancellation_slot.cpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
// Test that header file is self-contained.
|
||||||
|
#include <boost/beast/core/detail/filtering_cancellation_slot.hpp>
|
||||||
|
|
||||||
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
|
|
||||||
|
struct filtering_cancellation_slot_test : beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
run()
|
||||||
|
{
|
||||||
|
using ct = net::cancellation_type;
|
||||||
|
ct fired = ct::none;
|
||||||
|
auto l = [&fired](ct tp){fired = tp;};
|
||||||
|
|
||||||
|
net::cancellation_signal sl;
|
||||||
|
|
||||||
|
detail::filtering_cancellation_slot<> slot{ct::terminal, sl.slot()};
|
||||||
|
slot.type |= ct::total;
|
||||||
|
slot = sl.slot();
|
||||||
|
|
||||||
|
slot.assign(l);
|
||||||
|
|
||||||
|
BEAST_EXPECT(fired == ct::none);
|
||||||
|
sl.emit(ct::total);
|
||||||
|
BEAST_EXPECT(fired == ct::total);
|
||||||
|
sl.emit(ct::partial);
|
||||||
|
BEAST_EXPECT(fired == ct::total);
|
||||||
|
sl.emit(ct::terminal);
|
||||||
|
BEAST_EXPECT(fired == ct::terminal);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(beast,core,filtering_cancellation_slot);
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
} // boost
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
// Test that header file is self-contained.
|
// Test that header file is self-contained.
|
||||||
#include <boost/beast/core/saved_handler.hpp>
|
#include <boost/beast/core/saved_handler.hpp>
|
||||||
|
#include <boost/asio/bind_cancellation_slot.hpp>
|
||||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
operator()()
|
operator()(system::error_code ec_ = {})
|
||||||
{
|
{
|
||||||
failed_ = false;
|
failed_ = false;
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
operator()()
|
operator()(system::error_code = {})
|
||||||
{
|
{
|
||||||
invoked_ = true;
|
invoked_ = true;
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
operator()()
|
operator()(system::error_code = {})
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -131,10 +131,88 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testSavedHandlerCancellation()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
net::cancellation_signal sig;
|
||||||
|
|
||||||
|
saved_handler sh;
|
||||||
|
BEAST_EXPECT(! sh.has_value());
|
||||||
|
|
||||||
|
sh.emplace(
|
||||||
|
net::bind_cancellation_slot(
|
||||||
|
sig.slot(), handler{}));
|
||||||
|
BEAST_EXPECT(sh.has_value());
|
||||||
|
BEAST_EXPECT(sig.slot().has_handler());
|
||||||
|
sig.emit(net::cancellation_type::all);
|
||||||
|
BEAST_EXPECT(! sh.has_value());
|
||||||
|
BEAST_EXPECT(!sig.slot().has_handler());
|
||||||
|
|
||||||
|
|
||||||
|
sh.emplace(
|
||||||
|
net::bind_cancellation_slot(
|
||||||
|
sig.slot(), handler{}));
|
||||||
|
BEAST_EXPECT(sh.has_value());
|
||||||
|
BEAST_EXPECT(sig.slot().has_handler());
|
||||||
|
sig.emit(net::cancellation_type::total);
|
||||||
|
BEAST_EXPECT(sh.has_value());
|
||||||
|
BEAST_EXPECT(sig.slot().has_handler());
|
||||||
|
sig.emit(net::cancellation_type::terminal);
|
||||||
|
BEAST_EXPECT(! sh.has_value());
|
||||||
|
BEAST_EXPECT(!sig.slot().has_handler());
|
||||||
|
|
||||||
|
sh.emplace(
|
||||||
|
net::bind_cancellation_slot(
|
||||||
|
sig.slot(), handler{}),
|
||||||
|
net::cancellation_type::total);
|
||||||
|
BEAST_EXPECT(sh.has_value());
|
||||||
|
BEAST_EXPECT(sig.slot().has_handler());
|
||||||
|
sig.emit(net::cancellation_type::total);
|
||||||
|
BEAST_EXPECT(! sh.has_value());
|
||||||
|
BEAST_EXPECT(!sig.slot().has_handler());
|
||||||
|
|
||||||
|
{
|
||||||
|
saved_handler sh_inner;
|
||||||
|
sh_inner.emplace(
|
||||||
|
net::bind_cancellation_slot(
|
||||||
|
sig.slot(), handler{}));
|
||||||
|
|
||||||
|
sh = std::move(sh_inner);
|
||||||
|
}
|
||||||
|
BEAST_EXPECT(sh.has_value());
|
||||||
|
BEAST_EXPECT(sig.slot().has_handler());
|
||||||
|
sig.emit(net::cancellation_type::all);
|
||||||
|
BEAST_EXPECT(! sh.has_value());
|
||||||
|
BEAST_EXPECT(!sig.slot().has_handler());
|
||||||
|
|
||||||
|
}
|
||||||
|
{
|
||||||
|
saved_handler sh;
|
||||||
|
net::cancellation_signal sig;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sh.emplace(
|
||||||
|
net::bind_cancellation_slot(
|
||||||
|
sig.slot(),
|
||||||
|
throwing_handler{}));
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch(std::exception const&)
|
||||||
|
{
|
||||||
|
pass();
|
||||||
|
}
|
||||||
|
BEAST_EXPECT(!sig.slot().has_handler());
|
||||||
|
BEAST_EXPECT(! sh.has_value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
testSavedHandler();
|
testSavedHandler();
|
||||||
|
testSavedHandlerCancellation();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,6 +25,12 @@
|
|||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
#include <boost/asio/strand.hpp>
|
#include <boost/asio/strand.hpp>
|
||||||
#include <boost/asio/write.hpp>
|
#include <boost/asio/write.hpp>
|
||||||
|
#include <boost/asio/bind_cancellation_slot.hpp>
|
||||||
|
#include <boost/asio/error.hpp>
|
||||||
|
#include <boost/asio/io_context.hpp>
|
||||||
|
#include <boost/asio/connect_pipe.hpp>
|
||||||
|
#include <boost/asio/readable_pipe.hpp>
|
||||||
|
#include <boost/asio/writable_pipe.hpp>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
#if BOOST_ASIO_HAS_CO_AWAIT
|
#if BOOST_ASIO_HAS_CO_AWAIT
|
||||||
@ -729,7 +735,57 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testCancellation(yield_context do_yield)
|
||||||
|
{
|
||||||
|
// this is tested on a pipe
|
||||||
|
// because the test::stream doesn't implement cancellation
|
||||||
|
{
|
||||||
|
response<string_body> m;
|
||||||
|
error_code ec;
|
||||||
|
net::writable_pipe ts{ioc_};
|
||||||
|
net::readable_pipe tr{ioc_};
|
||||||
|
net::connect_pipe(tr, ts);
|
||||||
|
net::cancellation_signal cl;
|
||||||
|
net::post(ioc_, [&]{cl.emit(net::cancellation_type::all);});
|
||||||
|
net::steady_timer timeout(ioc_, std::chrono::seconds(5));
|
||||||
|
timeout.async_wait(
|
||||||
|
[&](error_code ec)
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||||
|
if (!ec) // this means the cancel failed!
|
||||||
|
ts.close();
|
||||||
|
});
|
||||||
|
multi_buffer b;
|
||||||
|
async_read(tr, b, m, net::bind_cancellation_slot(cl.slot(), do_yield[ec]));
|
||||||
|
timeout.cancel();
|
||||||
|
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
response<string_body> m;
|
||||||
|
error_code ec;
|
||||||
|
net::writable_pipe ts{ioc_};
|
||||||
|
net::readable_pipe tr{ioc_};
|
||||||
|
net::connect_pipe(tr, ts);
|
||||||
|
net::cancellation_signal cl;
|
||||||
|
net::post(ioc_, [&]{cl.emit(net::cancellation_type::all);});
|
||||||
|
net::steady_timer timeout(ioc_, std::chrono::seconds(5));
|
||||||
|
timeout.async_wait(
|
||||||
|
[&](error_code ec)
|
||||||
|
{
|
||||||
|
// using BEAST_EXPECT HERE is a race condition, since the test suite might
|
||||||
|
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||||
|
if (!ec) // this means the cancel failed!
|
||||||
|
ts.close();
|
||||||
|
});
|
||||||
|
multi_buffer b;
|
||||||
|
async_read(tr, b, m, net::bind_cancellation_slot(cl.slot(), do_yield[ec]));
|
||||||
|
timeout.cancel();
|
||||||
|
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||||
|
}
|
||||||
|
// the timer handler may be invoked after the test suite is complete if we don't post.
|
||||||
|
asio::post(ioc_, do_yield);
|
||||||
|
}
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
@ -761,6 +817,11 @@ public:
|
|||||||
testReadSomeHeader(yield);
|
testReadSomeHeader(yield);
|
||||||
});
|
});
|
||||||
testReadSomeHeader();
|
testReadSomeHeader();
|
||||||
|
yield_to(
|
||||||
|
[&](yield_context yield)
|
||||||
|
{
|
||||||
|
testCancellation(yield);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,8 +22,13 @@
|
|||||||
#include <boost/beast/_experimental/test/stream.hpp>
|
#include <boost/beast/_experimental/test/stream.hpp>
|
||||||
#include <boost/beast/test/yield_to.hpp>
|
#include <boost/beast/test/yield_to.hpp>
|
||||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
|
#include <boost/asio/bind_cancellation_slot.hpp>
|
||||||
#include <boost/asio/error.hpp>
|
#include <boost/asio/error.hpp>
|
||||||
#include <boost/asio/io_context.hpp>
|
#include <boost/asio/io_context.hpp>
|
||||||
|
#include <boost/asio/connect_pipe.hpp>
|
||||||
|
#include <boost/asio/readable_pipe.hpp>
|
||||||
|
#include <boost/asio/writable_pipe.hpp>
|
||||||
|
#include <boost/asio/steady_timer.hpp>
|
||||||
#include <boost/asio/strand.hpp>
|
#include <boost/asio/strand.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -1050,6 +1055,69 @@ public:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
testCancellation(yield_context do_yield)
|
||||||
|
{
|
||||||
|
// this is tested on a pipe
|
||||||
|
// because the test::stream doesn't implement cancellation
|
||||||
|
{
|
||||||
|
response<string_body> m;
|
||||||
|
m.version(10);
|
||||||
|
m.result(status::ok);
|
||||||
|
m.set(field::server, "test");
|
||||||
|
m.set(field::content_length, "5");
|
||||||
|
// make the content big enough so it overflows the buffer
|
||||||
|
// that'll make the op never complete if we don't cancel
|
||||||
|
m.body().assign(10000000, '*');
|
||||||
|
error_code ec;
|
||||||
|
net::writable_pipe ts{ioc_};
|
||||||
|
net::readable_pipe tr{ioc_};
|
||||||
|
net::connect_pipe(tr, ts);
|
||||||
|
net::cancellation_signal cl;
|
||||||
|
net::post(ioc_, [&]{cl.emit(net::cancellation_type::all);});
|
||||||
|
net::steady_timer timeout(ioc_, std::chrono::seconds(5));
|
||||||
|
timeout.async_wait(
|
||||||
|
[&](error_code ec)
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||||
|
if (!ec) // this means the cancel failed!
|
||||||
|
ts.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
async_write(ts, m, net::bind_cancellation_slot(cl.slot(), do_yield[ec]));
|
||||||
|
timeout.cancel();
|
||||||
|
net::post(ioc_, do_yield); // wait for the timeout to finish
|
||||||
|
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
response<string_body> m;
|
||||||
|
m.version(11);
|
||||||
|
m.result(status::ok);
|
||||||
|
m.set(field::server, "test");
|
||||||
|
m.set(field::transfer_encoding, "chunked");
|
||||||
|
m.body().assign(10000000, '*');
|
||||||
|
error_code ec;
|
||||||
|
net::writable_pipe ts{ioc_};
|
||||||
|
net::readable_pipe tr{ioc_};
|
||||||
|
net::connect_pipe(tr, ts);
|
||||||
|
net::cancellation_signal cl;
|
||||||
|
net::post(ioc_, [&]{cl.emit(net::cancellation_type::all);});
|
||||||
|
net::steady_timer timeout(ioc_, std::chrono::seconds(5));
|
||||||
|
timeout.async_wait(
|
||||||
|
[&](error_code ec)
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||||
|
if (!ec) // this means the cancel failed!
|
||||||
|
ts.close();
|
||||||
|
});
|
||||||
|
async_write(ts, m, net::bind_cancellation_slot(cl.slot(), do_yield[ec]));
|
||||||
|
timeout.cancel();
|
||||||
|
net::post(ioc_, do_yield); // wait for the timeout to finish
|
||||||
|
BEAST_EXPECT(ec == net::error::operation_aborted);
|
||||||
|
}
|
||||||
|
// the timer handler may be invoked after the test suite is complete if we don't post.
|
||||||
|
asio::post(ioc_, do_yield);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
@ -1077,6 +1145,11 @@ public:
|
|||||||
#if BOOST_ASIO_HAS_CO_AWAIT
|
#if BOOST_ASIO_HAS_CO_AWAIT
|
||||||
boost::ignore_unused(&write_test::testAwaitableCompiles);
|
boost::ignore_unused(&write_test::testAwaitableCompiles);
|
||||||
#endif
|
#endif
|
||||||
|
yield_to(
|
||||||
|
[&](yield_context yield)
|
||||||
|
{
|
||||||
|
testCancellation(yield);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ add_executable (tests-beast-websocket
|
|||||||
test.hpp
|
test.hpp
|
||||||
_detail_prng.cpp
|
_detail_prng.cpp
|
||||||
accept.cpp
|
accept.cpp
|
||||||
|
cancel.cpp
|
||||||
close.cpp
|
close.cpp
|
||||||
error.cpp
|
error.cpp
|
||||||
frame.cpp
|
frame.cpp
|
||||||
|
@ -12,6 +12,7 @@ local SOURCES =
|
|||||||
_detail_impl_base.cpp
|
_detail_impl_base.cpp
|
||||||
_detail_prng.cpp
|
_detail_prng.cpp
|
||||||
accept.cpp
|
accept.cpp
|
||||||
|
cancel.cpp
|
||||||
close.cpp
|
close.cpp
|
||||||
error.cpp
|
error.cpp
|
||||||
frame.cpp
|
frame.cpp
|
||||||
|
230
test/beast/websocket/cancel.cpp
Normal file
230
test/beast/websocket/cancel.cpp
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2016-2019 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/websocket/stream.hpp>
|
||||||
|
#include <boost/beast/core/tcp_stream.hpp>
|
||||||
|
|
||||||
|
#include <boost/beast/_experimental/test/stream.hpp>
|
||||||
|
#include <boost/beast/_experimental/test/tcp.hpp>
|
||||||
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
|
#include <boost/asio/strand.hpp>
|
||||||
|
#include <boost/asio/bind_cancellation_slot.hpp>
|
||||||
|
#include <boost/asio/deferred.hpp>
|
||||||
|
#include <boost/asio/redirect_error.hpp>
|
||||||
|
|
||||||
|
#include "test.hpp"
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace beast {
|
||||||
|
namespace websocket {
|
||||||
|
|
||||||
|
struct async_all_server_op : boost::asio::coroutine
|
||||||
|
{
|
||||||
|
stream<asio::ip::tcp::socket> & ws;
|
||||||
|
|
||||||
|
async_all_server_op(stream<asio::ip::tcp::socket> & ws) : ws(ws) {}
|
||||||
|
|
||||||
|
template<typename Self>
|
||||||
|
void operator()(Self && self, error_code ec = {}, std::size_t sz = 0)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
return self.complete(ec);
|
||||||
|
|
||||||
|
BOOST_ASIO_CORO_REENTER(*this)
|
||||||
|
{
|
||||||
|
self.reset_cancellation_state([](net::cancellation_type ct){return ct;});
|
||||||
|
BOOST_ASIO_CORO_YIELD ws.async_handshake("test", "/", std::move(self));
|
||||||
|
BOOST_ASIO_CORO_YIELD ws.async_ping("", std::move(self));
|
||||||
|
BOOST_ASIO_CORO_YIELD ws.async_write_some(false, net::buffer("FOO", 3), std::move(self));
|
||||||
|
BOOST_ASIO_CORO_YIELD ws.async_write_some(true, net::buffer("BAR", 3), std::move(self));
|
||||||
|
BOOST_ASIO_CORO_YIELD ws.async_close("testing", std::move(self));
|
||||||
|
self.complete({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<BOOST_BEAST_ASYNC_TPARAM1 CompletionToken>
|
||||||
|
BOOST_BEAST_ASYNC_RESULT1(CompletionToken)
|
||||||
|
async_all_server(
|
||||||
|
stream<asio::ip::tcp::socket> & ws,
|
||||||
|
CompletionToken && token)
|
||||||
|
{
|
||||||
|
return net::async_compose<CompletionToken, void(error_code)>
|
||||||
|
(
|
||||||
|
async_all_server_op{ws},
|
||||||
|
token, ws
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct async_all_client_op : boost::asio::coroutine
|
||||||
|
{
|
||||||
|
stream<asio::ip::tcp::socket> & ws;
|
||||||
|
|
||||||
|
async_all_client_op(stream<asio::ip::tcp::socket> & ws) : ws(ws) {}
|
||||||
|
struct impl_t
|
||||||
|
{
|
||||||
|
impl_t () = default;
|
||||||
|
std::string res;
|
||||||
|
net::dynamic_string_buffer<char, std::char_traits<char>, std::allocator<char>> buf =
|
||||||
|
net::dynamic_buffer(res);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<impl_t> impl{std::make_shared<impl_t>()};
|
||||||
|
|
||||||
|
template<typename Self>
|
||||||
|
void operator()(Self && self, error_code ec = {}, std::size_t sz = 0)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
return self.complete(ec);
|
||||||
|
BOOST_ASIO_CORO_REENTER(*this)
|
||||||
|
{
|
||||||
|
// let everything pass
|
||||||
|
self.reset_cancellation_state([](net::cancellation_type ct){return ct;});
|
||||||
|
|
||||||
|
BOOST_ASIO_CORO_YIELD ws.async_accept(std::move(self));
|
||||||
|
BOOST_ASIO_CORO_YIELD ws.async_pong("", std::move(self));
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_ASIO_CORO_YIELD ws.async_read(impl->buf, std::move(self));
|
||||||
|
BEAST_EXPECTS(impl->res == "FOOBAR", impl->res);
|
||||||
|
|
||||||
|
BOOST_ASIO_CORO_YIELD ws.async_read(impl->buf, std::move(self));
|
||||||
|
BEAST_EXPECTS(ec == websocket::error::closed
|
||||||
|
|| ec == net::error::connection_reset
|
||||||
|
// hard coded winapi error (WSAECONNRESET), same as connection_reset, but asio delivers it with system_category
|
||||||
|
|| (ec.value() == 10054
|
||||||
|
&& ec.category() == system::system_category())
|
||||||
|
|| ec == net::error::not_connected, ec.message());
|
||||||
|
self.complete({});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<BOOST_BEAST_ASYNC_TPARAM1 CompletionToken>
|
||||||
|
BOOST_BEAST_ASYNC_RESULT1(CompletionToken)
|
||||||
|
async_all_client(
|
||||||
|
stream<asio::ip::tcp::socket> & ws,
|
||||||
|
CompletionToken && token)
|
||||||
|
{
|
||||||
|
return net::async_compose<CompletionToken, void(error_code)>
|
||||||
|
(
|
||||||
|
async_all_client_op{ws},
|
||||||
|
token, ws
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class cancel_test : public websocket_test_suite
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::size_t run_impl(net::cancellation_signal &sl,
|
||||||
|
net::io_context &ctx,
|
||||||
|
std::size_t trigger = -1,
|
||||||
|
net::cancellation_type tp = net::cancellation_type::terminal)
|
||||||
|
{
|
||||||
|
std::size_t cnt = 0;
|
||||||
|
std::size_t res = 0u;
|
||||||
|
while ((res = ctx.run_one()) != 0)
|
||||||
|
if (trigger == cnt ++)
|
||||||
|
{
|
||||||
|
sl.emit(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t testAll(std::size_t & cancel_counter,
|
||||||
|
bool cancel_server = false,
|
||||||
|
std::size_t trigger = -1)
|
||||||
|
{
|
||||||
|
net::cancellation_signal sig1, sig2;
|
||||||
|
|
||||||
|
net::io_context ioc;
|
||||||
|
using tcp = net::ip::tcp;
|
||||||
|
|
||||||
|
stream<tcp::socket> ws1(ioc.get_executor());
|
||||||
|
stream<tcp::socket> ws2(ioc.get_executor());
|
||||||
|
test::connect(ws1.next_layer(), ws2.next_layer());
|
||||||
|
|
||||||
|
async_all_server(ws1,
|
||||||
|
net::bind_cancellation_slot(sig1.slot(), [&](system::error_code ec)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
if (ec == net::error::operation_aborted &&
|
||||||
|
(cancel_server || trigger == static_cast<std::size_t>(-1)))
|
||||||
|
cancel_counter++;
|
||||||
|
|
||||||
|
BEAST_EXPECTS(ec == net::error::operation_aborted
|
||||||
|
|| ec == net::error::broken_pipe
|
||||||
|
// winapi WSAECONNRESET, as system_category
|
||||||
|
|| ec == error_code(10054, boost::system::system_category())
|
||||||
|
|| ec == net::error::bad_descriptor
|
||||||
|
|| ec == net::error::eof, ec.message());
|
||||||
|
get_lowest_layer(ws1).close();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
async_all_client(
|
||||||
|
ws2,
|
||||||
|
net::bind_cancellation_slot(sig2.slot(), [&](system::error_code ec)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
if (ec == net::error::operation_aborted &&
|
||||||
|
(!cancel_server || trigger == static_cast<std::size_t>(-1)))
|
||||||
|
cancel_counter++;
|
||||||
|
BEAST_EXPECTS(ec == net::error::operation_aborted
|
||||||
|
|| ec == error::closed
|
||||||
|
|| ec == net::error::broken_pipe
|
||||||
|
|| ec == net::error::connection_reset
|
||||||
|
|| ec == net::error::not_connected
|
||||||
|
// winapi WSAECONNRESET, as system_category
|
||||||
|
|| ec == error_code(10054, boost::system::system_category())
|
||||||
|
|| ec == net::error::eof, ec.message());
|
||||||
|
get_lowest_layer(ws1).close();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return run_impl(cancel_server ? sig1 : sig2, ioc, trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
void brute_force()
|
||||||
|
{
|
||||||
|
std::size_t cancel_counter = 0;
|
||||||
|
const auto init = testAll(cancel_counter);
|
||||||
|
BEAST_EXPECT(cancel_counter == 0u);
|
||||||
|
for (std::size_t cnt = 0; cnt < init; cnt ++)
|
||||||
|
testAll(cancel_counter, true, cnt);
|
||||||
|
|
||||||
|
BEAST_EXPECT(cancel_counter > 0u);
|
||||||
|
cancel_counter = 0u;
|
||||||
|
for (std::size_t cnt = 0; cnt < init; cnt ++)
|
||||||
|
testAll(cancel_counter, false, cnt);
|
||||||
|
|
||||||
|
|
||||||
|
BEAST_EXPECT(cancel_counter > 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
brute_force();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(beast,websocket,cancel);
|
||||||
|
|
||||||
|
} // websocket
|
||||||
|
} // beast
|
||||||
|
} // boost
|
@ -251,6 +251,9 @@ struct handler
|
|||||||
using executor_type = boost::asio::io_context::executor_type;
|
using executor_type = boost::asio::io_context::executor_type;
|
||||||
executor_type get_executor() const noexcept;
|
executor_type get_executor() const noexcept;
|
||||||
|
|
||||||
|
using cancellation_slot_type = boost::asio::cancellation_slot;
|
||||||
|
cancellation_slot_type get_cancellation_slot() const noexcept;
|
||||||
|
|
||||||
void operator()(boost::beast::error_code, std::size_t);
|
void operator()(boost::beast::error_code, std::size_t);
|
||||||
};
|
};
|
||||||
//]
|
//]
|
||||||
@ -265,6 +268,12 @@ inline auto handler::get_executor() const noexcept ->
|
|||||||
static boost::asio::io_context ioc;
|
static boost::asio::io_context ioc;
|
||||||
return ioc.get_executor();
|
return ioc.get_executor();
|
||||||
}
|
}
|
||||||
|
inline auto handler::get_cancellation_slot() const noexcept ->
|
||||||
|
cancellation_slot_type
|
||||||
|
{
|
||||||
|
return cancellation_slot_type();
|
||||||
|
}
|
||||||
|
|
||||||
inline void handler::operator()(
|
inline void handler::operator()(
|
||||||
boost::beast::error_code, std::size_t)
|
boost::beast::error_code, std::size_t)
|
||||||
{
|
{
|
||||||
@ -296,6 +305,18 @@ struct associated_executor<handler, Executor>
|
|||||||
Executor const& ex = Executor{}) noexcept;
|
Executor const& ex = Executor{}) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class CancellationSlot>
|
||||||
|
struct associated_cancellation_slot<handler, CancellationSlot>
|
||||||
|
{
|
||||||
|
using type = cancellation_slot;
|
||||||
|
|
||||||
|
static
|
||||||
|
type
|
||||||
|
get(handler const& h,
|
||||||
|
CancellationSlot const& cs = CancellationSlot{}) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
} // boost
|
} // boost
|
||||||
} // asio
|
} // asio
|
||||||
//]
|
//]
|
||||||
@ -315,6 +336,15 @@ get(handler const&, Executor const&) noexcept -> type
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class CancellationSlot>
|
||||||
|
auto
|
||||||
|
boost::asio::associated_cancellation_slot<handler, CancellationSlot>::
|
||||||
|
get(handler const&, CancellationSlot const&) noexcept -> type
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
|
Reference in New Issue
Block a user