diff --git a/CHANGELOG.md b/CHANGELOG.md
index 982bb314..0b17efe5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@ Version 201
* Add bind_back_handler
* Tidy up default-constructed iterators
* Add core errors and conditions
+* New basic_stream_socket
--------------------------------------------------------------------------------
diff --git a/doc/qbk/09_releases.qbk b/doc/qbk/09_releases.qbk
index 000526a2..444162f8 100644
--- a/doc/qbk/09_releases.qbk
+++ b/doc/qbk/09_releases.qbk
@@ -54,7 +54,7 @@
[*Experimental]
-* Add [link beast.ref.boost__beast__timeout_socket `timeout_socket`]
+* Add `timeout_socket`
diff --git a/doc/qbk/quickref.xml b/doc/qbk/quickref.xml
index d097edda..1f2e1f7d 100644
--- a/doc/qbk/quickref.xml
+++ b/doc/qbk/quickref.xml
@@ -179,6 +179,7 @@
basic_flat_bufferbasic_multi_buffer
+ basic_stream_socketbuffered_read_streambuffers_adaptorbuffers_cat_view
@@ -189,14 +190,14 @@
file_posixfile_stdiofile_win32
+ flat_buffer
+ flat_static_buffer
+ flat_static_buffer_base
- flat_buffer
- flat_static_buffer
- flat_static_buffer_basehandler_ptriequaliless
@@ -205,17 +206,24 @@
static_bufferstatic_buffer_basestatic_string
+ stream_socketstring_paramstring_view
+ Constants
+
+ condition
+ error
+ file_mode
+ Functions
+ async_connectbind_back_handlerbind_front_handlerbind_handler
- make_printablebuffers_catbuffers_frontbuffers_prefix
@@ -224,17 +232,12 @@
buffers_to_stringgeneric_categoryiequals
+ make_printableostreamread_sizeread_size_or_throwto_static_string
- Constants
-
- condition
- error
- file_mode
- Type Traits
@@ -312,22 +315,16 @@
Classes
- basic_timeout_socketflat_streamssl_streamhttp::icy_streamtest::fail_counttest::stream
- timeout_handle
- timeout_socket
- timeout_work_guardFunctions
- async_connect
- set_timeout_service_optionstest::connect
diff --git a/include/boost/beast/_experimental/core/detail/impl/timeout_service.hpp b/include/boost/beast/_experimental/core/detail/impl/timeout_service.hpp
deleted file mode 100644
index 4cbbc821..00000000
--- a/include/boost/beast/_experimental/core/detail/impl/timeout_service.hpp
+++ /dev/null
@@ -1,326 +0,0 @@
-//
-// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#ifndef BOOST_BEAST_CORE_DETAIL_IMPL_TIMEOUT_SERVICE_HPP
-#define BOOST_BEAST_CORE_DETAIL_IMPL_TIMEOUT_SERVICE_HPP
-
-namespace boost {
-namespace beast {
-namespace detail {
-
-timeout_service::
-timeout_service(net::io_context& ctx)
- : service_base(ctx)
- , thunks_(1) // element [0] reserved for "null"
- , timer_(ctx)
-{
-}
-
-timeout_handle
-timeout_service::
-make_handle()
-{
- std::lock_guard lock(m_);
- if(free_thunk_ != 0)
- {
- auto const n = free_thunk_;
- auto& t = thunks_[n];
- free_thunk_ = t.pos; // next in free list
- t = {};
- return timeout_handle(n, *this);
- }
- auto const n = thunks_.size();
- thunks_.emplace_back();
- return timeout_handle(n, *this);
-}
-
-void
-timeout_service::
-set_option(std::chrono::seconds n)
-{
- interval_ = n;
-}
-
-template
-void
-timeout_service::
-set_callback(
- timeout_handle h,
- Executor const& ex,
- CancelHandler&& handler)
-{
- thunks_[h.id_].callback.emplace(
- callback<
- Executor,
- typename std::decay::type>{
- h, ex,
- std::forward(handler)});
-}
-
-void
-timeout_service::
-on_work_started(timeout_handle h)
-{
- BOOST_ASSERT(h.id_ != 0);
- if( [this, h]
- {
- std::lock_guard lock(m_);
- auto& t = thunks_[h.id_];
- insert(t, *fresh_);
- return ++pending_ == 1;
- }())
- {
- do_async_wait();
- }
-}
-
-void
-timeout_service::
-on_work_stopped(timeout_handle h)
-{
- BOOST_ASSERT(h.id_ != 0);
- std::lock_guard lock(m_);
- auto& t = thunks_[h.id_];
- if(t.list != nullptr)
- {
- BOOST_ASSERT(! t.expired);
- remove(t);
- }
- if(--pending_ == 0)
- timer_.cancel();
- BOOST_ASSERT(pending_ >= 0);
-}
-
-/*
- Synchronization points
-
- (A) async_op invoke
- (B) timeout handler invoke expired=true
- (C) posted handler invoked canceled=true
-
- ----------------------------------------------
-
- Linearized paths (for async_read)
-
- ----------------------------------------------
-
- 1. async_read
- 2. async_read complete, async_op posted
-(A) 3. async_op invoked
- - work_.try_complete() returns true
- + expired==false
- + thunk is removed from list
-
- ----------------------------------------------
-
- 1. async_read
- 2. async_read complete, async_op posted
-(B) 3. timeout, cancel posted
- - expired=true
- - thunk is removed from list
-(A) 4. async_op invoked
- - work_.try_complete() returns false
- + completed=true
- - handler is saved
-(C) 5. cancel invoked
- - saved handler is invoked
- + expired==true, canceled==false, completed==true
- + work_.try_complete() returns true
-
- ----------------------------------------------
-
- The following two paths are not distinguishable:
-
- 1. async_read
-(B) 2. timeout, cancel posted
- - expired=true
- - thunk is removed from list
- 3. async_read complete, async_op posted
-(C) 4. cancel invoked
- - socket::cancel called (what does this do?)
- - canceled=true
-(A) 5. async_op invoked
- - expired==true, canceled==true, completed==false
- - work_.try_complete() returns `true`
-
- 1. async_read
-(B) 2. timeout, `cancel` posted
- - expired=true
- - thunk is removed from list
-(C) 3. cancel invoked, async_read canceled
- - socket::cancel called
- - canceled=true
-(A) 4. async_op invoked, ec==operation_aborted
- - expired==true, canceled==true, completed=false
- - work_.try_complete()` returns true
-*/
-bool
-timeout_service::
-on_try_work_complete(timeout_handle h)
-{
- BOOST_ASSERT(h.id_ != 0);
- std::lock_guard lock(m_);
- auto& t = thunks_[h.id_];
- if(! t.expired)
- {
- // hot path: operation complete
- BOOST_ASSERT(t.list != nullptr);
- BOOST_ASSERT(! t.canceled);
- BOOST_ASSERT(! t.completed);
- remove(t);
- return true;
- }
- BOOST_ASSERT(t.list == nullptr);
- if(! t.canceled)
- {
- // happens when operation completes before
- // posted cancel handler is invoked.
- t.completed = true;
- return false;
- }
- if(t.completed)
- {
- // happens when the saved handler is
- // invoked from the posted callback
- t.expired = false;
- t.canceled = false;
- t.completed = false;
- return true;
- }
- // happens when operation_aborted is delivered
- t.expired = false;
- t.canceled = false;
- return true;
-}
-
-void
-timeout_service::
-on_cancel(timeout_handle h)
-{
- std::lock_guard lock(m_);
- auto& t = thunks_[h.id_];
- BOOST_ASSERT(t.expired);
- t.canceled = true;
-}
-
-//------------------------------------------------------------------------------
-
-void
-timeout_service::
-destroy(timeout_handle h)
-{
- BOOST_ASSERT(h.id_ != 0);
- std::lock_guard lock(m_);
- thunks_[h.id_].pos = free_thunk_;
- free_thunk_ = h.id_;
-}
-
-// Precondition: caller holds the mutex
-void
-timeout_service::
-insert(
- thunk& t,
- thunk::list_type& list)
-{
- BOOST_ASSERT(t.list == nullptr);
- list.emplace_back(&t); // can throw
- t.list = &list;
- t.pos = list.size();
-}
-
-// Precondition: caller holds the mutex
-void
-timeout_service::
-remove(thunk& t)
-{
- BOOST_ASSERT(t.list != nullptr);
- BOOST_ASSERT(
- t.list == stale_ ||
- t.list == fresh_);
- BOOST_ASSERT(t.list->size() > 0);
- auto& list = *t.list;
- auto const n = list.size() - 1;
- if(t.pos != n)
- {
- // move back element to t.pos
- list[t.pos] = list[n];
- list[t.pos]->pos = t.pos;
- }
- t.list = nullptr;
- list.resize(n);
-}
-
-void
-timeout_service::
-do_async_wait()
-{
- timer_.expires_after(interval_);
- timer_.async_wait(
- [this](error_code ec)
- {
- this->on_timer(ec);
- });
-}
-
-void
-timeout_service::
-on_timer(error_code ec)
-{
- if(ec == net::error::operation_aborted)
- {
- BOOST_ASSERT(fresh_->empty());
- BOOST_ASSERT(stale_->empty());
- return;
- }
- std::vector expired;
- {
- std::lock_guard lock(m_);
- if(! stale_->empty())
- {
- for(auto t : *stale_)
- {
- // remove from list
- t->list = nullptr;
- t->expired = true;
- }
- std::swap(expired, *stale_);
- stale_->reserve(expired.size() / 2);
- }
- std::swap(fresh_, stale_);
- }
- for(auto p : expired)
- p->callback();
- if( [this]
- {
- std::lock_guard lock(m_);
- BOOST_ASSERT(pending_);
- pending_ =
- ! stale_->empty() ||
- ! fresh_->empty();
- return pending_;
- }())
- {
- do_async_wait();
- }
-}
-
-void
-timeout_service::
-shutdown() noexcept
-{
- // The ExecutionContext is already in a stopped
- // state, so no synchronization is required.
- timer_.cancel();
-}
-
-} // detail
-} // beast
-} // boost
-
-#endif
diff --git a/include/boost/beast/_experimental/core/detail/saved_handler.hpp b/include/boost/beast/_experimental/core/detail/saved_handler.hpp
index d1d000c8..6990cb0e 100644
--- a/include/boost/beast/_experimental/core/detail/saved_handler.hpp
+++ b/include/boost/beast/_experimental/core/detail/saved_handler.hpp
@@ -59,11 +59,10 @@ public:
std::forward(h)));
}
- template
+ template<
+ class Handler, class T0, class... TN>
void
- emplace(Handler&& h,
- T0&& t0, TN&... tn)
+ emplace(Handler&& h, T0&& t0, TN&&... tn)
{
using type = decltype(
beast::bind_front_handler(
diff --git a/include/boost/beast/_experimental/core/detail/timeout_service.hpp b/include/boost/beast/_experimental/core/detail/timeout_service.hpp
deleted file mode 100644
index 7637fb79..00000000
--- a/include/boost/beast/_experimental/core/detail/timeout_service.hpp
+++ /dev/null
@@ -1,133 +0,0 @@
-//
-// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#ifndef BOOST_BEAST_CORE_DETAIL_TIMEOUT_SERVICE_HPP
-#define BOOST_BEAST_CORE_DETAIL_TIMEOUT_SERVICE_HPP
-
-#include
-#include
-#include
-#include
-#include
-#include // #include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace boost {
-namespace beast {
-
-class timeout_handle;
-
-namespace detail {
-
-class timeout_service
- : public service_base
-{
- template
- struct callback
- {
- timeout_handle th;
- Executor ex;
- Handler h;
-
- void
- operator()()
- {
- net::post(ex,
- beast::bind_front_handler(
- std::move(*this), 0));
- }
-
- void
- operator()(int)
- {
- th.service().on_cancel(th);
- h();
- }
- };
-
-public:
- using key_type = timeout_service;
-
- // VFALCO Should be execution_context
- BOOST_BEAST_DECL
- explicit
- timeout_service(net::io_context& ctx);
-
- BOOST_BEAST_DECL
- timeout_handle
- make_handle();
-
- BOOST_BEAST_DECL
- void set_option(std::chrono::seconds n);
-
- // Undefined if work is active
- template
- void set_callback(
- timeout_handle h,
- Executor const& ex,
- CancelHandler&& handler);
-
- BOOST_BEAST_DECL
- void on_work_started(timeout_handle h);
-
- BOOST_BEAST_DECL
- void on_work_stopped(timeout_handle h);
-
- BOOST_BEAST_DECL
- bool on_try_work_complete(timeout_handle h);
-
-private:
- friend class beast::timeout_handle;
-
- BOOST_BEAST_DECL
- void destroy(timeout_handle h);
-
- BOOST_BEAST_DECL
- void insert(thunk& t, thunk::list_type& list);
-
- BOOST_BEAST_DECL
- void remove(thunk& t);
-
- BOOST_BEAST_DECL
- void do_async_wait();
-
- BOOST_BEAST_DECL
- void on_cancel(timeout_handle h);
-
- BOOST_BEAST_DECL
- void on_timer(error_code ec);
-
- BOOST_BEAST_DECL
- virtual void shutdown() noexcept override;
-
- std::mutex m_;
- thunk::list_type list_[2];
- thunk::list_type* fresh_ = &list_[0];
- thunk::list_type* stale_ = &list_[1];
- std::deque thunks_;
- std::size_t free_thunk_ = 0;
- net::steady_timer timer_;
- std::chrono::seconds interval_{30ul};
- long pending_ = 0;
-};
-
-} // detail
-} // beast
-} // boost
-
-#include
-
-#endif
diff --git a/include/boost/beast/_experimental/core/detail/timeout_service_base.hpp b/include/boost/beast/_experimental/core/detail/timeout_service_base.hpp
deleted file mode 100644
index ded1cf2d..00000000
--- a/include/boost/beast/_experimental/core/detail/timeout_service_base.hpp
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#ifndef BOOST_BEAST_CORE_DETAIL_TIMEOUT_SERVICE_BASE_HPP
-#define BOOST_BEAST_CORE_DETAIL_TIMEOUT_SERVICE_BASE_HPP
-
-#include
-
-#include
-
-namespace boost {
-namespace beast {
-namespace detail {
-
-struct thunk
-{
- using list_type =
- std::vector;
-
- saved_handler callback;
- list_type* list = nullptr;
- std::size_t pos = 0; // also: next in free list
- bool expired = false;
- bool canceled = false;
- bool completed = false;
-};
-
-} // detail
-} // beast
-} // boost
-
-#endif
diff --git a/include/boost/beast/_experimental/core/impl/timeout_service.hpp b/include/boost/beast/_experimental/core/impl/timeout_service.hpp
deleted file mode 100644
index 61aeda06..00000000
--- a/include/boost/beast/_experimental/core/impl/timeout_service.hpp
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#ifndef BOOST_BEAST_CORE_IMPL_TIMEOUT_SERVICE_HPP
-#define BOOST_BEAST_CORE_IMPL_TIMEOUT_SERVICE_HPP
-
-#include
-#include
-#include
-
-namespace boost {
-namespace beast {
-
-timeout_handle::
-timeout_handle(net::io_context& ioc)
- : timeout_handle(
- net::use_service<
- detail::timeout_service>(
- ioc).make_handle())
-{
-}
-
-void
-timeout_handle::
-destroy()
-{
- BOOST_ASSERT(svc_ != nullptr);
- svc_->destroy(*this);
- id_ = 0;
- svc_ = nullptr;
-}
-
-template
-void
-timeout_handle::
-set_callback(
- Executor const& ex, CancelHandler&& handler)
-{
- svc_->set_callback(*this, ex,
- std::forward(handler));
-}
-
-//------------------------------------------------------------------------------
-
-void
-set_timeout_service_options(
- net::io_context& ioc,
- std::chrono::seconds interval)
-{
- net::use_service<
- detail::timeout_service>(
- ioc).set_option(interval);
-}
-
-} // beast
-} // boost
-
-#endif
diff --git a/include/boost/beast/_experimental/core/impl/timeout_socket.hpp b/include/boost/beast/_experimental/core/impl/timeout_socket.hpp
deleted file mode 100644
index d2ef676b..00000000
--- a/include/boost/beast/_experimental/core/impl/timeout_socket.hpp
+++ /dev/null
@@ -1,478 +0,0 @@
-//
-// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#ifndef BOOST_BEAST_CORE_IMPL_TIMEOUT_SOCKET_HPP
-#define BOOST_BEAST_CORE_IMPL_TIMEOUT_SOCKET_HPP
-
-#include
-#include
-#include
-#include
-#include
-
-namespace boost {
-namespace beast {
-
-template
-template
-class basic_timeout_socket::async_op
-{
-public:
- async_op(async_op&&) = default;
- async_op(async_op const&) = delete;
-
- template
- async_op(
- Buffers const& b,
- DeducedHandler&& h,
- basic_timeout_socket& s,
- std::true_type)
- : h_(std::forward(h))
- , s_(s)
- , work_(s.rd_timer_)
- , wg0_(s_.get_executor())
- , wg1_(get_executor())
- , saved_(s_.rd_op_)
-
- {
- s_.sock_.async_read_some(b, std::move(*this));
- }
-
- template
- async_op(
- Buffers const& b,
- DeducedHandler&& h,
- basic_timeout_socket& s,
- std::false_type)
- : h_(std::forward(h))
- , s_(s)
- , work_(s.wr_timer_)
- , wg0_(s_.get_executor())
- , wg1_(get_executor())
- , saved_(s_.wr_op_)
- {
- s_.sock_.async_write_some(b, std::move(*this));
- }
-
- void
- operator()(error_code ec, std::size_t bytes_transferred)
- {
- if(! work_.try_complete())
- {
- saved_.emplace(
- std::move(*this),
- ec,
- bytes_transferred);
- return;
- }
- h_(ec, bytes_transferred);
- }
-
- //
-
- using allocator_type =
- net::associated_allocator_t;
-
- using executor_type =
- net::associated_executor_t&>().get_executor())>;
-
- allocator_type
- get_allocator() const noexcept
- {
- return net::get_associated_allocator(h_);
- }
-
- executor_type
- get_executor() const noexcept
- {
- return net::get_associated_executor(
- h_, s_.get_executor());
- }
-
- template
- friend
- void asio_handler_invoke(Function&& f, async_op* op)
- {
- using net::asio_handler_invoke;
- asio_handler_invoke(f, std::addressof(op->h_));
- }
-
- friend
- void* asio_handler_allocate(
- std::size_t size, async_op* op)
- {
- using net::asio_handler_allocate;
- return asio_handler_allocate(
- size, std::addressof(op->h_));
- }
-
- friend
- void asio_handler_deallocate(
- void* p, std::size_t size, async_op* op)
- {
- using net::asio_handler_deallocate;
- asio_handler_deallocate(
- p, size, std::addressof(op->h_));
- }
-
- friend
- bool asio_handler_is_continuation(async_op* op)
- {
- using net::asio_handler_is_continuation;
- return asio_handler_is_continuation(
- std::addressof(op->h_));
- }
-
-private:
- Handler h_;
- basic_timeout_socket& s_;
- timeout_work_guard work_;
- net::executor_work_guard wg0_;
- net::executor_work_guard wg1_;
- detail::saved_handler& saved_;
-};
-
-//------------------------------------------------------------------------------
-
-template
-template
-basic_timeout_socket::
-basic_timeout_socket(ExecutionContext& ctx)
- : ex_(ctx.get_executor())
- , rd_timer_(ctx)
- , wr_timer_(ctx)
- , cn_timer_(ctx)
- , sock_(ctx)
-{
- rd_timer_.set_callback(ex_,
- [this]
- {
- if(rd_op_.empty())
- sock_.cancel();
- else
- rd_op_();
- });
-
- wr_timer_.set_callback(ex_,
- [this]
- {
- if(wr_op_.empty())
- sock_.cancel();
- else
- wr_op_();
- });
-
- cn_timer_.set_callback(ex_,
- [this]
- {
- if(cn_op_.empty())
- sock_.cancel();
- else
- cn_op_();
- });
-}
-
-template
-basic_timeout_socket::
-~basic_timeout_socket()
-{
- rd_timer_.destroy();
- wr_timer_.destroy();
-}
-
-template
-template
-BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
- void(error_code, std::size_t))
-basic_timeout_socket::
-async_read_some(
- MutableBufferSequence const& buffers,
- ReadHandler&& handler)
-{
- static_assert(net::is_mutable_buffer_sequence<
- MutableBufferSequence>::value,
- "MutableBufferSequence requirements not met");
- BOOST_BEAST_HANDLER_INIT(
- ReadHandler, void(error_code, std::size_t));
- async_op(buffers,
- std::forward(handler), *this,
- std::true_type{});
- return init.result.get();
-}
-
-template
-template
-BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
- void(error_code, std::size_t))
-basic_timeout_socket::
-async_write_some(
- ConstBufferSequence const& buffers,
- WriteHandler&& handler)
-{
- static_assert(net::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence requirements not met");
- BOOST_BEAST_HANDLER_INIT(
- WriteHandler, void(error_code, std::size_t));
- async_op(buffers,
- std::forward(handler), *this,
- std::false_type{});
- return init.result.get();
-}
-
-//------------------------------------------------------------------------------
-
-namespace detail {
-
-template<
- class Protocol, class Executor,
- class Handler>
-class connect_op
-{
-public:
- template<
- class Endpoints,
- class Condition,
- class DeducedHandler>
- connect_op(
- basic_timeout_socket& s,
- Endpoints const& eps,
- Condition cond,
- DeducedHandler&& h)
- : h_(std::forward(h))
- , work_(s.cnd_timer_)
- , s_(s)
- , wg0_(s_.get_executor())
- , wg1_(get_executor())
- {
- net::async_connect(
- s_.next_layer(), eps, cond,
- std::move(*this));
- }
-
- template
- void
- operator()(error_code ec, Arg&& arg)
- {
- if(! work_.try_complete())
- {
- s_.cn_op_.emplace(
- std::move(*this),
- ec,
- std::forward(arg));
- return;
- }
- h_(ec, std::forward(arg));
- }
-
- //
-
- using allocator_type =
- net::associated_allocator_t;
-
- using executor_type =
- net::associated_executor_t&>().get_executor())>;
-
- allocator_type
- get_allocator() const noexcept
- {
- return net::get_associated_allocator(h_);
- }
-
- executor_type
- get_executor() const noexcept
- {
- return net::get_associated_executor(
- h_, s_.get_executor());
- }
-
- template
- friend
- void asio_handler_invoke(
- Function&& f, connect_op* op)
- {
- using net::asio_handler_invoke;
- asio_handler_invoke(
- f, std::addressof(op->h_));
- }
-
- friend
- void* asio_handler_allocate(
- std::size_t size, connect_op* op)
- {
- using net::asio_handler_allocate;
- return asio_handler_allocate(
- size, std::addressof(op->h_));
- }
-
- friend
- void asio_handler_deallocate(
- void* p, std::size_t size, connect_op* op)
- {
- using net::asio_handler_deallocate;
- asio_handler_deallocate(
- p, size, std::addressof(op->h_));
- }
-
- friend
- bool asio_handler_is_continuation(connect_op* op)
- {
- using net::asio_handler_is_continuation;
- return asio_handler_is_continuation(
- std::addressof(op->h_));
- }
-
-private:
- Handler h_;
- timeout_work_guard work_;
- basic_timeout_socket& s_;
- net::executor_work_guard wg0_;
- net::executor_work_guard wg1_;
-};
-
-struct any_endpoint
-{
- template
- bool
- operator()(
- Error const&, Endpoint const&) const noexcept
- {
- return true;
- }
-};
-
-template
-struct endpoint_range_type
-{
- using const_iterator = Iterator;
-
- Iterator begin_;
- Iterator end_;
-
- Iterator begin() const noexcept
- {
- return begin_;
- }
-
- Iterator end() const noexcept
- {
- return end_;
- }
-};
-
-template
-endpoint_range_type
-endpoint_range(Iterator first, Iterator last)
-{
- return endpoint_range_type<
- Iterator>(first, last);
-}
-
-} // detail
-
-template<
- class Protocol, class Executor,
- class EndpointSequence,
- class RangeConnectHandler, class>
-BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
- void(error_code, typename Protocol::endpoint))
-async_connect(
- basic_timeout_socket& s,
- EndpointSequence const& endpoints,
- RangeConnectHandler&& handler)
-{
- BOOST_BEAST_HANDLER_INIT(RangeConnectHandler,
- void(error_code, typename Protocol::endpoint));
- detail::connect_op(
- s, endpoints, detail::any_endpoint{},
- std::forward(handler));
- return init.result.get();
-}
-
-template<
- class Protocol, class Executor,
- class EndpointSequence,
- class ConnectCondition,
- class RangeConnectHandler, class>
-BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
- void (error_code, typename Protocol::endpoint))
-async_connect(
- basic_timeout_socket& s,
- EndpointSequence const& endpoints,
- ConnectCondition connect_condition,
- RangeConnectHandler&& handler)
-{
- BOOST_BEAST_HANDLER_INIT(RangeConnectHandler,
- void(error_code, typename Protocol::endpoint));
- detail::connect_op(
- s, endpoints, connect_condition,
- std::forward(handler));
- return init.result.get();
-}
-
-template<
- class Protocol, class Executor,
- class Iterator,
- class IteratorConnectHandler, class>
-BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
- void (error_code, Iterator))
-async_connect(
- basic_timeout_socket& s,
- Iterator begin, Iterator end,
- IteratorConnectHandler&& handler)
-{
- BOOST_BEAST_HANDLER_INIT(IteratorConnectHandler,
- void(error_code, Iterator));
- detail::connect_op(
- s, detail::endpoint_range(begin, end), detail::any_endpoint{},
- std::forward(handler));
- return init.result.get();
-}
-
-template<
- class Protocol, class Executor,
- class Iterator,
- class ConnectCondition,
- class IteratorConnectHandler, class>
-BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
- void (error_code, Iterator))
-async_connect(
- basic_timeout_socket& s,
- Iterator begin, Iterator end,
- ConnectCondition connect_condition,
- IteratorConnectHandler&& handler)
-{
- BOOST_BEAST_HANDLER_INIT(IteratorConnectHandler,
- void(error_code, Iterator));
- detail::connect_op(
- s, detail::endpoint_range(begin, end), connect_condition,
- std::forward(handler));
- return init.result.get();
-}
-
-} // beast
-} // boost
-
-#endif
diff --git a/include/boost/beast/_experimental/core/timeout_service.hpp b/include/boost/beast/_experimental/core/timeout_service.hpp
deleted file mode 100644
index 7251acdd..00000000
--- a/include/boost/beast/_experimental/core/timeout_service.hpp
+++ /dev/null
@@ -1,135 +0,0 @@
-//
-// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#ifndef BOOST_BEAST_CORE_TIMEOUT_SERVICE_HPP
-#define BOOST_BEAST_CORE_TIMEOUT_SERVICE_HPP
-
-#include
-#include // #include
-#include
-
-namespace boost {
-namespace beast {
-
-namespace detail {
-class timeout_service;
-} // detail
-
-class timeout_work_guard;
-
-class timeout_handle
-{
- std::size_t id_ = 0;
- detail::timeout_service* svc_ = nullptr;
-
- timeout_handle(
- std::size_t id,
- detail::timeout_service& svc)
- : id_(id)
- , svc_(&svc)
- {
- }
-
- detail::timeout_service&
- service() const
- {
- return *svc_;
- }
-
- friend class detail::timeout_service;
- friend class timeout_work_guard;
-
-public:
- timeout_handle() = default;
- timeout_handle(timeout_handle const&) = default;
- timeout_handle& operator=(timeout_handle const&) = default;
-
- timeout_handle(std::nullptr_t)
- {
- }
-
- timeout_handle&
- operator=(std::nullptr_t)
- {
- id_ = 0;
- svc_ = nullptr;
- return *this;
- }
-
- // VFALCO should be execution_context
- BOOST_BEAST_DECL
- explicit
- timeout_handle(net::io_context& ioc);
-
- BOOST_BEAST_DECL
- void
- destroy();
-
- template
- void
- set_callback(
- Executor const& ex, CancelHandler&& handler);
-
- explicit
- operator bool() const noexcept
- {
- return svc_ != nullptr;
- }
-
- friend bool operator==(
- timeout_handle const& lhs,
- std::nullptr_t) noexcept
- {
- return lhs.svc_ == nullptr;
- }
-
- friend bool operator==(
- std::nullptr_t,
- timeout_handle const& rhs) noexcept
- {
- return rhs.svc_ == nullptr;
- }
-
- friend bool operator!=(
- timeout_handle const& lhs,
- std::nullptr_t) noexcept
- {
- return lhs.svc_ != nullptr;
- }
-
- friend bool operator!=(
- std::nullptr_t,
- timeout_handle const& rhs) noexcept
- {
- return rhs.svc_ != nullptr;
- }
-};
-
-/** Set timeout service options in an execution context.
-
- This changes the time interval for all timeouts associated
- with the execution context. The option must be set before any
- timeout objects are constructed.
-
- @param ctx The execution context.
-
- @param interval The approximate amount of time until a timeout occurs.
-*/
-BOOST_BEAST_DECL
-void
-set_timeout_service_options(
- net::io_context& ctx, // VFALCO should be execution_context
- std::chrono::seconds interval);
-
-} // beast
-} // boost
-
-#include
-
-#endif
diff --git a/include/boost/beast/_experimental/core/timeout_socket.hpp b/include/boost/beast/_experimental/core/timeout_socket.hpp
deleted file mode 100644
index 69308847..00000000
--- a/include/boost/beast/_experimental/core/timeout_socket.hpp
+++ /dev/null
@@ -1,632 +0,0 @@
-//
-// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#ifndef BOOST_BEAST_CORE_TIMEOUT_SOCKET_HPP
-#define BOOST_BEAST_CORE_TIMEOUT_SOCKET_HPP
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace boost {
-namespace asio {
-namespace ip {
-class tcp;
-} // ip
-} // asio
-} // boost
-
-namespace boost {
-namespace beast {
-
-namespace detail {
-template
-class connect_op;
-} // detail
-
-/** A socket wrapper which automatically times out on asynchronous reads.
-
- This wraps a normal stream socket and implements a simple and efficient
- timeout for asynchronous read operations.
-
- @note Meets the requirements of @b AsyncReadStream and @b AsyncWriteStream
-*/
-template<
- class Protocol,
- class Executor = net::executor
->
-class basic_timeout_socket
-{
- template class async_op;
- template
- friend class detail::connect_op;
-
- Executor ex_; // must come first
- timeout_handle rd_timer_;
- timeout_handle wr_timer_;
- timeout_handle cn_timer_;
- detail::saved_handler rd_op_;
- detail::saved_handler wr_op_;
- detail::saved_handler cn_op_;
- net::basic_stream_socket sock_;
-
-public:
- /// The type of the next layer.
- using next_layer_type = net::basic_stream_socket;
-
- /// The type of the lowest layer.
- using lowest_layer_type = get_lowest_layer;
-
- /// The protocol used by the stream.
- using protocol_type = Protocol;
-
- /// The type of the executor associated with the object.
- using executor_type = Executor;
-
- /** Destructor
-
- The behavior of destruction while asynchronous operations
- are pending is undefined.
- */
- ~basic_timeout_socket();
-
- // VFALCO we only support default-construction
- // of the contained socket for now.
- // This constructor needs a protocol parameter.
- //
- /** Constructor
- */
- template::value &&
- std::is_constructible<
- executor_type,
- typename ExecutionContext::executor_type>::value
- >::type
-#endif
- >
- explicit
- basic_timeout_socket(ExecutionContext& ctx);
-
- //--------------------------------------------------------------------------
-
- /** Get the executor associated with the object.
-
- This function may be used to obtain the executor object that the
- stream uses to dispatch handlers for asynchronous operations.
-
- @return A copy of the executor that stream will use to dispatch handlers.
- */
- executor_type
- get_executor() const noexcept
- {
- return ex_;
- }
-
- /** Get a reference to the next layer
-
- This function returns a reference to the next layer
- in a stack of stream layers.
-
- @return A reference to the next layer in the stack of
- stream layers.
- */
- next_layer_type&
- next_layer() noexcept
- {
- return sock_;
- }
-
- /** Get a reference to the next layer
-
- This function returns a reference to the next layer in a
- stack of stream layers.
-
- @return A reference to the next layer in the stack of
- stream layers.
- */
- next_layer_type const&
- next_layer() const noexcept
- {
- return sock_;
- }
-
- /** Get a reference to the lowest layer
-
- This function returns a reference to the lowest layer
- in a stack of stream layers.
-
- @return A reference to the lowest layer in the stack of
- stream layers.
- */
- lowest_layer_type&
- lowest_layer() noexcept
- {
- return sock_.lowest_layer();
- }
-
- /** Get a reference to the lowest layer
-
- This function returns a reference to the lowest layer
- in a stack of stream layers.
-
- @return A reference to the lowest layer in the stack of
- stream layers. Ownership is not transferred to the caller.
- */
- lowest_layer_type const&
- lowest_layer() const noexcept
- {
- return sock_.lowest_layer();
- }
-
- //--------------------------------------------------------------------------
-
- /** Start an asynchronous read.
-
- This function is used to asynchronously read data from the stream socket.
- The function call always returns immediately.
-
- @param buffers One or more buffers into which the data will be read.
- Although the buffers object may be copied as necessary, ownership of the
- underlying memory blocks is retained by the caller, which must guarantee
- that they remain valid until the handler is called.
-
- @param handler The handler to be called when the read operation completes.
- Copies will be made of the handler as required. The function signature of
- the handler must be:
- @code void handler(
- const boost::system::error_code& error, // Result of operation.
- std::size_t bytes_transferred // Number of bytes read.
- ); @endcode
- Regardless of whether the asynchronous operation completes immediately or
- not, the handler will not be invoked from within this function. Invocation
- of the handler will be performed in a manner equivalent to using
- net::io_context::post().
- */
- template
- BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
- void(boost::system::error_code, std::size_t))
- async_read_some(
- MutableBufferSequence const& buffers,
- ReadHandler&& handler);
-
- /** Start an asynchronous write.
-
- This function is used to asynchronously write data to the stream socket.
- The function call always returns immediately.
-
- @param buffers One or more data buffers to be written to the socket.
- Although the buffers object may be copied as necessary, ownership of the
- underlying memory blocks is retained by the caller, which must guarantee
- that they remain valid until the handler is called.
-
- @param handler The handler to be called when the write operation completes.
- Copies will be made of the handler as required. The function signature of
- the handler must be:
- @code void handler(
- const boost::system::error_code& error, // Result of operation.
- std::size_t bytes_transferred // Number of bytes written.
- ); @endcode
- Regardless of whether the asynchronous operation completes immediately or
- not, the handler will not be invoked from within this function. Invocation
- of the handler will be performed in a manner equivalent to using
- net::io_context::post().
- */
- template
- BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
- void(boost::system::error_code, std::size_t))
- async_write_some(
- ConstBufferSequence const& buffers,
- WriteHandler&& handler);
-};
-
-//------------------------------------------------------------------------------
-
-/// A TCP/IP socket wrapper which has a built-in asynchronous timeout
-using timeout_socket = basic_timeout_socket<
- net::ip::tcp,
- net::io_context::executor_type>;
-
-/**
- @defgroup async_connect boost::beast::async_connect
-
- @brief Asynchronously establishes a socket connection by trying each
- endpoint in a sequence, and terminating if a timeout occurs.
-*/
-/* @{ */
-/** Asynchronously establishes a socket connection by trying each endpoint in a sequence.
-
- This function attempts to connect a socket to one of a sequence of
- endpoints. It does this by repeated calls to the underlying socket's
- @c async_connect member function, once for each endpoint in the sequence,
- until a connection is successfully established or a timeout occurs.
-
- @param s The @ref basic_timeout_socket to be connected. If the socket
- is already open, it will be closed.
-
- @param endpoints A sequence of endpoints.
-
- @param handler The handler to be called when the connect operation
- completes. Ownership of the handler may be transferred. The function
- signature of the handler must be:
- @code
- void handler(
- // Result of operation. if the sequence is empty, set to
- // net::error::not_found. Otherwise, contains the
- // error from the last connection attempt.
- error_code const& error,
-
- // On success, the successfully connected endpoint.
- // Otherwise, a default-constructed endpoint.
- typename Protocol::endpoint const& endpoint
- );
- @endcode
-
- Regardless of whether the asynchronous operation completes immediately or
- not, the handler will not be invoked from within this function. Invocation
- of the handler will be performed in a manner equivalent to using
- `net::io_context::post()`.
-
- @par Example
-
- @code
- net::tcp::resolver r(ioc);
- net::tcp::resolver::query q("host", "service");
- timeout_socket s(ioc.get_executor());
-
- // ...
-
- r.async_resolve(q, resolve_handler);
-
- // ...
-
- void resolve_handler(
- boost::system::error_code const& ec,
- tcp::resolver::results_type results)
- {
- if (!ec)
- {
- async_connect(s, results, connect_handler);
- }
- }
-
- // ...
-
- void connect_handler(
- boost::system::error_code const& ec,
- tcp::endpoint const& endpoint)
- {
- // ...
- }
- @endcode
-*/
-template<
- class Protocol, class Executor,
- class EndpointSequence,
- class RangeConnectHandler
-#if ! BOOST_BEAST_DOXYGEN
- ,class = typename std::enable_if<
- net::is_endpoint_sequence<
- EndpointSequence>::value>::type
-#endif
->
-BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
- void (boost::system::error_code, class Protocol::endpoint))
-async_connect(
- basic_timeout_socket& s,
- EndpointSequence const& endpoints,
- RangeConnectHandler&& handler);
-
-/** Asynchronously establishes a socket connection by trying each endpoint in a sequence.
-
- This function attempts to connect a socket to one of a sequence of
- endpoints. It does this by repeated calls to the underlying socket's
- @c async_connect member function, once for each endpoint in the sequence,
- until a connection is successfully established or a timeout occurs.
-
- @param s The @ref basic_timeout_socket to be connected. If the socket
- is already open, it will be closed.
-
- @param endpoints A sequence of endpoints.
-
- @param connect_condition A function object that is called prior to each
- connection attempt. The signature of the function object must be:
-
- @code
- bool connect_condition(
- boost::system::error_code const& ec,
- typename Protocol::endpoint const& next);
- @endcode
-
- The @c ec parameter contains the result from the most recent connect
- operation. Before the first connection attempt, @c ec is always set to
- indicate success. The @c next parameter is the next endpoint to be tried.
- The function object should return true if the next endpoint should be tried,
- and false if it should be skipped.
-
- @param handler The handler to be called when the connect operation
- completes. Ownership of the handler may be transferred. The function
- signature of the handler must be:
- @code
- void handler(
- // Result of operation. if the sequence is empty, set to
- // net::error::not_found. Otherwise, contains the
- // error from the last connection attempt.
- error_code const& error,
-
- // On success, the successfully connected endpoint.
- // Otherwise, a default-constructed endpoint.
- typename Protocol::endpoint const& endpoint
- );
- @endcode
-
- Regardless of whether the asynchronous operation completes immediately or
- not, the handler will not be invoked from within this function. Invocation
- of the handler will be performed in a manner equivalent to using
- `net::io_context::post()`.
-
- @par Example
-
- The following connect condition function object can be used to output
- information about the individual connection attempts:
-
- @code
- struct my_connect_condition
- {
- bool operator()(
- boost::system::error_code const& ec,
- net::ip::tcp::endpoint const& next)
- {
- if (ec) std::cout << "Error: " << ec.message() << std::endl;
- std::cout << "Trying: " << next << std::endl;
- return true;
- }
- };
- @endcode
-
- It would be used with the @ref boost::beast::async_connect
- function as follows:
-
- @code
- net::tcp::resolver r(ioc);
- net::tcp::resolver::query q("host", "service");
- timeout_socket s(ioc.get_executor());
-
- // ...
-
- r.async_resolve(q, resolve_handler);
-
- // ...
-
- void resolve_handler(
- boost::system::error_code const& ec,
- tcp::resolver::results_type results)
- {
- if (!ec)
- {
- async_connect(s, results, my_connect_condition{}, connect_handler);
- }
- }
-
- // ...
-
- void connect_handler(
- boost::system::error_code const& ec,
- tcp::endpoint const& endpoint)
- {
- // ...
- }
- @endcode
-*/
-template<
- class Protocol, class Executor,
- class EndpointSequence,
- class ConnectCondition,
- class RangeConnectHandler
-#if ! BOOST_BEAST_DOXYGEN
- ,class = typename std::enable_if<
- net::is_endpoint_sequence<
- EndpointSequence>::value>::type
-#endif
->
-BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
- void (boost::system::error_code, class Protocol::endpoint))
-async_connect(
- basic_timeout_socket& s,
- EndpointSequence const& endpoints,
- ConnectCondition connect_condition,
- RangeConnectHandler&& handler);
-
-/** Asynchronously establishes a socket connection by trying each endpoint in a sequence.
-
- This function attempts to connect a socket to one of a sequence of
- endpoints. It does this by repeated calls to the underlying socket's
- @c async_connect member function, once for each endpoint in the sequence,
- until a connection is successfully established or a timeout occurs.
-
- @param s The @ref timeout_socket to be connected. If the socket
- is already open, it will be closed.
-
- @param begin An iterator pointing to the start of a sequence of endpoints.
-
- @param end An iterator pointing to the end of a sequence of endpoints.
-
- @param handler The handler to be called when the connect operation
- completes. Ownership of the handler may be transferred. The function
- signature of the handler must be:
- @code
- void handler(
- // Result of operation. if the sequence is empty, set to
- // net::error::not_found. Otherwise, contains the
- // error from the last connection attempt.
- error_code const& error,
-
- // On success, an iterator denoting the successfully
- // connected endpoint. Otherwise, the end iterator.
- Iterator iterator
- );
- @endcode
-
- Regardless of whether the asynchronous operation completes immediately or
- not, the handler will not be invoked from within this function. Invocation
- of the handler will be performed in a manner equivalent to using
- `net::io_context::post()`.
-
- @par Example
-
- @code
- std::vector endpoints = ...;
- timeout_socket s(ioc.get_executor());
-
- async_connect(s,
- endpoints.begin(), endpoints.end(),
- connect_handler);
-
- void connect_handler(
- boost::system::error_code const& ec,
- std::vector::iterator)
- {
- // ...
- }
- @endcode
-*/
-template<
- class Protocol, class Executor,
- class Iterator,
- class IteratorConnectHandler
-#if ! BOOST_BEAST_DOXYGEN
- ,class = typename std::enable_if<
- ! net::is_endpoint_sequence<
- Iterator>::value>::type
-#endif
->
-BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
- void (boost::system::error_code, Iterator))
-async_connect(
- basic_timeout_socket& s,
- Iterator begin, Iterator end,
- IteratorConnectHandler&& handler);
-
-/** Asynchronously establishes a socket connection by trying each endpoint in a sequence.
-
- This function attempts to connect a socket to one of a sequence of
- endpoints. It does this by repeated calls to the underlying socket's
- @c async_connect member function, once for each endpoint in the sequence,
- until a connection is successfully established or a timeout occurs.
-
- @param s The @ref basic_timeout_socket to be connected. If the socket
- is already open, it will be closed.
-
- @param begin An iterator pointing to the start of a sequence of endpoints.
-
- @param end An iterator pointing to the end of a sequence of endpoints.
-
- @param connect_condition A function object that is called prior to each
- connection attempt. The signature of the function object must be:
-
- @code
- bool connect_condition(
- boost::system::error_code const& ec,
- Iterator next);
- @endcode
-
- @param handler The handler to be called when the connect operation
- completes. Ownership of the handler may be transferred. The function
- signature of the handler must be:
- @code
- void handler(
- // Result of operation. if the sequence is empty, set to
- // net::error::not_found. Otherwise, contains the
- // error from the last connection attempt.
- error_code const& error,
-
- // On success, an iterator denoting the successfully
- // connected endpoint. Otherwise, the end iterator.
- Iterator iterator
- );
- @endcode
-
- Regardless of whether the asynchronous operation completes immediately or
- not, the handler will not be invoked from within this function. Invocation
- of the handler will be performed in a manner equivalent to using
- `net::io_context::post()`.
-
- @par Example
-
- The following connect condition function object can be used to output
- information about the individual connection attempts:
-
- @code
- struct my_connect_condition
- {
- bool operator()(
- boost::system::error_code const& ec,
- net::ip::tcp::endpoint const& next)
- {
- if (ec) std::cout << "Error: " << ec.message() << std::endl;
- std::cout << "Trying: " << next << std::endl;
- return true;
- }
- };
- @endcode
-
- It would be used with the @ref boost::beast::async_connect
- function as follows:
-
- @code
- std::vector endpoints = ...;
- timeout_socket s(ioc.get_executor());
-
- async_connect(s, endpoints.begin(), endpoints.end(),
- my_connect_condition{}, connect_handler);
-
- void connect_handler(
- boost::system::error_code const& ec,
- std::vector::iterator)
- {
- // ...
- }
- @endcode
-*/
-template<
- class Protocol, class Executor,
- class Iterator,
- class ConnectCondition,
- class IteratorConnectHandler
-#if ! BOOST_BEAST_DOXYGEN
- ,class = typename std::enable_if<
- ! net::is_endpoint_sequence<
- Iterator>::value>::type
-#endif
->
-BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
- void (boost::system::error_code, Iterator))
-async_connect(
- basic_timeout_socket& s,
- Iterator begin, Iterator end,
- ConnectCondition connect_condition,
- IteratorConnectHandler&& handler);
-/* @} */
-
-} // beast
-} // boost
-
-#include
-
-#endif
diff --git a/include/boost/beast/_experimental/core/timeout_work_guard.hpp b/include/boost/beast/_experimental/core/timeout_work_guard.hpp
deleted file mode 100644
index 9112f1e5..00000000
--- a/include/boost/beast/_experimental/core/timeout_work_guard.hpp
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#ifndef BOOST_BEAST_CORE_TIMEOUT_WORK_GUARD_HPP
-#define BOOST_BEAST_CORE_TIMEOUT_WORK_GUARD_HPP
-
-#include
-#include
-#include
-
-namespace boost {
-namespace beast {
-
-class timeout_work_guard
-{
- timeout_handle h_;
-
-public:
- timeout_work_guard(timeout_work_guard const&) = delete;
- timeout_work_guard& operator=(timeout_work_guard const&) = delete;
-
- ~timeout_work_guard()
- {
- reset();
- }
-
- timeout_work_guard(timeout_work_guard&& other)
- : h_(other.h_)
- {
- other.h_ = nullptr;
- }
-
- explicit
- timeout_work_guard(timeout_handle h)
- : h_(h)
- {
- h_.service().on_work_started(h_);
- }
-
- bool
- owns_work() const
- {
- return h_ != nullptr;
- }
-
- void
- reset()
- {
- if(h_)
- h_.service().on_work_stopped(h_);
- }
-
- bool
- try_complete()
- {
- BOOST_ASSERT(h_ != nullptr);
- auto result =
- h_.service().on_try_work_complete(h_);
- h_ = nullptr;
- return result;
- }
-};
-
-} // beast
-} // boost
-
-#endif
diff --git a/include/boost/beast/core.hpp b/include/boost/beast/core.hpp
index 642d6eda..a932b997 100644
--- a/include/boost/beast/core.hpp
+++ b/include/boost/beast/core.hpp
@@ -12,6 +12,7 @@
#include
+#include
#include
#include
#include
@@ -37,6 +38,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/include/boost/beast/core/basic_stream_socket.hpp b/include/boost/beast/core/basic_stream_socket.hpp
new file mode 100644
index 00000000..a8801fe4
--- /dev/null
+++ b/include/boost/beast/core/basic_stream_socket.hpp
@@ -0,0 +1,1131 @@
+//
+// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_CORE_BASIC_STREAM_SOCKET_HPP
+#define BOOST_BEAST_CORE_BASIC_STREAM_SOCKET_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace boost {
+namespace beast {
+
+/** A stream socket wrapper with integrated timeout and bandwidth management.
+
+ This wraps a normal stream socket to provide the following additional
+ features:
+
+ @li The class template is parameterized on a user-defined executor
+ used for asynchronous operations. This achieves partial support for
+ "Networking TS enhancement to enable custom I/O executors" [P1322R0].
+
+ @li Optional timeouts may be specified for logical operations which
+ perform asynchronous reads, writes, and connects.
+
+ @li Optional bytes-per-second rate limits may be set independently
+ on asynchronous reads and writes.
+
+ @par Usage
+
+ Objects of this type are designed to be used in places where a
+ regular networking TCP/IP socket is being used. In particular this
+ class template replaces `net::basic_stream_socket`. The constructors
+ used here are similar to those of networking sockets, subject to the
+ that either an executor or an execution context may be passed in
+ the first argument.
+
+ @par Using Executors
+
+ @par Using Timeouts
+
+ To use this stream declare an instance of the class. Then, before
+ each logical operation for which a timeout is desired, call
+ @ref expires_after with a duration, or call @ref expires_at with a
+ time point. Alternatively, call @ref expires_never to disable the
+ timeout for subsequent logical operations.
+
+ A logical operation is defined as one of the following:
+
+ @li A call to @ref beast::async_connect where the stream is passed
+ as the first argument.
+
+ @li One or more calls to either one or both of the stream's
+ @ref async_read_some and @ref async_write_some member functions.
+ This also includes indirect calls, for example when passing the
+ stream as the first argument to an initiating function such as
+ `net::async_read_until`.
+
+ Each logical operation can be considered as peforming just reads,
+ just writes, or both reads and writes. Calls to @ref beast::async_connect
+ count as both a read and a write, although no actual reads or writes
+ are performed. While logical operations can include both reading
+ and writing, the usual restriction on having at most one read and
+ one write outstanding simultaneously applies.
+
+ The implementation maintains two timers: one timer for reads, and
+ another timer for writes. When the expiration time is adjusted
+ (by calling @ref expires_after or @ref expires_at), the indiviudal
+ timer is only set if there is not currently an operation of that
+ type (read or write) outstanding. It is undefined behavior to set
+ an expiration when there is both a read and a write pending, since
+ there would be no available timer to apply the expiration to.
+
+ @par Example
+ This code sets a timeout, and uses a generic networking stream algorithm
+ to read data from a timed stream until a particular delimiter is found
+ or until the stream times out:
+ @code
+ template
+ void async_read_line (
+ basic_stream_socket& stream,
+ net::streambuf& buffer, ReadHandler&& handler)
+ {
+ stream.expires_after (std::chrono::seconds(30));
+
+ net::async_read_until (stream, buffer, "\r\n", std::forward(handler));
+ }
+ @endcode
+
+ When a timeout occurs the socket will be closed, canceling any
+ pending I/O operations. The completion handlers for these canceled
+ operations will be invoked with the error @ref beast::error::timeout.
+
+ @par Using Rate Limits
+
+ @tparam Protocol A type meeting the requirements of Protocol
+ representing the protocol the protocol to use for the basic stream socket.
+ A common choice is `net::ip::tcp`.
+
+ @tparam Executor A type meeting the requirements of Executor to
+ be used for submitting all completion handlers which do not already have an
+ associated executor.
+
+ @note A multi-stream object must not be moved or destroyed while there
+ are oustanding asynchronous operations associated with it. Objects of this
+ type meet the requirements of @b AsyncReadStream and @b AsyncWriteStream.
+
+ @par Thread Safety
+ Distinct objects: Safe.@n
+ Shared objects: Unsafe. The application must also ensure
+ that all asynchronous operations are performed within the same
+ implicit or explicit strand.
+
+ @see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html
+*/
+template
+class basic_stream_socket
+ : private detail::stream_socket_base
+{
+ using time_point = typename
+ std::chrono::steady_clock::time_point;
+
+ // the number of seconds in each time slice
+ // for applying bandwidth rate limiting.
+ enum : std::size_t
+ {
+ rate_seconds = 3
+ };
+
+ static constexpr time_point never()
+ {
+ return (time_point::max)();
+ }
+
+ static std::size_t constexpr no_limit =
+ (std::numeric_limits::max)();
+
+ struct impl_type
+ : std::enable_shared_from_this
+ {
+ Executor ex; // must come first
+ net::basic_stream_socket<
+ Protocol> socket;
+ net::steady_timer rate_timer; // rate-limit interval timer
+ net::steady_timer read_timer; // for read timeout
+ net::steady_timer write_timer; // for write/connect timeout
+
+ // VFALCO these could be 32-bit unsigneds
+ std::size_t read_limit = no_limit; // read budget
+ std::size_t read_remain = no_limit; // read balance
+ std::size_t write_limit = no_limit; // write budget
+ std::size_t write_remain = no_limit;// write balance
+
+ char waiting = 0; // number of waiters on rate timer
+ bool read_pending = false; // if read (or connect) is pending
+ bool read_closed = false; // if read timed out
+ bool write_pending = false; // if write (or connect) is pending
+ bool write_closed = false; // if write (or connect) timed out
+
+ template
+ explicit
+ impl_type(Executor const&, Args&&...);
+
+ impl_type(impl_type&&) = default;
+ impl_type& operator=(impl_type&&);
+
+ void reset(); // set timeouts to never
+ void close(); // cancel everything
+ void maybe_kick(); // kick the rate timer if needed
+ void on_timer(); // rate timer completion
+ };
+
+ // We use shared ownership for the state so it can
+ // outlive the destruction of the stream_socket object,
+ // in the case where there is no outstanding read or
+ // write but the implementation is still waiting on
+ // the rate timer.
+ std::shared_ptr impl_;
+
+ // Restricted until P1322R0 is incorporated into Boost.Asio.
+ static_assert(
+ std::is_convertible<
+ decltype(std::declval().context()),
+ net::io_context&>::value,
+ "Only net::io_context is currently supported for executor_type::context()");
+
+ template class read_op;
+ template class write_op;
+
+ template
+ friend class detail::stream_socket_connect_op;
+
+ template
+ friend class basic_stream_socket;
+
+public:
+ /// The type of the next layer.
+ using next_layer_type = net::basic_stream_socket;
+
+ /// The type of the lowest layer.
+ using lowest_layer_type = get_lowest_layer;
+
+ /// The type of the executor associated with the object.
+ using executor_type = Executor;
+
+ /// The protocol type.
+ using protocol_type = Protocol;
+
+ /// The endpoint type.
+ using endpoint_type = typename Protocol::endpoint;
+
+ /** Destructor
+
+ This function destroys the socket.
+
+ @note The behavior of destruction while asynchronous
+ operations are outstanding is undefined.
+ */
+ ~basic_stream_socket();
+
+ /** Construct a basic_stream_socket without opening it.
+
+ This constructor creates a stream socket without opening it. The
+ socket needs to be opened and then connected or accepted before
+ data can be sent or received on it.
+
+ @param ctx An object whose type meets the requirements of
+ ExecutionContext, which the stream socket will use
+ to dispatch handlers for any asynchronous operations performed
+ on the socket. Currently, the only supported execution context
+ which may be passed here is `net::io_context`.
+
+ @note This function does not participate in overload resolution unless:
+ @li `std::is_convertible::value` is `true`, and
+ @li `std::is_constructible::value` is `true`.
+
+ @see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html
+ */
+ template<
+ class ExecutionContext
+ #if ! BOOST_BEAST_DOXYGEN
+ , class = typename std::enable_if<
+ std::is_convertible<
+ ExecutionContext&,
+ net::execution_context&>::value &&
+ std::is_constructible<
+ executor_type,
+ typename ExecutionContext::executor_type>::value
+ >::type
+ #endif
+ >
+ explicit
+ basic_stream_socket(ExecutionContext& ctx);
+
+ /** Construct a basic_stream_socket without opening it.
+
+ This constructor creates a stream socket without opening it. The
+ socket needs to be opened and then connected or accepted before
+ data can be sent or received on it.
+
+ @param ex The executor which the stream socket will use to dispatch
+ handlers for any asynchronous operations performed on the socket.
+ Currently, only executors that return `net::io_context&` from
+ `ex.context()` are supported.
+
+ @see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html
+ */
+ explicit
+ basic_stream_socket(executor_type const& ex);
+
+ /** Construct and open a basic_stream_socket.
+
+ This constructor creates and opens a stream socket. The socket
+ needs to be connected or accepted before data can be sent or
+ received on it.
+
+ @param ctx An object whose type meets the requirements of
+ ExecutionContext, which the stream socket will use
+ to dispatch handlers for any asynchronous operations performed
+ on the socket. Currently, the only supported execution context
+ which may be passed here is `net::io_context`.
+
+ @param protocol An object specifying protocol parameters to be
+ used.
+
+ @note This function does not participate in overload resolution unless:
+ @li `std::is_convertible::value` is `true`, and
+ @li `std::is_constructible::value` is `true`.
+
+ @see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html
+ */
+ template<
+ class ExecutionContext
+ #if ! BOOST_BEAST_DOXYGEN
+ ,class = typename std::enable_if<
+ std::is_convertible<
+ ExecutionContext&,
+ net::execution_context&>::value &&
+ std::is_constructible<
+ executor_type,
+ typename ExecutionContext::executor_type>::value
+ >::type
+ #endif
+ >
+ basic_stream_socket(
+ ExecutionContext& ctx,
+ protocol_type const& protocol);
+
+ /** Construct and open a basic_stream_socket.
+
+ This constructor creates and opens a stream socket. The socket
+ needs to be connected or accepted before data can be sent or
+ received on it.
+
+ @param ex The executor which the stream socket will use to dispatch
+ handlers for any asynchronous operations performed on the socket.
+ Currently, only executors that return `net::io_context&` from
+ `ex.context()` are supported.
+
+ @param protocol An object specifying protocol parameters to be
+ used.
+
+ @note This function does not participate in overload resolution unless:
+ @li `std::is_convertible::value` is `true`, and
+ @li `std::is_constructible::value` is `true`.
+
+ @see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html
+ */
+ basic_stream_socket(
+ executor_type const& ex,
+ protocol_type const& protocol);
+
+ /** Construct a basic_stream_socket, opening and binding it to the given local endpoint.
+
+ This constructor creates a stream socket and automatically
+ opens it bound to the specified endpoint on the local machine.
+ The protocol used is the protocol associated with the given
+ endpoint.
+
+ @param ctx An object whose type meets the requirements of
+ ExecutionContext, which the stream socket will use
+ to dispatch handlers for any asynchronous operations performed
+ on the socket. Currently, the only supported execution context
+ which may be passed here is `net::io_context`.
+
+ @param endpoint An endpoint on the local machine to which the
+ stream socket will be bound.
+
+ @note This function does not participate in overload resolution unless:
+ @li `std::is_convertible::value` is `true`, and
+ @li `std::is_constructible::value` is `true`.
+
+ @see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html
+ */
+ template<
+ class ExecutionContext
+ #if ! BOOST_BEAST_DOXYGEN
+ , class = typename std::enable_if<
+ std::is_convertible<
+ ExecutionContext&,
+ net::execution_context&>::value &&
+ std::is_constructible<
+ executor_type,
+ typename ExecutionContext::executor_type>::value
+ >::type
+ #endif
+ >
+ basic_stream_socket(
+ ExecutionContext& ctx,
+ endpoint_type const& endpoint);
+
+ /** Construct a basic_stream_socket, opening and binding it to the given local endpoint.
+
+ This constructor creates a stream socket and automatically
+ opens it bound to the specified endpoint on the local machine.
+ The protocol used is the protocol associated with the given
+ endpoint.
+
+ @param ex The executor which the stream socket will use to dispatch
+ handlers for any asynchronous operations performed on the socket.
+ Currently, only executors that return `net::io_context&` from
+ `ex.context()` are supported.
+
+ @param endpoint An endpoint on the local machine to which the
+ stream socket will be bound.
+
+ @note This function does not participate in overload resolution unless:
+ @li `std::is_convertible::value` is `true`, and
+ @li `std::is_constructible::value` is `true`.
+
+ @see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html
+ */
+ basic_stream_socket(
+ executor_type const& ex,
+ endpoint_type const& endpoint);
+
+ /** Construct a basic_stream_socket, opening and binding it to the given local endpoint.
+
+ This constructor creates a stream socket object to from an existing
+ next layer object.
+
+ @param ctx An object whose type meets the requirements of
+ ExecutionContext, which the stream socket will use
+ to dispatch handlers for any asynchronous operations performed
+ on the socket. Currently, the only supported execution context
+ which may be passed here is `net::io_context`.
+
+ @param socket The socket object to construct with. Ownership of
+ this object is transferred by move.
+
+ @note This function does not participate in overload resolution unless:
+ @li `std::is_convertible::value` is `true`, and
+ @li `std::is_constructible::value` is `true`.
+
+ @see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html
+ */
+ template<
+ class ExecutionContext
+ #if ! BOOST_BEAST_DOXYGEN
+ ,class = typename std::enable_if<
+ std::is_convertible<
+ ExecutionContext&,
+ net::execution_context&>::value &&
+ std::is_constructible<
+ executor_type,
+ typename ExecutionContext::executor_type>::value
+ >::type
+ #endif
+ >
+ basic_stream_socket(
+ ExecutionContext& ctx,
+ next_layer_type&& socket);
+
+ /** Construct a basic_stream_socket, opening and binding it to the given local endpoint.
+
+ This constructor creates a stream socket object to from an existing
+ next layer object.
+
+ @param ex The executor which the stream socket will use to dispatch
+ handlers for any asynchronous operations performed on the socket.
+ Currently, only executors that return `net::io_context&` from
+ `ex.context()` are supported.
+
+ @param socket The socket object to construct with. Ownership of
+ this object is transferred by move.
+
+ @note This function does not participate in overload resolution unless:
+ @li `std::is_convertible::value` is `true`, and
+ @li `std::is_constructible::value` is `true`.
+
+ @see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html
+ */
+ basic_stream_socket(
+ executor_type const& ex,
+ next_layer_type&& socket);
+
+ /** Move-construct a basic_stream_socket from another
+
+ This constructor moves a stream socket from one object to another.
+
+ The behavior of moving a stream socket while asynchronous operations
+ are outstanding is undefined.
+
+ @param other The other basic_stream_socket object from which the
+ move will occur.
+
+ @note Following the move, the moved-from object is in the same state
+ as if constructed using the @c basic_stream_socket(ExecutionContext&)
+ constructor.
+ */
+ basic_stream_socket(basic_stream_socket&& other);
+
+ /** Move-assign a basic_stream_socket from another.
+
+ This assignment operator moves a stream socket from one object
+ to another.
+
+ The behavior of move assignment while asynchronous operations
+ are pending is undefined.
+
+ @param other The other basic_stream_socket object from which the
+ move will occur.
+
+ @note Following the move, the moved-from object is in the same state
+ as if constructed using the @c basic_stream_socket(ExecutionContext&)
+ constructor.
+ */
+ basic_stream_socket& operator=(basic_stream_socket&& other);
+
+ /** Move-construct a basic_stream_socket from a socket of another protocol and executor type.
+
+ This constructor moves a stream socket from one object to another.
+
+ The behavior of moving a stream socket while asynchronous operations
+ are outstanding is undefined.
+
+ @param other The other basic_stream_socket object from which the
+ move will occur.
+
+ @note This constructor does not participate in overload resolution unless:
+ @li `std::is_convertible::value` is `true`, and
+ @li `std::is_convertible::value` is `true`.
+ */
+ template<
+ class OtherProtocol,
+ class OtherExecutor
+ #if ! BOOST_BEAST_DOXYGEN
+ ,class = typename std::enable_if<
+ std::is_convertible<
+ OtherProtocol, protocol_type>::value &&
+ std::is_convertible<
+ OtherExecutor, executor_type>::value>::type
+ #endif
+ >
+ basic_stream_socket(
+ basic_stream_socket&& other);
+
+ /** Move-assign a basic_stream_socket from a socket of another protocol and executor type.
+
+ This assignment operator a stream socket from one object to another.
+
+ The behavior of moving a stream socket while asynchronous operations
+ are outstanding is undefined.
+
+ @param other The other basic_stream_socket object from which the
+ move will occur.
+
+ @note This constructor does not participate in overload resolution unless:
+ @li `std::is_convertible::value` is `true`, and
+ @li `std::is_convertible::value` is `true`.
+ */
+ template<
+ class OtherProtocol,
+ class OtherExecutor
+ #if ! BOOST_BEAST_DOXYGEN
+ ,class = typename std::enable_if<
+ std::is_convertible<
+ OtherProtocol, protocol_type>::value &&
+ std::is_convertible<
+ OtherExecutor, executor_type>::value>::type
+ #endif
+ >
+ basic_stream_socket& operator=(basic_stream_socket<
+ OtherProtocol, OtherExecutor>&& other);
+
+ //--------------------------------------------------------------------------
+
+ /** Get the executor associated with the object.
+
+ This function may be used to obtain the executor object that the
+ stream uses to dispatch handlers for asynchronous operations.
+
+ @return A copy of the executor that stream will use to dispatch handlers.
+ */
+ executor_type
+ get_executor() const noexcept
+ {
+ return impl_->ex;
+ }
+
+ /** Get a reference to the next layer
+
+ This function returns a reference to the next layer
+ in a stack of stream layers.
+
+ @return A reference to the next layer in the stack of
+ stream layers.
+ */
+ next_layer_type&
+ next_layer() noexcept
+ {
+ return impl_->socket;
+ }
+
+ /** Get a reference to the next layer
+
+ This function returns a reference to the next layer in a
+ stack of stream layers.
+
+ @return A reference to the next layer in the stack of
+ stream layers.
+ */
+ next_layer_type const&
+ next_layer() const noexcept
+ {
+ return impl_->socket;
+ }
+
+ /** Get a reference to the lowest layer
+
+ This function returns a reference to the lowest layer
+ in a stack of stream layers.
+
+ @return A reference to the lowest layer in the stack of
+ stream layers.
+ */
+ lowest_layer_type&
+ lowest_layer() noexcept
+ {
+ return impl_->socket.lowest_layer();
+ }
+
+ /** Get a reference to the lowest layer
+
+ This function returns a reference to the lowest layer
+ in a stack of stream layers.
+
+ @return A reference to the lowest layer in the stack of
+ stream layers. Ownership is not transferred to the caller.
+ */
+ lowest_layer_type const&
+ lowest_layer() const noexcept
+ {
+ return impl_->socket.lowest_layer();
+ }
+
+ /** Set the number of bytes allowed to be read per second.
+
+ The limit will take effect in the next measured time
+ interval (currently set to 3 seconds).
+
+ @param bytes_per_second The maximum number of bytes the
+ implementation should attempt to read per second. A value
+ of zero indicates no limit.
+ */
+ void
+ read_limit(std::size_t bytes_per_second);
+
+ /** Set the number of bytes allowed to be written per second.
+
+ The limit will take effect in the next measured time
+ interval (currently set to 3 seconds).
+
+ @param bytes_per_second The maximum number of bytes the
+ implementation should attempt to write per second. A value
+ of zero indicates no limit.
+ */
+ void
+ write_limit(std::size_t bytes_per_second);
+
+ /** Set the timeout for the next logical operation.
+
+ This sets either the read timer, the write timer, or
+ both timers to expire after the specified amount of time
+ has elapsed. If a timer expires when the corresponding
+ asynchronous operation is outstanding, the stream will be
+ closed and any outstanding operations will complete with the
+ error @ref beast::error::timeout. Otherwise, if the timer
+ expires while no operations are outstanding, and the expiraton
+ is not set again, the next operation will time out immediately.
+
+ The timer applies collectively to any asynchronous reads
+ or writes initiated after the expiration is set, until the
+ expiration is set again. A call to @ref beast::async_connect
+ counts as both a read and a write.
+
+ @param expiry_time The amount of time after which a logical
+ operation should be considered timed out.
+ */
+ void
+ expires_after(
+ std::chrono::nanoseconds expiry_time);
+
+ /** Set the timeout for the next logical operation.
+
+ This sets either the read timer, the write timer, or both
+ timers to expire at the specified time point. If a timer
+ expires when the corresponding asynchronous operation is
+ outstanding, the stream will be closed and any outstanding
+ operations will complete with the error @ref beast::error::timeout.
+ Otherwise, if the timer expires while no operations are outstanding,
+ and the expiraton is not set again, the next operation will time out
+ immediately.
+
+ The timer applies collectively to any asynchronous reads
+ or writes initiated after the expiration is set, until the
+ expiration is set again. A call to @ref beast::async_connect
+ counts as both a read and a write.
+
+ @param expiry_time The time point after which a logical
+ operation should be considered timed out.
+ */
+ void
+ expires_at(net::steady_timer::time_point expiry_time);
+
+ /// Disable the timeout for the next logical operation.
+ void
+ expires_never();
+
+ /** Close the timed stream.
+
+ This cancels all timers and pending I/O. The completion handlers
+ for any pending I/O will see an error code.
+ */
+ void
+ close()
+ {
+ impl_->close();
+ }
+
+ //--------------------------------------------------------------------------
+
+ /** Start an asynchronous read.
+
+ This function is used to asynchronously read data from the stream socket.
+ The function call always returns immediately.
+
+ @param buffers One or more buffers into which the data will be read.
+ Although the buffers object may be copied as necessary, ownership of the
+ underlying memory blocks is retained by the caller, which must guarantee
+ that they remain valid until the handler is called.
+
+ @param handler The handler to be called when the read operation completes.
+ Copies will be made of the handler as required. The function signature of
+ the handler must be:
+ @code
+ void handler(
+ error_code const & error, // Result of operation.
+ std::size_t bytes_transferred // Number of bytes read.
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes immediately or
+ not, the handler will not be invoked from within this function. Invocation
+ of the handler will be performed in a manner equivalent to using
+ `net::io_context::post()`.
+ */
+ template
+ BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
+ void(error_code, std::size_t))
+ async_read_some(
+ MutableBufferSequence const& buffers,
+ ReadHandler&& handler);
+
+ /** Start an asynchronous write.
+
+ This function is used to asynchronously write data to the stream socket.
+ The function call always returns immediately.
+
+ @param buffers One or more data buffers to be written to the socket.
+ Although the buffers object may be copied as necessary, ownership of the
+ underlying memory blocks is retained by the caller, which must guarantee
+ that they remain valid until the handler is called.
+
+ @param handler The handler to be called when the write operation completes.
+ Copies will be made of the handler as required. The function signature of
+ the handler must be:
+ @code
+ void handler(
+ error_code const & error, // Result of operation.
+ std::size_t bytes_transferred // Number of bytes written.
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes immediately or
+ not, the handler will not be invoked from within this function. Invocation
+ of the handler will be performed in a manner equivalent to using
+ `net::io_context::post()`.
+ */
+ template
+ BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
+ void(error_code, std::size_t))
+ async_write_some(
+ ConstBufferSequence const& buffers,
+ WriteHandler&& handler);
+};
+
+//------------------------------------------------------------------------------
+
+/**
+ @defgroup async_connect boost::beast::async_connect
+ @brief Asynchronously establishes a socket connection by trying each
+ endpoint in a sequence, and terminating if a timeout occurs.
+*/
+/* @{ */
+/** Asynchronously establishes a socket connection by trying each endpoint in a sequence, and terminating if a timeout occurs.
+
+ This function attempts to connect a socket to one of a sequence of
+ endpoints. It does this by repeated calls to the underlying socket's
+ @c async_connect member function, once for each endpoint in the sequence,
+ until a connection is successfully established or a timeout occurs.
+
+ @param s The @ref beast::basic_stream_socket to be connected. If the socket
+ is already open, it will be closed.
+
+ @param endpoints A sequence of endpoints.
+
+ @param handler The handler to be called when the connect operation
+ completes. Ownership of the handler may be transferred. The function
+ signature of the handler must be:
+ @code
+ void handler(
+ // Result of operation. if the sequence is empty, set to
+ // net::error::not_found. Otherwise, contains the
+ // error from the last connection attempt.
+ error_code const& error,
+
+ // On success, the successfully connected endpoint.
+ // Otherwise, a default-constructed endpoint.
+ typename Protocol::endpoint const& endpoint
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes immediately or
+ not, the handler will not be invoked from within this function. Invocation
+ of the handler will be performed in a manner equivalent to using
+ `net::io_context::post()`.
+
+ @par Example
+ @code
+ net::tcp::resolver r(ioc);
+ net::tcp::resolver::query q("host", "service");
+ timed_stream s(ioc.get_executor());
+
+ // ...
+ r.async_resolve(q, resolve_handler);
+
+ // ...
+
+ void resolve_handler(
+ error_code const& ec,
+ tcp::resolver::results_type results)
+ {
+ if (!ec)
+ {
+ async_connect(s, results, connect_handler);
+ }
+ }
+
+ // ...
+ void connect_handler(
+ error_code const& ec,
+ tcp::endpoint const& endpoint)
+ {
+ // ...
+ }
+ @endcode
+*/
+template<
+ class Protocol, class Executor,
+ class EndpointSequence,
+ class RangeConnectHandler
+#if ! BOOST_BEAST_DOXYGEN
+ ,class = typename std::enable_if<
+ net::is_endpoint_sequence<
+ EndpointSequence>::value>::type
+#endif
+>
+BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
+ void (error_code, typename Protocol::endpoint))
+async_connect(
+ basic_stream_socket& s,
+ EndpointSequence const& endpoints,
+ RangeConnectHandler&& handler);
+
+/** Asynchronously establishes a socket connection by trying each endpoint in a sequence, and terminating if a timeout occurs.
+
+ This function attempts to connect a socket to one of a sequence of
+ endpoints. It does this by repeated calls to the underlying socket's
+ @c async_connect member function, once for each endpoint in the sequence,
+ until a connection is successfully established or a timeout occurs.
+
+ @param s The @ref beast::basic_stream_socket to be connected. If the socket
+ is already open, it will be closed.
+
+ @param endpoints A sequence of endpoints.
+
+ @param connect_condition A function object that is called prior to each
+ connection attempt. The signature of the function object must be:
+ @code
+ bool connect_condition(
+ error_code const& ec,
+ typename Protocol::endpoint const& next);
+ @endcode
+ The @c ec parameter contains the result from the most recent connect
+ operation. Before the first connection attempt, @c ec is always set to
+ indicate success. The @c next parameter is the next endpoint to be tried.
+ The function object should return true if the next endpoint should be tried,
+ and false if it should be skipped.
+ @param handler The handler to be called when the connect operation
+ completes. Ownership of the handler may be transferred. The function
+ signature of the handler must be:
+ @code
+ void handler(
+ // Result of operation. if the sequence is empty, set to
+ // net::error::not_found. Otherwise, contains the
+ // error from the last connection attempt.
+ error_code const& error,
+
+ // On success, the successfully connected endpoint.
+ // Otherwise, a default-constructed endpoint.
+ typename Protocol::endpoint const& endpoint
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes immediately or
+ not, the handler will not be invoked from within this function. Invocation
+ of the handler will be performed in a manner equivalent to using
+ `net::io_context::post()`.
+
+ @par Example
+ The following connect condition function object can be used to output
+ information about the individual connection attempts:
+ @code
+ struct my_connect_condition
+ {
+ bool operator()(
+ error_code const& ec,
+ net::ip::tcp::endpoint const& next)
+ {
+ if (ec) std::cout << "Error: " << ec.message() << std::endl;
+ std::cout << "Trying: " << next << std::endl;
+ return true;
+ }
+ };
+ @endcode
+ It would be used with the @ref boost::beast::async_connect
+ function as follows:
+ @code
+ net::tcp::resolver r(ioc);
+ net::tcp::resolver::query q("host", "service");
+ timed_stream s(ioc.get_executor());
+
+ // ...
+ r.async_resolve(q, resolve_handler);
+
+ // ...
+
+ void resolve_handler(
+ error_code const& ec,
+ tcp::resolver::results_type results)
+ {
+ if (!ec)
+ {
+ async_connect(s, results, my_connect_condition{}, connect_handler);
+ }
+ }
+
+ // ...
+ void connect_handler(
+ error_code const& ec,
+ tcp::endpoint const& endpoint)
+ {
+ // ...
+ }
+ @endcode
+*/
+template<
+ class Protocol, class Executor,
+ class EndpointSequence,
+ class ConnectCondition,
+ class RangeConnectHandler
+#if ! BOOST_BEAST_DOXYGEN
+ ,class = typename std::enable_if<
+ net::is_endpoint_sequence<
+ EndpointSequence>::value>::type
+#endif
+>
+BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
+ void (error_code, typename Protocol::endpoint))
+async_connect(
+ basic_stream_socket& s,
+ EndpointSequence const& endpoints,
+ ConnectCondition connect_condition,
+ RangeConnectHandler&& handler);
+
+/** Asynchronously establishes a socket connection by trying each endpoint in a sequence, and terminating if a timeout occurs.
+
+ This function attempts to connect a socket to one of a sequence of
+ endpoints. It does this by repeated calls to the underlying socket's
+ @c async_connect member function, once for each endpoint in the sequence,
+ until a connection is successfully established or a timeout occurs.
+
+ @param s The @ref beast::basic_stream_socket to be connected. If the socket
+ is already open, it will be closed.
+
+ @param begin An iterator pointing to the start of a sequence of endpoints.
+
+ @param end An iterator pointing to the end of a sequence of endpoints.
+
+ @param handler The handler to be called when the connect operation
+ completes. Ownership of the handler may be transferred. The function
+ signature of the handler must be:
+ @code
+ void handler(
+ // Result of operation. if the sequence is empty, set to
+ // net::error::not_found. Otherwise, contains the
+ // error from the last connection attempt.
+ error_code const& error,
+
+ // On success, an iterator denoting the successfully
+ // connected endpoint. Otherwise, the end iterator.
+ Iterator iterator
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes immediately or
+ not, the handler will not be invoked from within this function. Invocation
+ of the handler will be performed in a manner equivalent to using
+ `net::io_context::post()`.
+
+ @par Example
+ @code
+ std::vector endpoints = ...;
+ timed_stream s(ioc.get_executor());
+
+ async_connect(s,
+ endpoints.begin(), endpoints.end(),
+ connect_handler);
+ void connect_handler(
+ error_code const& ec,
+ std::vector::iterator)
+ {
+ // ...
+ }
+ @endcode
+*/
+template<
+ class Protocol, class Executor,
+ class Iterator,
+ class IteratorConnectHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
+ void (error_code, Iterator))
+async_connect(
+ basic_stream_socket& s,
+ Iterator begin, Iterator end,
+ IteratorConnectHandler&& handler);
+
+/** Asynchronously establishes a socket connection by trying each endpoint in a sequence, and terminating if a timeout occurs.
+
+ This function attempts to connect a socket to one of a sequence of
+ endpoints. It does this by repeated calls to the underlying socket's
+ @c async_connect member function, once for each endpoint in the sequence,
+ until a connection is successfully established or a timeout occurs.
+
+ @param s The @ref beast::basic_stream_socket to be connected. If the socket
+ is already open, it will be closed.
+
+ @param begin An iterator pointing to the start of a sequence of endpoints.
+
+ @param end An iterator pointing to the end of a sequence of endpoints.
+
+ @param connect_condition A function object that is called prior to each
+ connection attempt. The signature of the function object must be:
+ @code
+ bool connect_condition(
+ error_code const& ec,
+ Iterator next);
+ @endcode
+ @param handler The handler to be called when the connect operation
+ completes. Ownership of the handler may be transferred. The function
+ signature of the handler must be:
+ @code
+ void handler(
+ // Result of operation. if the sequence is empty, set to
+ // net::error::not_found. Otherwise, contains the
+ // error from the last connection attempt.
+ error_code const& error,
+
+ // On success, an iterator denoting the successfully
+ // connected endpoint. Otherwise, the end iterator.
+ Iterator iterator
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes immediately or
+ not, the handler will not be invoked from within this function. Invocation
+ of the handler will be performed in a manner equivalent to using
+ `net::io_context::post()`.
+
+ @par Example
+ The following connect condition function object can be used to output
+ information about the individual connection attempts:
+ @code
+ struct my_connect_condition
+ {
+ bool operator()(
+ error_code const& ec,
+ net::ip::tcp::endpoint const& next)
+ {
+ if (ec) std::cout << "Error: " << ec.message() << std::endl;
+ std::cout << "Trying: " << next << std::endl;
+ return true;
+ }
+ };
+ @endcode
+ It would be used with the @ref boost::beast::async_connect
+ function as follows:
+ @code
+ std::vector endpoints = ...;
+ timed_stream s(ioc.get_executor());
+
+ async_connect(s, endpoints.begin(), endpoints.end(),
+ my_connect_condition{}, connect_handler);
+ void connect_handler(
+ error_code const& ec,
+ std::vector::iterator)
+ {
+ // ...
+ }
+ @endcode
+*/
+template<
+ class Protocol, class Executor,
+ class Iterator,
+ class ConnectCondition,
+ class IteratorConnectHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
+ void (error_code, Iterator))
+async_connect(
+ basic_stream_socket& s,
+ Iterator begin, Iterator end,
+ ConnectCondition connect_condition,
+ IteratorConnectHandler&& handler);
+/* @} */
+
+} // beast
+} // boost
+
+#include
+
+#endif
diff --git a/include/boost/beast/core/detail/operation_base.hpp b/include/boost/beast/core/detail/operation_base.hpp
new file mode 100644
index 00000000..d85169ca
--- /dev/null
+++ b/include/boost/beast/core/detail/operation_base.hpp
@@ -0,0 +1,241 @@
+//
+// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_CORE_DETAIL_OPERATION_BASE_HPP
+#define BOOST_BEAST_CORE_DETAIL_OPERATION_BASE_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+/** Base class which stores a handler and forwards handler associations.
+
+ This mix-in assists bind wrappers, intermediate handlers, composed
+ operations, and similar types which store a handler, by acting as
+ a base class which holds the handler. Any networking customizations
+ on the handler will be propagated to the derived class. Specifically:
+
+ @li Any allocator associated with the handler will be propagated to
+ this object. Otherwise, the allocator associated with this
+ object will be a default allocator which the caller may specify
+ through a template parameter and constructor parameter.
+
+ @li Any executor associated with the handler will be propagated to
+ this object. Otherwise, the executor associated with this
+ object will be a default executor which the caller may specify
+ through a template parameter and constructor parameter.
+
+ @li The legacy customization points
+ `asio_handler_invoke`,
+ `asio_handler_allocate`,
+ `asio_handler_deallocate`, and
+ `asio_handler_is_continuation`,
+ which use argument-dependent lookup, will be forwarded to the
+ legacy customization points associated with the handler.
+
+ @par Example
+
+ The following declaration produces a class which wraps a
+ handler and inherits all of the networking customizations
+ associated with that handler:
+
+ @code
+ template
+ struct wrapped_handler : operation_base<
+ Handler, net::associated_executor_t>
+ {
+ template
+ explicit wrapped_handler (Handler_&& handler)
+ : operation_base>(
+ std::forward(handler), net::get_associated_executor(handler))
+ {
+ }
+
+ template
+ void operator()(Args&&... args)
+ {
+ this->handler_(std::forward(args)...);
+ }
+ };
+ @endcode
+
+ @tparam Handler The type of the completion handler to store.
+ This type must meet the requirements of CompletionHandler.
+
+ @tparam Executor The executor type to use if the handler does not
+ have an associated executor.
+
+ @tparam Allocator The allocator type to use if the handler does not
+ have an associated allocator. If this parameter is omitted, then
+ `std::allocator` will be used.
+*/
+template<
+ class Handler,
+ class Executor,
+ class Allocator = std::allocator
+>
+class operation_base
+#if ! BOOST_BEAST_DOXYGEN
+ : private boost::empty_value<
+ net::associated_allocator_t<
+ Handler, Allocator>, 0>
+ , private boost::empty_value<
+ net::associated_executor_t<
+ Handler, Executor>, 1>
+#endif
+{
+public:
+ /** The type of allocator associated with this object.
+
+ If a class derived from @ref operation_base is a completion
+ handler, then the associated allocator of the derived class will
+ be this type.
+ */
+ using allocator_type =
+ net::associated_allocator_t<
+ Handler, Allocator>;
+
+ /** The type of executor associated with this object.
+
+ If a class derived from @ref operation_base is a completion
+ handler, then the associated executor of the derived class will
+ be this type.
+ */
+ using executor_type =
+ net::associated_executor_t<
+ Handler, Executor>;
+
+ /** Returns the allocator associated with this object.
+
+ If a class derived from @ref operation_base is a completion
+ handler, then the object returned from this function will be used
+ as the associated allocator of the derived class.
+ */
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::empty_value<
+ allocator_type, 0>::get();
+ }
+
+ /** Returns the allocator associated with this object.
+
+ If a class derived from @ref operation_base is a completion
+ handler, then the object returned from this function will be used
+ as the associated allocator of the derived class.
+ */
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::empty_value<
+ executor_type, 1>::get();
+ }
+
+protected:
+ Handler handler_;
+
+ template<
+ class DeducedHandler
+#if BOOST_BEAST_DOXYGEN
+ ,class = typename std::enable_if<
+ ! std::is_same::type,
+ operation_base
+ >::value>::type
+#endif
+ >
+ operation_base(
+ DeducedHandler&& handler,
+ executor_type ex = executor_type{},
+ allocator_type alloc = allocator_type{})
+ : boost::empty_value(
+ boost::empty_init_t{}, alloc)
+ , boost::empty_value(
+ boost::empty_init_t{}, ex)
+ , handler_(std::forward(handler))
+ {
+ }
+
+public:
+#if ! BOOST_BEAST_DOXYGEN
+ template<
+ class Handler_,
+ class Executor_,
+ class Allocator_,
+ class Function>
+ friend
+ void asio_handler_invoke(
+ Function&& f,
+ operation_base<
+ Handler_, Executor_, Allocator_>* p);
+
+ friend
+ void* asio_handler_allocate(
+ std::size_t size, operation_base* p)
+ {
+ using net::asio_handler_allocate;
+ return asio_handler_allocate(
+ size, std::addressof(p->handler_));
+ }
+
+ friend
+ void asio_handler_deallocate(
+ void* mem, std::size_t size,
+ operation_base* p)
+ {
+ using net::asio_handler_deallocate;
+ asio_handler_deallocate(mem, size,
+ std::addressof(p->handler_));
+ }
+
+ friend
+ bool asio_handler_is_continuation(
+ operation_base* p)
+ {
+ using net::asio_handler_is_continuation;
+ return asio_handler_is_continuation(
+ std::addressof(p->handler_));
+ }
+#endif
+};
+
+#if ! BOOST_BEAST_DOXYGEN
+
+template<
+ class Handler,
+ class Executor,
+ class Allocator,
+ class Function>
+void asio_handler_invoke(
+ Function&& f,
+ operation_base<
+ Handler, Executor, Allocator>* p)
+{
+ using net::asio_handler_invoke;
+ asio_handler_invoke(
+ f, std::addressof(p->handler_));
+}
+
+#endif
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/include/boost/beast/core/detail/stream_socket_base.hpp b/include/boost/beast/core/detail/stream_socket_base.hpp
new file mode 100644
index 00000000..5ee9df2f
--- /dev/null
+++ b/include/boost/beast/core/detail/stream_socket_base.hpp
@@ -0,0 +1,66 @@
+//
+// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_CORE_DETAIL_STREAM_SOCKET_BASE_HPP
+#define BOOST_BEAST_CORE_DETAIL_STREAM_SOCKET_BASE_HPP
+
+#include
+#include
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+template
+class stream_socket_connect_op;
+
+class stream_socket_base
+{
+protected:
+ class pending_guard
+ {
+ bool& b_;
+ bool clear_ = true;
+
+ public:
+ ~pending_guard()
+ {
+ if(clear_)
+ b_ = false;
+ }
+
+ explicit
+ pending_guard(bool& b)
+ : b_(b)
+ {
+ BOOST_ASSERT(! b_);
+ b_ = true;
+ }
+
+ pending_guard(pending_guard&& other) noexcept
+ : b_(other.b_)
+ , clear_(boost::exchange(other.clear_, false))
+ {
+ }
+
+ void
+ reset()
+ {
+ BOOST_ASSERT(clear_);
+ b_ = false;
+ clear_ = false;
+ }
+ };
+};
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/include/boost/beast/core/impl/basic_stream_socket.hpp b/include/boost/beast/core/impl/basic_stream_socket.hpp
new file mode 100644
index 00000000..2b95369a
--- /dev/null
+++ b/include/boost/beast/core/impl/basic_stream_socket.hpp
@@ -0,0 +1,1073 @@
+//
+// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_CORE_IMPL_BASIC_STREAM_SOCKET_HPP
+#define BOOST_BEAST_CORE_IMPL_BASIC_STREAM_SOCKET_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace boost {
+namespace beast {
+
+/*
+ The algorithm for implementing the timeout depends
+ on the executor providing ordered execution guarantee.
+ `net::strand` automatically provides this, and we assume
+ that an implicit strand (one thread calling io_context::run)
+ also provides this.
+*/
+
+template
+template
+class basic_stream_socket::read_op
+ : public detail::operation_base<
+ Handler, Executor>
+ , public boost::asio::coroutine
+{
+ basic_stream_socket& s_;
+ net::executor_work_guard wg0_;
+ net::executor_work_guard wg1_;
+ pending_guard pg_;
+ Buffers b_;
+
+ struct timeout_handler
+ {
+ std::shared_ptr impl;
+
+ void
+ operator()(error_code ec)
+ {
+ // timer canceled
+ if(ec == net::error::operation_aborted)
+ return;
+
+ BOOST_ASSERT(! ec);
+
+ if(! impl->read_closed)
+ {
+ // timeout
+ impl->close();
+ impl->read_closed = true;
+ }
+ else
+ {
+ // late completion
+ impl->read_closed = false;
+ }
+ }
+ };
+
+public:
+ template
+ read_op(
+ basic_stream_socket& s,
+ Buffers const& b,
+ Handler_&& h)
+ : detail::operation_base<
+ Handler, Executor>(
+ std::forward(h),
+ net::get_associated_executor(
+ h, s.get_executor()))
+ , s_(s)
+ , wg0_(s_.get_executor())
+ , wg1_(net::get_associated_executor(
+ this->handler_, s_.get_executor()))
+ , pg_(s_.impl_->read_pending)
+ , b_(b)
+ {
+ (*this)();
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0)
+ {
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ // must come first
+ // VFALCO TODO what about the handler's allocator?
+ s_.impl_->read_timer.async_wait(
+ net::bind_executor(
+ this->get_executor(),
+ timeout_handler{s_.impl_}));
+
+ s_.impl_->maybe_kick();
+
+ // check if the balance is zero
+ if(s_.impl_->read_remain == 0)
+ {
+ // wait until the next time slice
+ ++s_.impl_->waiting;
+ BOOST_ASIO_CORO_YIELD
+ s_.impl_->rate_timer.async_wait(std::move(*this));
+
+ if(ec)
+ {
+ // caused by impl::close on timeout
+ BOOST_ASSERT(
+ ec == net::error::operation_aborted);
+ goto upcall;
+ }
+
+ // must call this
+ s_.impl_->on_timer();
+ BOOST_ASSERT(s_.impl_->read_remain > 0);
+ }
+
+ // we always use buffers_prefix,
+ // to reduce template instantiations.
+ BOOST_ASSERT(s_.impl_->read_remain > 0);
+ BOOST_ASIO_CORO_YIELD
+ s_.impl_->socket.async_read_some(
+ beast::buffers_prefix(
+ s_.impl_->read_remain, b_),
+ std::move(*this));
+
+ if(s_.impl_->read_remain != no_limit)
+ {
+ // adjust balance
+ BOOST_ASSERT(
+ bytes_transferred <= s_.impl_->read_remain);
+ s_.impl_->read_remain -= bytes_transferred;
+ }
+
+ {
+ // try cancelling timer
+ auto const n =
+ s_.impl_->read_timer.cancel();
+
+ if(s_.impl_->read_closed)
+ {
+ // timeout handler already invoked
+ BOOST_ASSERT(n == 0);
+ ec = beast::error::timeout;
+ s_.impl_->read_closed = false;
+ }
+ else if(n == 0)
+ {
+ // timeout handler already queued
+ ec = beast::error::timeout;
+
+ s_.impl_->close();
+ s_.impl_->read_closed = true;
+ }
+ else
+ {
+ // timeout was canceled
+ BOOST_ASSERT(n == 1);
+ }
+ }
+
+ upcall:
+ pg_.reset();
+ this->handler_(ec, bytes_transferred);
+ }
+ }
+};
+
+//------------------------------------------------------------------------------
+
+template
+template
+class basic_stream_socket::write_op
+ : public detail::operation_base<
+ Handler, Executor>
+ , public boost::asio::coroutine
+{
+ basic_stream_socket& s_;
+ net::executor_work_guard