summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/beast/test/extras
diff options
context:
space:
mode:
Diffstat (limited to 'src/boost/libs/beast/test/extras')
-rw-r--r--src/boost/libs/beast/test/extras/README.md3
-rw-r--r--src/boost/libs/beast/test/extras/include/boost/beast/doc_debug.hpp170
-rw-r--r--src/boost/libs/beast/test/extras/include/boost/beast/test/fuzz.hpp105
-rw-r--r--src/boost/libs/beast/test/extras/include/boost/beast/test/sig_wait.hpp38
-rw-r--r--src/boost/libs/beast/test/extras/include/boost/beast/test/test_allocator.hpp310
-rw-r--r--src/boost/libs/beast/test/extras/include/boost/beast/test/throughput.hpp57
-rw-r--r--src/boost/libs/beast/test/extras/include/boost/beast/test/websocket.hpp250
-rw-r--r--src/boost/libs/beast/test/extras/include/boost/beast/test/yield_to.hpp139
8 files changed, 1072 insertions, 0 deletions
diff --git a/src/boost/libs/beast/test/extras/README.md b/src/boost/libs/beast/test/extras/README.md
new file mode 100644
index 00000000..ea6ffce9
--- /dev/null
+++ b/src/boost/libs/beast/test/extras/README.md
@@ -0,0 +1,3 @@
+# Extras
+
+These are not part of the official public Beast interface but they are used by the tests and some third party programs.
diff --git a/src/boost/libs/beast/test/extras/include/boost/beast/doc_debug.hpp b/src/boost/libs/beast/test/extras/include/boost/beast/doc_debug.hpp
new file mode 100644
index 00000000..61c39b03
--- /dev/null
+++ b/src/boost/libs/beast/test/extras/include/boost/beast/doc_debug.hpp
@@ -0,0 +1,170 @@
+//
+// 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)
+//
+
+#ifndef BOOST_BEAST_DOC_DEBUG_HPP
+#define BOOST_BEAST_DOC_DEBUG_HPP
+
+namespace beast {
+
+#if BOOST_BEAST_DOXYGEN
+
+/// doc type (documentation debug helper)
+using doc_type = int;
+
+/// doc enum (documentation debug helper)
+enum doc_enum
+{
+ /// One (documentation debug helper)
+ one,
+
+ /// Two (documentation debug helper)
+ two
+};
+
+/// doc enum class (documentation debug helper)
+enum class doc_enum_class : unsigned
+{
+ /// one (documentation debug helper)
+ one,
+
+ /// two (documentation debug helper)
+ two
+};
+
+/// doc func (documentation debug helper)
+void doc_func();
+
+/// doc class (documentation debug helper)
+struct doc_class
+{
+ /// doc class member func (documentation debug helper)
+ void func();
+};
+
+/// (documentation debug helper)
+namespace nested {
+
+/// doc type (documentation debug helper)
+using nested_doc_type = int;
+
+/// doc enum (documentation debug helper)
+enum nested_doc_enum
+{
+ /// One (documentation debug helper)
+ one,
+
+ /// Two (documentation debug helper)
+ two
+};
+
+/// doc enum class (documentation debug helper)
+enum class nested_doc_enum_class : unsigned
+{
+ /// one (documentation debug helper)
+ one,
+
+ /// two (documentation debug helper)
+ two
+};
+
+/// doc func (documentation debug helper)
+void nested_doc_func();
+
+/// doc class (documentation debug helper)
+struct nested_doc_class
+{
+ /// doc class member func (documentation debug helper)
+ void func();
+};
+
+} // nested
+
+/** This is here to help troubleshoot doc/reference.xsl problems
+
+ Embedded references:
+
+ @li type @ref doc_type
+
+ @li enum @ref doc_enum
+
+ @li enum item @ref doc_enum::one
+
+ @li enum_class @ref doc_enum_class
+
+ @li enum_class item @ref doc_enum_class::one
+
+ @li func @ref doc_func
+
+ @li class @ref doc_class
+
+ @li class func @ref doc_class::func
+
+ @li nested type @ref nested::nested_doc_type
+
+ @li nested enum @ref nested::nested_doc_enum
+
+ @li nested enum item @ref nested::nested_doc_enum::one
+
+ @li nested enum_class @ref nested::nested_doc_enum_class
+
+ @li nested enum_class item @ref nested::nested_doc_enum_class::one
+
+ @li nested func @ref nested::nested_doc_func
+
+ @li nested class @ref nested::nested_doc_class
+
+ @li nested class func @ref nested::nested_doc_class::func
+*/
+void doc_debug();
+
+namespace nested {
+
+/** This is here to help troubleshoot doc/reference.xsl problems
+
+ Embedded references:
+
+ @li type @ref doc_type
+
+ @li enum @ref doc_enum
+
+ @li enum item @ref doc_enum::one
+
+ @li enum_class @ref doc_enum_class
+
+ @li enum_class item @ref doc_enum_class::one
+
+ @li func @ref doc_func
+
+ @li class @ref doc_class
+
+ @li class func @ref doc_class::func
+
+ @li nested type @ref nested_doc_type
+
+ @li nested enum @ref nested_doc_enum
+
+ @li nested enum item @ref nested_doc_enum::one
+
+ @li nested enum_class @ref nested_doc_enum_class
+
+ @li nested enum_class item @ref nested_doc_enum_class::one
+
+ @li nested func @ref nested_doc_func
+
+ @li nested class @ref nested_doc_class
+
+ @li nested class func @ref nested_doc_class::func
+*/
+void nested_doc_debug();
+
+} // nested
+
+#endif
+
+} // beast
+
+#endif
diff --git a/src/boost/libs/beast/test/extras/include/boost/beast/test/fuzz.hpp b/src/boost/libs/beast/test/extras/include/boost/beast/test/fuzz.hpp
new file mode 100644
index 00000000..5491b2e2
--- /dev/null
+++ b/src/boost/libs/beast/test/extras/include/boost/beast/test/fuzz.hpp
@@ -0,0 +1,105 @@
+//
+// 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
+//
+
+#ifndef BOOST_BEAST_TEST_FUZZ_HPP
+#define BOOST_BEAST_TEST_FUZZ_HPP
+
+#include <boost/beast/core/static_string.hpp>
+#include <boost/beast/core/string.hpp>
+#include <random>
+
+namespace boost {
+namespace beast {
+namespace test {
+
+class fuzz_rand
+{
+ std::mt19937 rng_;
+
+public:
+ std::mt19937&
+ rng()
+ {
+ return rng_;
+ }
+
+ template<class Unsigned>
+ Unsigned
+ operator()(Unsigned n)
+ {
+ return static_cast<Unsigned>(
+ std::uniform_int_distribution<
+ Unsigned>{0, n-1}(rng_));
+ }
+};
+
+template<std::size_t N, class Rand, class F>
+static
+void
+fuzz(
+ static_string<N> const& input,
+ std::size_t repeat,
+ std::size_t depth,
+ Rand& r,
+ F const& f)
+{
+ static_string<N> mod{input};
+ for(auto i = repeat; i; --i)
+ {
+ switch(r(4))
+ {
+ case 0: // insert
+ if(mod.size() >= mod.max_size())
+ continue;
+ mod.insert(r(mod.size() + 1), 1,
+ static_cast<char>(r(256)));
+ break;
+
+ case 1: // erase
+ if(mod.size() == 0)
+ continue;
+ mod.erase(r(mod.size()), 1);
+ break;
+
+ case 2: // swap
+ {
+ if(mod.size() <= 1)
+ continue;
+ auto off = r(mod.size() - 1);
+ auto const temp = mod[off];
+ mod[off] = mod[off + 1];
+ mod[off + 1] = temp;
+ break;
+ }
+ case 3: // repeat
+ {
+ if(mod.empty())
+ continue;
+ auto n = (std::min)(
+ std::geometric_distribution<
+ std::size_t>{}(r.rng()),
+ mod.max_size() - mod.size());
+ if(n == 0)
+ continue;
+ auto off = r(mod.size());
+ mod.insert(off, n, mod[off + 1]);
+ break;
+ }
+ }
+ f(string_view{mod.data(), mod.size()});
+ if(depth > 0)
+ fuzz(mod, repeat, depth - 1, r, f);
+ }
+}
+
+} // test
+} // beast
+} // boost
+
+#endif
diff --git a/src/boost/libs/beast/test/extras/include/boost/beast/test/sig_wait.hpp b/src/boost/libs/beast/test/extras/include/boost/beast/test/sig_wait.hpp
new file mode 100644
index 00000000..96c41da1
--- /dev/null
+++ b/src/boost/libs/beast/test/extras/include/boost/beast/test/sig_wait.hpp
@@ -0,0 +1,38 @@
+//
+// 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
+//
+
+#ifndef BOOST_BEAST_TEST_SIG_WAIT_HPP
+#define BOOST_BEAST_TEST_SIG_WAIT_HPP
+
+#include <boost/asio.hpp>
+
+namespace boost {
+namespace beast {
+namespace test {
+
+/// Block until SIGINT or SIGTERM is received.
+inline
+void
+sig_wait()
+{
+ net::io_context ioc;
+ net::signal_set signals(
+ ioc, SIGINT, SIGTERM);
+ signals.async_wait(
+ [&](boost::system::error_code const&, int)
+ {
+ });
+ ioc.run();
+}
+
+} // test
+} // beast
+} // boost
+
+#endif
diff --git a/src/boost/libs/beast/test/extras/include/boost/beast/test/test_allocator.hpp b/src/boost/libs/beast/test/extras/include/boost/beast/test/test_allocator.hpp
new file mode 100644
index 00000000..b985c539
--- /dev/null
+++ b/src/boost/libs/beast/test/extras/include/boost/beast/test/test_allocator.hpp
@@ -0,0 +1,310 @@
+//
+// 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
+//
+
+#ifndef BOOST_BEAST_TEST_TEST_ALLOCATOR_HPP
+#define BOOST_BEAST_TEST_TEST_ALLOCATOR_HPP
+
+#include <atomic>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace test {
+
+struct test_allocator_info
+{
+ std::size_t id;
+ std::size_t ncopy = 0;
+ std::size_t nmove = 0;
+ std::size_t nmassign = 0;
+ std::size_t ncpassign = 0;
+ std::size_t nselect = 0;
+ std::size_t max_size = (
+ std::numeric_limits<std::size_t>::max)();
+
+ test_allocator_info()
+ : id([]
+ {
+ static std::atomic<std::size_t> sid(0);
+ return ++sid;
+ }())
+ {
+ }
+};
+
+template<
+ class T,
+ bool Equal,
+ bool Assign,
+ bool Move,
+ bool Swap,
+ bool Select>
+class test_allocator;
+
+template<
+ class T,
+ bool Equal,
+ bool Assign,
+ bool Move,
+ bool Swap,
+ bool Select>
+struct test_allocator_base
+{
+};
+
+// Select == true
+template<
+ class T,
+ bool Equal,
+ bool Assign,
+ bool Move,
+ bool Swap>
+struct test_allocator_base<
+ T, Equal, Assign, Move, Swap, true>
+{
+ static
+ test_allocator<T, Equal, Assign, Move, Swap, true>
+ select_on_container_copy_construction(test_allocator<
+ T, Equal, Assign, Move, Swap, true> const&)
+ {
+ return test_allocator<T,
+ Equal, Assign, Move, Swap, true>{};
+ }
+};
+
+template<
+ class T,
+ bool Equal,
+ bool Assign,
+ bool Move,
+ bool Swap,
+ bool Select>
+class test_allocator
+ : public test_allocator_base<
+ T, Equal, Assign, Move, Swap, Select>
+{
+ std::shared_ptr<test_allocator_info> info_;
+
+ template<class, bool, bool, bool, bool, bool>
+ friend class test_allocator;
+
+public:
+ using value_type = T;
+
+ using propagate_on_container_copy_assignment =
+ std::integral_constant<bool, Assign>;
+
+ using propagate_on_container_move_assignment =
+ std::integral_constant<bool, Move>;
+
+ using propagate_on_container_swap =
+ std::integral_constant<bool, Swap>;
+
+ template<class U>
+ struct rebind
+ {
+ using other = test_allocator<U, Equal, Assign, Move, Swap, Select>;
+ };
+
+ test_allocator()
+ : info_(std::make_shared<
+ test_allocator_info>())
+ {
+ }
+
+ test_allocator(test_allocator const& u) noexcept
+ : info_(u.info_)
+ {
+ ++info_->ncopy;
+ }
+
+ template<class U>
+ test_allocator(test_allocator<U,
+ Equal, Assign, Move, Swap, Select> const& u) noexcept
+ : info_(u.info_)
+ {
+ ++info_->ncopy;
+ }
+
+ test_allocator(test_allocator&& t)
+ : info_(t.info_)
+ {
+ ++info_->nmove;
+ }
+
+ test_allocator&
+ operator=(test_allocator const& u) noexcept
+ {
+ info_ = u.info_;
+ ++info_->ncpassign;
+ return *this;
+ }
+
+ test_allocator&
+ operator=(test_allocator&& u) noexcept
+ {
+ info_ = u.info_;
+ ++info_->nmassign;
+ return *this;
+ }
+
+ value_type*
+ allocate(std::size_t n)
+ {
+ return static_cast<value_type*>(
+ ::operator new (n*sizeof(value_type)));
+ }
+
+ void
+ deallocate(value_type* p, std::size_t) noexcept
+ {
+ ::operator delete(p);
+ }
+
+ std::size_t
+ max_size() const
+ {
+ return info_->max_size;
+ }
+
+ void
+ max_size(std::size_t n)
+ {
+ info_->max_size = n;
+ }
+
+ bool
+ operator==(test_allocator const& other) const
+ {
+ return id() == other.id() || Equal;
+ }
+
+ bool
+ operator!=(test_allocator const& other) const
+ {
+ return ! this->operator==(other);
+ }
+
+ std::size_t
+ id() const
+ {
+ return info_->id;
+ }
+
+ test_allocator_info*
+ operator->() const
+ {
+ return info_.get();
+ }
+};
+
+//------------------------------------------------------------------------------
+
+#if 0
+struct allocator_info
+{
+ std::size_t const id;
+
+ allocator_info()
+ : id([]
+ {
+ static std::atomic<std::size_t> sid(0);
+ return ++sid;
+ }())
+ {
+ }
+};
+
+struct allocator_defaults
+{
+ static std::size_t constexpr max_size =
+ (std::numeric_limits<std::size_t>::max)();
+};
+
+template<
+ class T,
+ class Traits = allocator_defaults>
+struct allocator
+{
+public:
+ using value_type = T;
+
+#if 0
+ template<class U>
+ struct rebind
+ {
+ using other =
+ test_allocator<U, Traits>;
+ };
+#endif
+
+ allocator() = default;
+ allocator(allocator&& t) = default;
+ allocator(allocator const& u) = default;
+
+ template<
+ class U,
+ class = typename std::enable_if<
+ ! std::is_same<U, T>::value>::type>
+ allocator(
+ allocator<U, Traits> const& u) noexcept
+ {
+ }
+
+ allocator&
+ operator=(allocator&& u) noexcept
+ {
+ return *this;
+ }
+
+ allocator&
+ operator=(allocator const& u) noexcept
+ {
+ return *this;
+ }
+
+ value_type*
+ allocate(std::size_t n)
+ {
+ return static_cast<value_type*>(
+ ::operator new(n * sizeof(value_type)));
+ }
+
+ void
+ deallocate(value_type* p, std::size_t) noexcept
+ {
+ ::operator delete(p);
+ }
+
+ std::size_t
+ max_size() const
+ {
+ }
+
+ bool
+ operator==(test_allocator const& other) const
+ {
+ return id() == other.id() || Equal;
+ }
+
+ bool
+ operator!=(test_allocator const& other) const
+ {
+ return ! this->operator==(other);
+ }
+};
+#endif
+
+} // test
+} // beast
+} // boost
+
+#endif
diff --git a/src/boost/libs/beast/test/extras/include/boost/beast/test/throughput.hpp b/src/boost/libs/beast/test/extras/include/boost/beast/test/throughput.hpp
new file mode 100644
index 00000000..c7eca048
--- /dev/null
+++ b/src/boost/libs/beast/test/extras/include/boost/beast/test/throughput.hpp
@@ -0,0 +1,57 @@
+//
+// 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
+//
+
+#ifndef BOOST_BEAST_TEST_THROUGHPUT_HPP
+#define BOOST_BEAST_TEST_THROUGHPUT_HPP
+
+#include <chrono>
+#include <cstdint>
+
+namespace boost {
+namespace beast {
+namespace test {
+
+class timer
+{
+ using clock_type =
+ std::chrono::system_clock;
+
+ clock_type::time_point when_;
+
+public:
+ using duration =
+ clock_type::duration;
+
+ timer()
+ : when_(clock_type::now())
+ {
+ }
+
+ duration
+ elapsed() const
+ {
+ return clock_type::now() - when_;
+ }
+};
+
+inline
+std::uint64_t
+throughput(std::chrono::duration<
+ double> const& elapsed, std::uint64_t items)
+{
+ using namespace std::chrono;
+ return static_cast<std::uint64_t>(
+ 1 / (elapsed/items).count());
+}
+
+} // test
+} // beast
+} // boost
+
+#endif
diff --git a/src/boost/libs/beast/test/extras/include/boost/beast/test/websocket.hpp b/src/boost/libs/beast/test/extras/include/boost/beast/test/websocket.hpp
new file mode 100644
index 00000000..20a541b5
--- /dev/null
+++ b/src/boost/libs/beast/test/extras/include/boost/beast/test/websocket.hpp
@@ -0,0 +1,250 @@
+//
+// 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
+//
+
+#ifndef BOOST_BEAST_TEST_WEBSOCKET_HPP
+#define BOOST_BEAST_TEST_WEBSOCKET_HPP
+
+#include <boost/beast/core/multi_buffer.hpp>
+#include <boost/beast/websocket/stream.hpp>
+#include <boost/beast/_experimental/test/stream.hpp>
+#include <boost/asio/executor_work_guard.hpp>
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/spawn.hpp>
+#include <cstdlib>
+#include <memory>
+#include <ostream>
+#include <thread>
+
+namespace boost {
+namespace beast {
+namespace test {
+
+// DEPRECATED
+class ws_echo_server
+{
+ std::ostream& log_;
+ net::io_context ioc_;
+ net::executor_work_guard<
+ net::io_context::executor_type> work_;
+ multi_buffer buffer_;
+ test::stream ts_;
+ std::thread t_;
+ websocket::stream<test::stream&> ws_;
+ bool close_ = false;
+
+public:
+ enum class kind
+ {
+ sync,
+ async,
+ async_client
+ };
+
+ explicit
+ ws_echo_server(
+ std::ostream& log,
+ kind k = kind::sync)
+ : log_(log)
+ , work_(ioc_.get_executor())
+ , ts_(ioc_)
+ , ws_(ts_)
+ {
+ beast::websocket::permessage_deflate pmd;
+ pmd.server_enable = true;
+ pmd.server_max_window_bits = 9;
+ pmd.compLevel = 1;
+ ws_.set_option(pmd);
+
+ switch(k)
+ {
+ case kind::sync:
+ t_ = std::thread{[&]{ do_sync(); }};
+ break;
+
+ case kind::async:
+ t_ = std::thread{[&]{ ioc_.run(); }};
+ do_accept();
+ break;
+
+ case kind::async_client:
+ t_ = std::thread{[&]{ ioc_.run(); }};
+ break;
+ }
+ }
+
+ ~ws_echo_server()
+ {
+ work_.reset();
+ t_.join();
+ }
+
+ test::stream&
+ stream()
+ {
+ return ts_;
+ }
+
+ void
+ async_handshake()
+ {
+ ws_.async_handshake("localhost", "/",
+ std::bind(
+ &ws_echo_server::on_handshake,
+ this,
+ std::placeholders::_1));
+ }
+
+ void
+ async_close()
+ {
+ net::post(ioc_,
+ [&]
+ {
+ if(ws_.is_open())
+ {
+ ws_.async_close({},
+ std::bind(
+ &ws_echo_server::on_close,
+ this,
+ std::placeholders::_1));
+ }
+ else
+ {
+ close_ = true;
+ }
+ });
+ }
+
+private:
+ void
+ do_sync()
+ {
+ try
+ {
+ ws_.accept();
+ for(;;)
+ {
+ ws_.read(buffer_);
+ ws_.text(ws_.got_text());
+ ws_.write(buffer_.data());
+ buffer_.consume(buffer_.size());
+ }
+ }
+ catch(system_error const& se)
+ {
+ boost::ignore_unused(se);
+#if 0
+ if( se.code() != error::closed &&
+ se.code() != error::failed &&
+ se.code() != net::error::eof)
+ log_ << "ws_echo_server: " << se.code().message() << std::endl;
+#endif
+ }
+ catch(std::exception const& e)
+ {
+ log_ << "ws_echo_server: " << e.what() << std::endl;
+ }
+ }
+
+ void
+ do_accept()
+ {
+ ws_.async_accept(std::bind(
+ &ws_echo_server::on_accept,
+ this,
+ std::placeholders::_1));
+ }
+
+ void
+ on_handshake(error_code ec)
+ {
+ if(ec)
+ return fail(ec);
+
+ do_read();
+ }
+
+ void
+ on_accept(error_code ec)
+ {
+ if(ec)
+ return fail(ec);
+
+ if(close_)
+ {
+ return ws_.async_close({},
+ std::bind(
+ &ws_echo_server::on_close,
+ this,
+ std::placeholders::_1));
+ }
+
+ do_read();
+ }
+
+ void
+ do_read()
+ {
+ ws_.async_read(buffer_,
+ std::bind(
+ &ws_echo_server::on_read,
+ this,
+ std::placeholders::_1));
+ }
+
+ void
+ on_read(error_code ec)
+ {
+ if(ec)
+ return fail(ec);
+ ws_.text(ws_.got_text());
+ ws_.async_write(buffer_.data(),
+ std::bind(
+ &ws_echo_server::on_write,
+ this,
+ std::placeholders::_1));
+ }
+
+ void
+ on_write(error_code ec)
+ {
+ if(ec)
+ return fail(ec);
+ buffer_.consume(buffer_.size());
+ do_read();
+ }
+
+ void
+ on_close(error_code ec)
+ {
+ if(ec)
+ return fail(ec);
+ }
+
+ void
+ fail(error_code ec)
+ {
+ boost::ignore_unused(ec);
+#if 0
+ if( ec != error::closed &&
+ ec != error::failed &&
+ ec != net::error::eof)
+ log_ <<
+ "echo_server_async: " <<
+ ec.message() <<
+ std::endl;
+#endif
+ }
+};
+
+} // test
+} // beast
+} // boost
+
+#endif
diff --git a/src/boost/libs/beast/test/extras/include/boost/beast/test/yield_to.hpp b/src/boost/libs/beast/test/extras/include/boost/beast/test/yield_to.hpp
new file mode 100644
index 00000000..e0064830
--- /dev/null
+++ b/src/boost/libs/beast/test/extras/include/boost/beast/test/yield_to.hpp
@@ -0,0 +1,139 @@
+//
+// 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
+//
+
+#ifndef BOOST_BEAST_TEST_YIELD_TO_HPP
+#define BOOST_BEAST_TEST_YIELD_TO_HPP
+
+#include <boost/asio/executor_work_guard.hpp>
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/spawn.hpp>
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+namespace boost {
+namespace beast {
+namespace test {
+
+/** Mix-in to support tests using asio coroutines.
+
+ Derive from this class and use yield_to to launch test
+ functions inside coroutines. This is handy for testing
+ asynchronous asio code.
+*/
+class enable_yield_to
+{
+protected:
+ net::io_context ioc_;
+
+private:
+ net::executor_work_guard<
+ net::io_context::executor_type> work_;
+ std::vector<std::thread> threads_;
+ std::mutex m_;
+ std::condition_variable cv_;
+ std::size_t running_ = 0;
+
+public:
+ /// The type of yield context passed to functions.
+ using yield_context =
+ net::yield_context;
+
+ explicit
+ enable_yield_to(std::size_t concurrency = 1)
+ : work_(ioc_.get_executor())
+ {
+ threads_.reserve(concurrency);
+ while(concurrency--)
+ threads_.emplace_back(
+ [&]{ ioc_.run(); });
+ }
+
+ ~enable_yield_to()
+ {
+ work_.reset();
+ for(auto& t : threads_)
+ t.join();
+ }
+
+ /// Return the `io_context` associated with the object
+ net::io_context&
+ get_io_service()
+ {
+ return ioc_;
+ }
+
+ /** Run one or more functions, each in a coroutine.
+
+ This call will block until all coroutines terminate.
+
+ Each functions should have this signature:
+ @code
+ void f(yield_context);
+ @endcode
+
+ @param fn... One or more functions to invoke.
+ */
+#if BOOST_BEAST_DOXYGEN
+ template<class... FN>
+ void
+ yield_to(FN&&... fn)
+#else
+ template<class F0, class... FN>
+ void
+ yield_to(F0&& f0, FN&&... fn);
+#endif
+
+private:
+ void
+ spawn()
+ {
+ }
+
+ template<class F0, class... FN>
+ void
+ spawn(F0&& f, FN&&... fn);
+};
+
+template<class F0, class... FN>
+void
+enable_yield_to::
+yield_to(F0&& f0, FN&&... fn)
+{
+ running_ = 1 + sizeof...(FN);
+ spawn(f0, fn...);
+ std::unique_lock<std::mutex> lock{m_};
+ cv_.wait(lock, [&]{ return running_ == 0; });
+}
+
+template<class F0, class... FN>
+inline
+void
+enable_yield_to::
+spawn(F0&& f, FN&&... fn)
+{
+ asio::spawn(ioc_,
+ [&](yield_context yield)
+ {
+ f(yield);
+ std::lock_guard<std::mutex> lock{m_};
+ if(--running_ == 0)
+ cv_.notify_all();
+ }
+ , boost::coroutines::attributes(2 * 1024 * 1024));
+ spawn(fn...);
+}
+
+} // test
+} // beast
+} // boost
+
+#endif