New stream_socket:

This I/O object wraps an ordinary socket and provides a built-in
timeout and optional bandwidth rate-limiting facility.

Added class template basic_stream_socket

* Meets the requirements of AsyncReadStream and AsyncWriteStream

* Partially supports P1322R0:
  "Networking TS enhancement to enable custom I/O executors"
  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html

  A strand or other io_context-compatible executor may be
  chosen to use for all asynchronous stream operations.

* Supports independent timeouts on logical operations:
  connect, read, write, or both read and write.

* Provides an option for a configurable rate limit
  limit on the maximum rates of reading or writing.

* The previous experimental implementation,
  `timeout_socket` and related types, is removed.

* stream_socket is an alias for basic_stream_socket which
  uses `net::ip::tcp` as its protocol.
This commit is contained in:
Vinnie Falco
2018-12-20 08:51:43 -08:00
parent 2d3912751b
commit 6caca92f0e
28 changed files with 3313 additions and 1969 deletions

View File

@ -4,6 +4,7 @@ Version 201
* Add bind_back_handler
* Tidy up default-constructed iterators
* Add core errors and conditions
* New basic_stream_socket
--------------------------------------------------------------------------------

View File

@ -54,7 +54,7 @@
[*Experimental]
* Add [link beast.ref.boost__beast__timeout_socket `timeout_socket`]
* Add `timeout_socket`

View File

@ -179,6 +179,7 @@
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__basic_flat_buffer">basic_flat_buffer</link></member>
<member><link linkend="beast.ref.boost__beast__basic_multi_buffer">basic_multi_buffer</link></member>
<member><link linkend="beast.ref.boost__beast__basic_stream_socket">basic_stream_socket</link></member>
<member><link linkend="beast.ref.boost__beast__buffered_read_stream">buffered_read_stream</link></member>
<member><link linkend="beast.ref.boost__beast__buffers_adaptor">buffers_adaptor</link></member>
<member><link linkend="beast.ref.boost__beast__buffers_cat_view">buffers_cat_view</link></member>
@ -189,14 +190,14 @@
<member><link linkend="beast.ref.boost__beast__file_posix">file_posix</link></member>
<member><link linkend="beast.ref.boost__beast__file_stdio">file_stdio</link></member>
<member><link linkend="beast.ref.boost__beast__file_win32">file_win32</link></member>
<member><link linkend="beast.ref.boost__beast__flat_buffer">flat_buffer</link></member>
<member><link linkend="beast.ref.boost__beast__flat_static_buffer">flat_static_buffer</link></member>
<member><link linkend="beast.ref.boost__beast__flat_static_buffer_base">flat_static_buffer_base</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">&nbsp;</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__flat_buffer">flat_buffer</link></member>
<member><link linkend="beast.ref.boost__beast__flat_static_buffer">flat_static_buffer</link></member>
<member><link linkend="beast.ref.boost__beast__flat_static_buffer_base">flat_static_buffer_base</link></member>
<member><link linkend="beast.ref.boost__beast__handler_ptr">handler_ptr</link></member>
<member><link linkend="beast.ref.boost__beast__iequal">iequal</link></member>
<member><link linkend="beast.ref.boost__beast__iless">iless</link></member>
@ -205,17 +206,24 @@
<member><link linkend="beast.ref.boost__beast__static_buffer">static_buffer</link></member>
<member><link linkend="beast.ref.boost__beast__static_buffer_base">static_buffer_base</link></member>
<member><link linkend="beast.ref.boost__beast__static_string">static_string</link></member>
<member><link linkend="beast.ref.boost__beast__stream_socket">stream_socket</link></member>
<member><link linkend="beast.ref.boost__beast__string_param">string_param</link></member>
<member><link linkend="beast.ref.boost__beast__string_view">string_view</link></member>
</simplelist>
<bridgehead renderas="sect3">Constants</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__condition">condition</link></member>
<member><link linkend="beast.ref.boost__beast__error">error</link></member>
<member><link linkend="beast.ref.boost__beast__file_mode">file_mode</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__async_connect">async_connect</link></member>
<member><link linkend="beast.ref.boost__beast__bind_back_handler">bind_back_handler</link></member>
<member><link linkend="beast.ref.boost__beast__bind_front_handler">bind_front_handler</link></member>
<member><link linkend="beast.ref.boost__beast__bind_handler">bind_handler</link></member>
<member><link linkend="beast.ref.boost__beast__make_printable">make_printable</link></member>
<member><link linkend="beast.ref.boost__beast__buffers_cat">buffers_cat</link></member>
<member><link linkend="beast.ref.boost__beast__buffers_front">buffers_front</link></member>
<member><link linkend="beast.ref.boost__beast__buffers_prefix">buffers_prefix</link></member>
@ -224,17 +232,12 @@
<member><link linkend="beast.ref.boost__beast__buffers_to_string">buffers_to_string</link></member>
<member><link linkend="beast.ref.boost__beast__generic_category">generic_category</link></member>
<member><link linkend="beast.ref.boost__beast__iequals">iequals</link></member>
<member><link linkend="beast.ref.boost__beast__make_printable">make_printable</link></member>
<member><link linkend="beast.ref.boost__beast__ostream">ostream</link></member>
<member><link linkend="beast.ref.boost__beast__read_size">read_size</link></member>
<member><link linkend="beast.ref.boost__beast__read_size_or_throw">read_size_or_throw</link></member>
<member><link linkend="beast.ref.boost__beast__to_static_string">to_static_string</link></member>
</simplelist>
<bridgehead renderas="sect3">Constants</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__condition">condition</link></member>
<member><link linkend="beast.ref.boost__beast__error">error</link></member>
<member><link linkend="beast.ref.boost__beast__file_mode">file_mode</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Type Traits</bridgehead>
@ -312,22 +315,16 @@
<entry valign="top">
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__basic_timeout_socket">basic_timeout_socket</link></member>
<member><link linkend="beast.ref.boost__beast__flat_stream">flat_stream</link></member>
<member><link linkend="beast.ref.boost__beast__ssl_stream">ssl_stream</link></member>
<member><link linkend="beast.ref.boost__beast__http__icy_stream">http::icy_stream</link></member>
<member><link linkend="beast.ref.boost__beast__test__fail_count">test::fail_count</link></member>
<member><link linkend="beast.ref.boost__beast__test__stream">test::stream</link></member>
<member><link linkend="beast.ref.boost__beast__timeout_handle">timeout_handle</link></member>
<member><link linkend="beast.ref.boost__beast__timeout_socket">timeout_socket</link></member>
<member><link linkend="beast.ref.boost__beast__timeout_work_guard">timeout_work_guard</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__async_connect">async_connect</link></member>
<member><link linkend="beast.ref.boost__beast__set_timeout_service_options">set_timeout_service_options</link></member>
<member><link linkend="beast.ref.boost__beast__test__connect">test::connect</link></member>
</simplelist>
</entry>

View File

@ -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<std::mutex> 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<class Executor, class CancelHandler>
void
timeout_service::
set_callback(
timeout_handle h,
Executor const& ex,
CancelHandler&& handler)
{
thunks_[h.id_].callback.emplace(
callback<
Executor,
typename std::decay<CancelHandler>::type>{
h, ex,
std::forward<CancelHandler>(handler)});
}
void
timeout_service::
on_work_started(timeout_handle h)
{
BOOST_ASSERT(h.id_ != 0);
if( [this, h]
{
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<thunk*> expired;
{
std::lock_guard<std::mutex> 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<std::mutex> 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

View File

@ -59,11 +59,10 @@ public:
std::forward<Handler>(h)));
}
template<class Handler,
class T0, class... TN>
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(

View File

@ -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 <boost/beast/_experimental/core/detail/service_base.hpp>
#include <boost/beast/_experimental/core/detail/timeout_service_base.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/io_context.hpp> // #include <boost/asio/execution_context.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/steady_timer.hpp>
#include <chrono>
#include <cstdlib>
#include <deque>
#include <mutex>
#include <utility>
#include <vector>
namespace boost {
namespace beast {
class timeout_handle;
namespace detail {
class timeout_service
: public service_base<timeout_service>
{
template<class Executor, class Handler>
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<class Executor, class CancelHandler>
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<thunk> thunks_;
std::size_t free_thunk_ = 0;
net::steady_timer timer_;
std::chrono::seconds interval_{30ul};
long pending_ = 0;
};
} // detail
} // beast
} // boost
#include <boost/beast/_experimental/core/detail/impl/timeout_service.hpp>
#endif

View File

@ -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 <boost/beast/_experimental/core/detail/saved_handler.hpp>
#include <vector>
namespace boost {
namespace beast {
namespace detail {
struct thunk
{
using list_type =
std::vector<thunk*>;
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

View File

@ -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 <boost/beast/_experimental/core/detail/timeout_service.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/assert.hpp>
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<class Executor, class CancelHandler>
void
timeout_handle::
set_callback(
Executor const& ex, CancelHandler&& handler)
{
svc_->set_callback(*this, ex,
std::forward<CancelHandler>(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

View File

@ -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 <boost/beast/core/detail/stream_algorithm.hpp>
#include <boost/beast/_experimental/core/timeout_work_guard.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <memory>
#include <utility>
namespace boost {
namespace beast {
template<class Protocol, class Executor>
template<class Handler>
class basic_timeout_socket<Protocol, Executor>::async_op
{
public:
async_op(async_op&&) = default;
async_op(async_op const&) = delete;
template<class Buffers, class DeducedHandler>
async_op(
Buffers const& b,
DeducedHandler&& h,
basic_timeout_socket& s,
std::true_type)
: h_(std::forward<DeducedHandler>(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<class Buffers, class DeducedHandler>
async_op(
Buffers const& b,
DeducedHandler&& h,
basic_timeout_socket& s,
std::false_type)
: h_(std::forward<DeducedHandler>(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<Handler>;
using executor_type =
net::associated_executor_t<Handler,
decltype(std::declval<basic_timeout_socket<
Protocol, Executor>&>().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<class Function>
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<Executor> wg0_;
net::executor_work_guard<executor_type> wg1_;
detail::saved_handler& saved_;
};
//------------------------------------------------------------------------------
template<class Protocol, class Executor>
template<class ExecutionContext, class>
basic_timeout_socket<Protocol, Executor>::
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<class Protocol, class Executor>
basic_timeout_socket<Protocol, Executor>::
~basic_timeout_socket()
{
rd_timer_.destroy();
wr_timer_.destroy();
}
template<class Protocol, class Executor>
template<class MutableBufferSequence, class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
void(error_code, std::size_t))
basic_timeout_socket<Protocol, Executor>::
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
static_assert(net::is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
BOOST_BEAST_HANDLER_INIT(
ReadHandler, void(error_code, std::size_t));
async_op<BOOST_ASIO_HANDLER_TYPE(ReadHandler,
void(error_code, std::size_t))>(buffers,
std::forward<ReadHandler>(handler), *this,
std::true_type{});
return init.result.get();
}
template<class Protocol, class Executor>
template<class ConstBufferSequence, class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
void(error_code, std::size_t))
basic_timeout_socket<Protocol, Executor>::
async_write_some(
ConstBufferSequence const& buffers,
WriteHandler&& handler)
{
static_assert(net::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
BOOST_BEAST_HANDLER_INIT(
WriteHandler, void(error_code, std::size_t));
async_op<BOOST_ASIO_HANDLER_TYPE(WriteHandler,
void(error_code, std::size_t))>(buffers,
std::forward<WriteHandler>(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<Protocol, Executor>& s,
Endpoints const& eps,
Condition cond,
DeducedHandler&& h)
: h_(std::forward<DeducedHandler>(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<class Arg>
void
operator()(error_code ec, Arg&& arg)
{
if(! work_.try_complete())
{
s_.cn_op_.emplace(
std::move(*this),
ec,
std::forward<Arg>(arg));
return;
}
h_(ec, std::forward<Arg>(arg));
}
//
using allocator_type =
net::associated_allocator_t<Handler>;
using executor_type =
net::associated_executor_t<Handler,
decltype(std::declval<basic_timeout_socket<
Protocol, Executor>&>().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<class Function>
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<Protocol, Executor>& s_;
net::executor_work_guard<Executor> wg0_;
net::executor_work_guard<executor_type> wg1_;
};
struct any_endpoint
{
template<class Error, class Endpoint>
bool
operator()(
Error const&, Endpoint const&) const noexcept
{
return true;
}
};
template<class Iterator>
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<class Iterator>
endpoint_range_type<Iterator>
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<Protocol, Executor>& s,
EndpointSequence const& endpoints,
RangeConnectHandler&& handler)
{
BOOST_BEAST_HANDLER_INIT(RangeConnectHandler,
void(error_code, typename Protocol::endpoint));
detail::connect_op<Protocol, Executor,
BOOST_ASIO_HANDLER_TYPE(RangeConnectHandler,
void(error_code,
typename Protocol::endpoint))>(
s, endpoints, detail::any_endpoint{},
std::forward<RangeConnectHandler>(handler));
return init.result.get();
}
template<
class Protocol, class Executor,
class EndpointSequence,
class ConnectCondition,
class RangeConnectHandler, class>
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,
void (error_code, typename Protocol::endpoint))
async_connect(
basic_timeout_socket<Protocol, Executor>& s,
EndpointSequence const& endpoints,
ConnectCondition connect_condition,
RangeConnectHandler&& handler)
{
BOOST_BEAST_HANDLER_INIT(RangeConnectHandler,
void(error_code, typename Protocol::endpoint));
detail::connect_op<Protocol, Executor,
BOOST_ASIO_HANDLER_TYPE(RangeConnectHandler,
void(error_code,
typename Protocol::endpoint))>(
s, endpoints, connect_condition,
std::forward<RangeConnectHandler>(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<Protocol, Executor>& s,
Iterator begin, Iterator end,
IteratorConnectHandler&& handler)
{
BOOST_BEAST_HANDLER_INIT(IteratorConnectHandler,
void(error_code, Iterator));
detail::connect_op<Protocol, Executor,
BOOST_ASIO_HANDLER_TYPE(IteratorConnectHandler,
void(error_code, Iterator))>(
s, detail::endpoint_range(begin, end), detail::any_endpoint{},
std::forward<IteratorConnectHandler>(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<Protocol, Executor>& s,
Iterator begin, Iterator end,
ConnectCondition connect_condition,
IteratorConnectHandler&& handler)
{
BOOST_BEAST_HANDLER_INIT(IteratorConnectHandler,
void(error_code, Iterator));
detail::connect_op<Protocol, Executor,
BOOST_ASIO_HANDLER_TYPE(IteratorConnectHandler,
void(error_code, Iterator))>(
s, detail::endpoint_range(begin, end), connect_condition,
std::forward<IteratorConnectHandler>(handler));
return init.result.get();
}
} // beast
} // boost
#endif

View File

@ -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 <boost/beast/core/detail/config.hpp>
#include <boost/asio/io_context.hpp> // #include <boost/asio/execution_context.hpp>
#include <cstddef>
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<class Executor, class CancelHandler>
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 <boost/beast/_experimental/core/impl/timeout_service.hpp>
#endif

View File

@ -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 <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/_experimental/core/timeout_service.hpp>
#include <boost/beast/_experimental/core/detail/saved_handler.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/basic_stream_socket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/executor.hpp>
#include <chrono>
namespace boost {
namespace asio {
namespace ip {
class tcp;
} // ip
} // asio
} // boost
namespace boost {
namespace beast {
namespace detail {
template<class, class, class>
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> class async_op;
template<class, class, class>
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<Protocol> sock_;
public:
/// The type of the next layer.
using next_layer_type = net::basic_stream_socket<Protocol>;
/// The type of the lowest layer.
using lowest_layer_type = get_lowest_layer<next_layer_type>;
/// 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<class ExecutionContext
#if ! BOOST_BEAST_DOXYGEN
, class = typename std::enable_if<
std::is_convertible<
ExecutionContext&,
net::execution_context&>::value &&
std::is_constructible<
executor_type,
typename ExecutionContext::executor_type>::value
>::type
#endif
>
explicit
basic_timeout_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<class MutableBufferSequence, class ReadHandler>
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<class ConstBufferSequence, class WriteHandler>
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<Protocol, Executor>& 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<Protocol, Executor>& 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<tcp::endpoint> 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<tcp::endpoint>::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<Protocol, Executor>& 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<tcp::endpoint> 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<tcp::endpoint>::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<Protocol, Executor>& s,
Iterator begin, Iterator end,
ConnectCondition connect_condition,
IteratorConnectHandler&& handler);
/* @} */
} // beast
} // boost
#include <boost/beast/_experimental/core/impl/timeout_socket.hpp>
#endif

View File

@ -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 <boost/beast/core/detail/config.hpp>
#include <boost/beast/_experimental/core/timeout_service.hpp>
#include <boost/assert.hpp>
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

View File

@ -12,6 +12,7 @@
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/basic_stream_socket.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/beast/core/buffered_read_stream.hpp>
@ -37,6 +38,7 @@
#include <boost/beast/core/span.hpp>
#include <boost/beast/core/static_buffer.hpp>
#include <boost/beast/core/static_string.hpp>
#include <boost/beast/core/stream_socket.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/beast/core/string_param.hpp>
#include <boost/beast/core/type_traits.hpp>

File diff suppressed because it is too large Load Diff

View File

@ -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 <boost/beast/core/detail/config.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/asio/system_executor.hpp>
#include <boost/core/empty_value.hpp>
#include <utility>
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 <class Handler>
struct wrapped_handler : operation_base<
Handler, net::associated_executor_t<Handler>>
{
template <class Handler_>
explicit wrapped_handler (Handler_&& handler)
: operation_base<Handler, net::associated_executor_t <Handler>>(
std::forward<Handler_>(handler), net::get_associated_executor(handler))
{
}
template <class... Args>
void operator()(Args&&... args)
{
this->handler_(std::forward<Args>(args)...);
}
};
@endcode
@tparam Handler The type of the completion handler to store.
This type must meet the requirements of <em>CompletionHandler</em>.
@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<void>` will be used.
*/
template<
class Handler,
class Executor,
class Allocator = std::allocator<void>
>
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<typename
std::decay<Handler_>::type,
operation_base
>::value>::type
#endif
>
operation_base(
DeducedHandler&& handler,
executor_type ex = executor_type{},
allocator_type alloc = allocator_type{})
: boost::empty_value<allocator_type, 0>(
boost::empty_init_t{}, alloc)
, boost::empty_value<executor_type, 1>(
boost::empty_init_t{}, ex)
, handler_(std::forward<DeducedHandler>(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

View File

@ -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 <boost/assert.hpp>
#include <boost/core/exchange.hpp>
namespace boost {
namespace beast {
namespace detail {
template<class, class, class>
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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
//
// 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_STREAM_SOCKET_HPP
#define BOOST_BEAST_CORE_STREAM_SOCKET_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/basic_stream_socket.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/asio/ip/tcp.hpp>
namespace boost {
namespace beast {
/** A TCP/IP stream socket which supports timeouts and rate limits
*/
using stream_socket = basic_stream_socket<
net::ip::tcp,
net::io_context::executor_type>;
} // beast
} // boost
#endif

View File

@ -42,7 +42,7 @@ class saved_handler
};
template<class Handler>
class impl : public base
class impl final : public base
{
Handler h_;
net::executor_work_guard<

View File

@ -16,16 +16,18 @@ add_executable (tests-beast-core
${EXTRAS_FILES}
${TEST_MAIN}
Jamfile
buffer_test.hpp
file_test.hpp
_detail_base64.cpp
_detail_buffer.cpp
_detail_clamp.cpp
_detail_operation_base.cpp
_detail_read.cpp
_detail_sha1.cpp
_detail_tuple.cpp
_detail_variant.cpp
_detail_varint.cpp
buffer_test.hpp
file_test.hpp
basic_stream_socket.cpp
bind_handler.cpp
buffer_traits.cpp
buffered_read_stream.cpp
@ -51,6 +53,7 @@ add_executable (tests-beast-core
span.cpp
static_buffer.cpp
static_string.cpp
stream_socket.cpp
string.cpp
string_param.cpp
type_traits.cpp

View File

@ -11,11 +11,13 @@ local SOURCES =
_detail_base64.cpp
_detail_buffer.cpp
_detail_clamp.cpp
_detail_operation_base.cpp
_detail_read.cpp
_detail_sha1.cpp
_detail_tuple.cpp
_detail_variant.cpp
_detail_varint.cpp
basic_stream_socket.cpp
bind_handler.cpp
buffer_traits.cpp
buffered_read_stream.cpp
@ -41,6 +43,7 @@ local SOURCES =
span.cpp
static_buffer.cpp
static_string.cpp
stream_socket.cpp
string.cpp
string_param.cpp
type_traits.cpp

View File

@ -0,0 +1,500 @@
//
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
// Test that header file is self-contained.
#include <boost/beast/core/detail/operation_base.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/core/ignore_unused.hpp>
//------------------------------------------------------------------------------
namespace boost {
namespace beast {
namespace detail {
namespace {
struct specialized_handler
{
bool invoked = false;
struct executor
{
void* context() { return nullptr; }
void on_work_started() {}
void on_work_finished() {}
template<class F> void dispatch(F&&) {}
template<class F> void post(F&&) {}
template<class F> void defer(F&&) {}
};
};
template<class Function>
static
void
asio_handler_invoke(
Function&& f,
specialized_handler* p)
{
p->invoked = true;
f();
}
static
void*
asio_handler_allocate(
std::size_t,
specialized_handler* p)
{
p->invoked = true;
return nullptr;
}
static
void
asio_handler_deallocate(
void*, std::size_t,
specialized_handler* p)
{
p->invoked = true;
}
static bool
asio_handler_is_continuation(
specialized_handler* p)
{
p->invoked = true;
return false;
}
} // <anonymous>
} // detail
} // beast
} // boost
//------------------------------------------------------------------------------
namespace boost {
namespace asio {
template<class Allocator>
struct associated_allocator<
boost::beast::detail::specialized_handler, Allocator>
{
using type = std::allocator<int>;
static type get(
boost::beast::detail::specialized_handler const& h,
Allocator const& a = Allocator()) noexcept
{
return type{};
}
};
template<class Executor>
struct associated_executor<
boost::beast::detail::specialized_handler, Executor>
{
using type = typename
boost::beast::detail::specialized_handler::executor;
static type get(
boost::beast::detail::specialized_handler const& h,
Executor const& ex = Executor()) noexcept
{
return type{};
}
};
} // asio
} // boost
//------------------------------------------------------------------------------
namespace boost {
namespace beast {
namespace detail {
class operation_base_test : public beast::unit_test::suite
{
public:
using default_alloc = std::allocator<void>;
using default_exec = net::system_executor;
struct U {};
struct V {};
template<class E>
struct executor
{
void* context() { return nullptr; }
void on_work_started() {}
void on_work_finished() {}
template<class F> void dispatch(F&&) {}
template<class F> void post(F&&) {}
template<class F> void defer(F&&) {}
};
struct none
{
void operator()() const
{
}
};
struct with_alloc
{
using allocator_type = std::allocator<U>;
};
struct with_exec
{
using executor_type = executor<U>;
};
struct move_only
{
move_only() = default;
move_only(move_only&&) = default;
move_only(move_only const&) = delete;
void operator()() const{};
};
template<
class H,
class E = default_exec,
class A = default_alloc>
using tested_base =
operation_base<H, E, A>;
struct movable_handler : tested_base<move_only>
{
movable_handler()
: tested_base<move_only>(move_only{})
{
}
};
struct test_handler :
tested_base<specialized_handler>
{
test_handler()
: tested_base<
specialized_handler>(
specialized_handler{})
{
}
bool invoked() const noexcept
{
return this->handler_.invoked;
}
};
// no nested allocator type
BOOST_STATIC_ASSERT(
std::is_same<
default_alloc,
net::associated_allocator_t<
tested_base<none>
>>::value);
BOOST_STATIC_ASSERT(
std::is_same<
default_alloc,
decltype(net::get_associated_allocator(
std::declval<tested_base<none>>()
))>::value);
BOOST_STATIC_ASSERT(
std::is_same<
default_alloc,
decltype(net::get_associated_allocator(
std::declval<tested_base<none>>()
))>::value);
BOOST_STATIC_ASSERT(
std::is_same<
std::allocator<U>,
decltype(net::get_associated_allocator(std::declval<
tested_base<none, default_exec, std::allocator<U>>>()
))>::value);
// shouldn't work due to net.ts limitations
BOOST_STATIC_ASSERT(
! std::is_same<
std::allocator<U>,
decltype(net::get_associated_allocator(
std::declval<tested_base<none>>(),
std::declval<std::allocator<U>>()
))>::value);
BOOST_STATIC_ASSERT(
std::is_same<
default_alloc,
decltype(net::get_associated_allocator(
std::declval<tested_base<none>>(),
std::declval<std::allocator<U>>()
))>::value);
// nested allocator_type
BOOST_STATIC_ASSERT(
std::is_same<
std::allocator<U>,
net::associated_allocator_t<
tested_base<with_alloc>
>>::value);
BOOST_STATIC_ASSERT(
std::is_same<
std::allocator<U>,
decltype(net::get_associated_allocator(
std::declval<tested_base<with_alloc>>()
))>::value);
BOOST_STATIC_ASSERT(
std::is_same<
std::allocator<U>,
decltype(net::get_associated_allocator(
std::declval<tested_base<with_alloc>>(),
std::declval<std::allocator<V>>()
))>::value);
// specialization of associated_allocator
BOOST_STATIC_ASSERT(
std::is_same<
std::allocator<int>,
net::associated_allocator_t<
tested_base<
specialized_handler>
>>::value);
BOOST_STATIC_ASSERT(
std::is_same<
std::allocator<int>,
decltype(net::get_associated_allocator(
std::declval<tested_base<
specialized_handler>>()
))>::value);
BOOST_STATIC_ASSERT(
std::is_same<
std::allocator<int>,
decltype(net::get_associated_allocator(
std::declval<tested_base<
specialized_handler>>(),
std::declval<std::allocator<V>>()
))>::value);
// no nested executor type
BOOST_STATIC_ASSERT(
std::is_same<
default_exec,
net::associated_executor_t<
tested_base<none>
>>::value);
BOOST_STATIC_ASSERT(
std::is_same<
executor<U>,
net::associated_executor_t<
tested_base<none, executor<U>>
>>::value);
BOOST_STATIC_ASSERT(
std::is_same<
default_exec,
decltype(net::get_associated_executor(
std::declval<tested_base<none>>()
))>::value);
BOOST_STATIC_ASSERT(
std::is_same<
executor<U>,
decltype(net::get_associated_executor(std::declval<
tested_base<none, executor<U>>>()
))>::value);
// shouldn't work due to net.ts limitations
BOOST_STATIC_ASSERT(
! std::is_same<
executor<U>,
decltype(net::get_associated_executor(
std::declval<tested_base<none>>(),
std::declval<executor<U>>()
))>::value);
BOOST_STATIC_ASSERT(
std::is_same<
default_exec,
decltype(net::get_associated_executor(
std::declval<tested_base<none>>(),
std::declval<executor<U>>()
))>::value);
// nested executor_type
BOOST_STATIC_ASSERT(
std::is_same<
executor<U>,
net::associated_executor_t<
tested_base<with_exec>
>>::value);
BOOST_STATIC_ASSERT(
std::is_same<
executor<U>,
decltype(net::get_associated_executor(
std::declval<tested_base<with_exec>>()
))>::value);
BOOST_STATIC_ASSERT(
std::is_same<
executor<U>,
decltype(net::get_associated_executor(
std::declval<tested_base<with_exec>>(),
std::declval<executor<V>>()
))>::value);
// specialization of associated_executor
BOOST_STATIC_ASSERT(
std::is_same<
specialized_handler::executor,
net::associated_executor_t<
tested_base<
specialized_handler>
>>::value);
BOOST_STATIC_ASSERT(
std::is_same<
specialized_handler::executor,
decltype(net::get_associated_executor(std::declval<
tested_base<
specialized_handler>>()
))>::value);
BOOST_STATIC_ASSERT(
std::is_same<
specialized_handler::executor,
decltype(net::get_associated_executor(std::declval<
tested_base<
specialized_handler>>(),
std::declval<executor<V>>()
))>::value);
//--------------------------------------------------------------------------
template <class Handler>
struct wrapped_handler : operation_base<
Handler, net::associated_executor_t<Handler>>
{
template <class Handler_>
explicit wrapped_handler (Handler_&& handler)
: operation_base<Handler, net::associated_executor_t <Handler>>(
std::forward<Handler_>(handler), net::get_associated_executor(handler))
{
}
template <class... Args>
void operator()(Args&&... args)
{
this->handler_(std::forward<Args>(args)...);
}
};
void
testJavadocs()
{
wrapped_handler<none>{none{}}();
}
//--------------------------------------------------------------------------
void
testLegacyHooks()
{
// asio_handler_invoke
{
test_handler h;
BEAST_EXPECT(! h.invoked());
bool invoked = false;
using net::asio_handler_invoke;
asio_handler_invoke(
[&invoked]
{
invoked =true;
}, &h);
BEAST_EXPECT(invoked);
BEAST_EXPECT(h.invoked());
}
// asio_handler_allocate
{
test_handler h;
BEAST_EXPECT(! h.invoked());
using net::asio_handler_allocate;
asio_handler_allocate(0, &h);
BEAST_EXPECT(h.invoked());
}
// asio_handler_deallocate
{
test_handler h;
BEAST_EXPECT(! h.invoked());
using net::asio_handler_deallocate;
asio_handler_deallocate(nullptr, 0, &h);
BEAST_EXPECT(h.invoked());
}
// asio_handler_deallocate
{
test_handler h;
BEAST_EXPECT(! h.invoked());
using net::asio_handler_is_continuation;
asio_handler_is_continuation(&h);
BEAST_EXPECT(h.invoked());
}
}
void
testSpecialMembers()
{
{
test_handler h1;
test_handler h2(std::move(h1));
test_handler h3(h2);
boost::ignore_unused(h3);
}
{
movable_handler h1;
movable_handler h2(std::move(h1));
boost::ignore_unused(h2);
}
}
void
run() override
{
testJavadocs();
testLegacyHooks();
testSpecialMembers();
}
};
BEAST_DEFINE_TESTSUITE(beast,core,operation_base);
} // detail
} // beast
} // boost

View File

@ -0,0 +1,132 @@
//
// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
// Test that header file is self-contained.
#include <boost/beast/core/basic_stream_socket.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/beast/core/stream_socket.hpp>
#include <boost/asio/read_until.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ip/udp.hpp>
namespace boost {
namespace beast {
class basic_stream_socket_test
: public beast::unit_test::suite
{
public:
struct read_handler
{
template<class... Args>
void operator()(Args&&...)
{
}
};
template <class Protocol, class Executor, class ReadHandler>
void async_read_line (
basic_stream_socket<Protocol, Executor>& stream,
net::streambuf& buffer, ReadHandler&& handler)
{
stream.expires_after(std::chrono::seconds(30));
net::async_read_until(stream, buffer, "\r\n", std::forward<ReadHandler>(handler));
}
void
testJavadocs()
{
BEAST_EXPECT((&basic_stream_socket_test::async_read_line<
net::ip::tcp, net::io_context::executor_type, read_handler>));
}
struct other_t
{
};
void
testMembers()
{
using tcp = net::ip::tcp;
using ep_t = tcp::endpoint;
using ioc_t = net::io_context;
using ex_t = ioc_t::executor_type;
using stream_t = basic_stream_socket<tcp, ex_t>;
net::io_context ioc;
auto ex = ioc.get_executor();
// construction
{
stream_t{ioc};
stream_t{ex};
BOOST_STATIC_ASSERT(! std::is_constructible<
stream_t, other_t>::value);
}
{
stream_t{ioc, tcp::v4()};
stream_t{ex, tcp::v4()};
BOOST_STATIC_ASSERT(! std::is_constructible<
stream_t, other_t, tcp>::value);
}
{
stream_t{ioc, ep_t{}};
stream_t{ex, ep_t{}};
BOOST_STATIC_ASSERT(! std::is_constructible<
stream_t, other_t, ep_t>::value);
}
{
tcp::socket sock(ioc);
stream_t{ioc, std::move(sock)};
stream_t{ex, std::move(sock)};
BOOST_STATIC_ASSERT(! std::is_constructible<
stream_t, other_t, tcp::socket>::value);
}
// move
{
stream_t s1(ioc);
stream_t s2(std::move(s1));
}
{
stream_t s1(ioc);
stream_t s2(ioc);
s2 = std::move(s1);
}
// converting move
{
// We don't have any convertible protocol types
#if 0
basic_stream_socket<net::ip::udp, ex_t> s1(ioc);
stream_t s2(std::move(s1));
stream_t s3 = std::move(s1);
#endif
}
}
void
run()
{
testJavadocs();
testMembers();
}
};
BEAST_DEFINE_TESTSUITE(beast,core,basic_stream_socket);
} // beast
} // boost

View File

@ -8,19 +8,18 @@
//
// Test that header file is self-contained.
#include <boost/beast/_experimental/core/timeout_socket.hpp>
#include <boost/beast/core/stream_socket.hpp>
#include <boost/beast/_experimental/core/timeout_service.hpp>
#include <boost/beast/test/yield_to.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/beast/test/yield_to.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <array>
#include <thread>
namespace boost {
namespace beast {
class timeout_socket_test
class stream_socket_test
: public beast::unit_test::suite
, public test::enable_yield_to
{
@ -152,18 +151,117 @@ public:
server srv(ep, log);
{
net::io_context ioc;
set_timeout_service_options(
ioc, std::chrono::seconds(1));
timeout_socket s(ioc);
stream_socket s(ioc);
s.next_layer().connect(ep);
char buf[32];
error_code ec;
s.expires_after(std::chrono::seconds(1));
s.async_read_some(net::buffer(buf),
[&](error_code ec, std::size_t n)
[&ec](error_code ec_, std::size_t)
{
log << "read_some: " << ec.message() << "\n";
boost::ignore_unused(ec, n);
ec = ec_;
});
ioc.run();
BEAST_EXPECTS(
ec == error::timeout, ec.message());
}
}
void
testConnect()
{
using es_t =
std::array<net::ip::tcp::endpoint, 2>;
{
net::io_context ioc;
stream_socket s(ioc);
beast::async_connect(s, es_t{},
[](error_code, es_t::value_type)
{
});
}
{
net::io_context ioc;
stream_socket s(ioc);
beast::async_connect(s, es_t{},
[](error_code, es_t::value_type)
{
return true;
},
[](error_code, es_t::value_type)
{
});
}
{
es_t es;
net::io_context ioc;
stream_socket s(ioc);
beast::async_connect(s, es.begin(), es.end(),
[](error_code, es_t::iterator)
{
});
}
{
es_t es;
net::io_context ioc;
stream_socket s(ioc);
beast::async_connect(s, es.begin(), es.end(),
[](error_code, es_t::value_type)
{
return true;
},
[](error_code, es_t::iterator)
{
});
}
pass();
}
void callConnects()
{
using es_t =
std::array<net::ip::tcp::endpoint, 2>;
{
net::io_context ioc;
stream_socket s(ioc);
async_connect(s, es_t{},
[](error_code, es_t::value_type)
{
});
}
{
net::io_context ioc;
stream_socket s(ioc);
async_connect(s, es_t{},
[](error_code, es_t::value_type)
{
return true;
},
[](error_code, es_t::value_type)
{
});
}
{
es_t es;
net::io_context ioc;
stream_socket s(ioc);
async_connect(s, es.begin(), es.end(),
[](error_code, es_t::iterator)
{
});
}
{
es_t es;
net::io_context ioc;
stream_socket s(ioc);
async_connect(s, es.begin(), es.end(),
[](error_code, es_t::value_type)
{
return true;
},
[](error_code, es_t::iterator)
{
});
}
}
@ -171,12 +269,11 @@ public:
run()
{
testAsync();
pass();
testConnect();
}
};
BEAST_DEFINE_TESTSUITE(beast,core,timeout_socket);
BEAST_DEFINE_TESTSUITE(beast,core,stream_socket);
} // beast
} // boost

View File

@ -21,9 +21,6 @@ add_executable (tests-beast-experimental
icy_stream.cpp
ssl_stream.cpp
stream.cpp
timeout_socket.cpp
timeout_service.cpp
timeout_work_guard.cpp
)
set_property(TARGET tests-beast-experimental PROPERTY FOLDER "tests")

View File

@ -13,9 +13,6 @@ local SOURCES =
icy_stream.cpp
ssl_stream.cpp
stream.cpp
timeout_socket.cpp
timeout_service.cpp
timeout_work_guard.cpp
;
local RUN_TESTS ;

View File

@ -1,35 +0,0 @@
//
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
// Test that header file is self-contained.
#include <boost/beast/_experimental/core/timeout_service.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
namespace boost {
namespace beast {
class timeout_service_test
: public beast::unit_test::suite
{
public:
void
run() override
{
net::io_context ctx;
set_timeout_service_options(ctx,
std::chrono::seconds(1));
pass();
}
};
BEAST_DEFINE_TESTSUITE(beast,core,timeout_service);
} // beast
} // boost

View File

@ -1,11 +0,0 @@
//
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
// Test that header file is self-contained.
#include <boost/beast/_experimental/core/timeout_work_guard.hpp>