mirror of
https://github.com/boostorg/beast.git
synced 2025-08-03 06:44:39 +02:00
Add experimental timeout_socket:
This is a socket wrapper which has a built-in timeout feature on reads and writes.
This commit is contained in:
@@ -1,3 +1,9 @@
|
||||
Version 187:
|
||||
|
||||
* Add experimental timeout_socket
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Version 186:
|
||||
|
||||
* basic_fields uses intrusive base hooks
|
||||
|
@@ -27,12 +27,16 @@
|
||||
|
||||
* ([issue 1091]) Fix timer on websocket upgrade in examples
|
||||
|
||||
* ([issue 1270]) `basic_fields` uses intrusive base hooks
|
||||
* ([issue 1270]) [link beast.ref.boost__beast__http__basic_fields `basic_fields`] uses intrusive base hooks
|
||||
|
||||
* ([issue 1267]) Fix parsing of out-of-bounds hex values
|
||||
|
||||
* Workaround for http-server-fast and libstdc++
|
||||
|
||||
[*Experimental]
|
||||
|
||||
* Add [link beast.ref.boost__beast__timeout_socket `timeout_socket`]
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -302,16 +302,19 @@
|
||||
<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_socket">timeout_socket</link></member>
|
||||
</simplelist>
|
||||
</entry>
|
||||
<entry valign="top">
|
||||
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
<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>
|
||||
|
@@ -0,0 +1,181 @@
|
||||
//
|
||||
// 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_IPP
|
||||
#define BOOST_BEAST_CORE_DETAIL_IMPL_TIMEOUT_SERVICE_IPP
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline
|
||||
timeout_object::
|
||||
timeout_object(boost::asio::io_context& ioc)
|
||||
: svc_(boost::asio::use_service<timeout_service>(ioc))
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline
|
||||
timeout_service::
|
||||
timeout_service(boost::asio::io_context& ctx)
|
||||
: service_base(ctx)
|
||||
, strand_(ctx.get_executor())
|
||||
, timer_(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
timeout_service::
|
||||
on_work_started(timeout_object& obj)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
BOOST_VERIFY(++obj.outstanding_work_ == 1);
|
||||
insert(obj, *fresh_);
|
||||
if(++count_ == 1)
|
||||
do_async_wait();
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
timeout_service::
|
||||
on_work_complete(timeout_object& obj)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
remove(obj);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
timeout_service::
|
||||
on_work_stopped(timeout_object& obj)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
BOOST_ASSERT(count_ > 0);
|
||||
BOOST_VERIFY(--obj.outstanding_work_ == 0);
|
||||
if(obj.list_ != nullptr)
|
||||
remove(obj);
|
||||
if(--count_ == 0)
|
||||
timer_.cancel();
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
timeout_service::
|
||||
set_option(std::chrono::seconds n)
|
||||
{
|
||||
interval_ = n;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Precondition: caller holds the mutex
|
||||
inline
|
||||
void
|
||||
timeout_service::
|
||||
insert(timeout_object& obj, list_type& list)
|
||||
{
|
||||
BOOST_ASSERT(obj.list_ == nullptr);
|
||||
list.push_back(&obj); // can throw
|
||||
obj.list_ = &list;
|
||||
obj.pos_ = list.size();
|
||||
}
|
||||
|
||||
// Precondition: caller holds the mutex
|
||||
inline
|
||||
void
|
||||
timeout_service::
|
||||
remove(timeout_object& obj)
|
||||
{
|
||||
BOOST_ASSERT(obj.list_ != nullptr);
|
||||
BOOST_ASSERT(
|
||||
obj.list_ == stale_ ||
|
||||
obj.list_ == fresh_);
|
||||
BOOST_ASSERT(obj.list_->size() > 0);
|
||||
auto& list = *obj.list_;
|
||||
auto const n = list.size() - 1;
|
||||
if(obj.pos_ != n)
|
||||
{
|
||||
auto other = list[n];
|
||||
list[obj.pos_] = other;
|
||||
other->pos_ = obj.pos_;
|
||||
}
|
||||
obj.list_ = nullptr;
|
||||
list.resize(n);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
timeout_service::
|
||||
do_async_wait()
|
||||
{
|
||||
timer_.expires_after(interval_);
|
||||
timer_.async_wait(
|
||||
boost::asio::bind_executor(
|
||||
strand_,
|
||||
[this](error_code ec)
|
||||
{
|
||||
this->on_timer(ec);
|
||||
}));
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
timeout_service::
|
||||
on_timer(error_code ec)
|
||||
{
|
||||
if(ec == boost::asio::error::operation_aborted)
|
||||
{
|
||||
BOOST_ASSERT(fresh_->empty());
|
||||
BOOST_ASSERT(stale_->empty());
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
if(! stale_->empty())
|
||||
{
|
||||
for(auto obj : *stale_)
|
||||
{
|
||||
obj->list_ = nullptr;
|
||||
obj->on_timeout();
|
||||
}
|
||||
stale_->clear();
|
||||
}
|
||||
std::swap(fresh_, stale_);
|
||||
}
|
||||
|
||||
do_async_wait();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline
|
||||
void
|
||||
timeout_service::
|
||||
shutdown() noexcept
|
||||
{
|
||||
boost::asio::post(
|
||||
boost::asio::bind_executor(
|
||||
strand_,
|
||||
[this]()
|
||||
{
|
||||
timer_.cancel();
|
||||
}));
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// 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_SERVICE_BASE_HPP
|
||||
#define BOOST_BEAST_CORE_DETAIL_SERVICE_BASE_HPP
|
||||
|
||||
#include <boost/asio/execution_context.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class T>
|
||||
class service_id : public boost::asio::execution_context::id
|
||||
{
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class service_base
|
||||
: public boost::asio::execution_context::service
|
||||
{
|
||||
protected:
|
||||
boost::asio::execution_context& ctx_;
|
||||
|
||||
public:
|
||||
static service_id<T> id;
|
||||
|
||||
explicit
|
||||
service_base(boost::asio::execution_context& ctx)
|
||||
: boost::asio::execution_context::service(ctx)
|
||||
, ctx_(ctx)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
service_id<T>
|
||||
service_base<T>::id;
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
124
include/boost/beast/experimental/core/detail/timeout_service.hpp
Normal file
124
include/boost/beast/experimental/core/detail/timeout_service.hpp
Normal file
@@ -0,0 +1,124 @@
|
||||
//
|
||||
// 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/core/error.hpp>
|
||||
#include <boost/beast/experimental/core/detail/service_base.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <boost/asio/bind_executor.hpp>
|
||||
#include <boost/asio/executor.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class timeout_service;
|
||||
|
||||
class timeout_object
|
||||
{
|
||||
friend class timeout_service;
|
||||
|
||||
using list_type = std::vector<timeout_object*>;
|
||||
|
||||
timeout_service& svc_;
|
||||
std::size_t pos_;
|
||||
list_type* list_ = nullptr;
|
||||
char outstanding_work_ = 0;
|
||||
|
||||
public:
|
||||
timeout_object() = delete;
|
||||
timeout_object(timeout_object&&) = delete;
|
||||
timeout_object(timeout_object const&) = delete;
|
||||
timeout_object& operator=(timeout_object&&) = delete;
|
||||
timeout_object& operator=(timeout_object const&) = delete;
|
||||
|
||||
// VFALCO should be execution_context
|
||||
explicit
|
||||
timeout_object(boost::asio::io_context& ioc);
|
||||
|
||||
timeout_service&
|
||||
service() const
|
||||
{
|
||||
return svc_;
|
||||
}
|
||||
|
||||
virtual void on_timeout() = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class timeout_service
|
||||
: public service_base<timeout_service>
|
||||
{
|
||||
public:
|
||||
using key_type = timeout_service;
|
||||
|
||||
// VFALCO Should be execution_context
|
||||
explicit
|
||||
timeout_service(boost::asio::io_context& ctx);
|
||||
|
||||
void
|
||||
on_work_started(timeout_object& obj);
|
||||
|
||||
void
|
||||
on_work_complete(timeout_object& obj);
|
||||
|
||||
void
|
||||
on_work_stopped(timeout_object& obj);
|
||||
|
||||
void
|
||||
set_option(std::chrono::seconds n);
|
||||
|
||||
private:
|
||||
friend class timeout_object;
|
||||
|
||||
using list_type = std::vector<timeout_object*>;
|
||||
|
||||
void insert(timeout_object& obj, list_type& list);
|
||||
void remove(timeout_object& obj);
|
||||
void do_async_wait();
|
||||
void on_timer(error_code ec);
|
||||
|
||||
virtual void shutdown() noexcept override;
|
||||
|
||||
boost::asio::strand<
|
||||
boost::asio::io_context::executor_type> strand_;
|
||||
|
||||
std::mutex m_;
|
||||
list_type list_[2];
|
||||
list_type* fresh_ = &list_[0];
|
||||
list_type* stale_ = &list_[1];
|
||||
std::size_t count_ = 0;
|
||||
boost::asio::steady_timer timer_;
|
||||
std::chrono::seconds interval_{30ul};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/experimental/core/detail/impl/timeout_service.ipp>
|
||||
|
||||
#endif
|
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// 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_WORK_GUARD_HPP
|
||||
#define BOOST_BEAST_CORE_DETAIL_TIMEOUT_WORK_GUARD_HPP
|
||||
|
||||
#include <boost/beast/experimental/core/detail/timeout_service.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/exchange.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
class timeout_work_guard
|
||||
{
|
||||
timeout_object* obj_;
|
||||
|
||||
public:
|
||||
timeout_work_guard(timeout_work_guard const&) = delete;
|
||||
timeout_work_guard& operator=(timeout_work_guard&&) = delete;
|
||||
timeout_work_guard& operator=(timeout_work_guard const&) = delete;
|
||||
|
||||
~timeout_work_guard()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
timeout_work_guard(timeout_work_guard&& other)
|
||||
: obj_(boost::exchange(other.obj_, nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
explicit
|
||||
timeout_work_guard(timeout_object& obj)
|
||||
: obj_(&obj)
|
||||
{
|
||||
obj_->service().on_work_started(*obj_);
|
||||
}
|
||||
|
||||
bool
|
||||
owns_work() const
|
||||
{
|
||||
return obj_ != nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
reset()
|
||||
{
|
||||
if(obj_)
|
||||
obj_->service().on_work_stopped(*obj_);
|
||||
}
|
||||
|
||||
void
|
||||
complete()
|
||||
{
|
||||
BOOST_ASSERT(obj_ != nullptr);
|
||||
obj_->service().on_work_complete(*obj_);
|
||||
obj_ = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// 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>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
inline
|
||||
void
|
||||
set_timeout_service_options(
|
||||
boost::asio::io_context& ioc,
|
||||
std::chrono::seconds interval)
|
||||
{
|
||||
boost::asio::use_service<
|
||||
detail::timeout_service>(ioc).set_option(interval);
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
207
include/boost/beast/experimental/core/impl/timeout_socket.hpp
Normal file
207
include/boost/beast/experimental/core/impl/timeout_socket.hpp
Normal file
@@ -0,0 +1,207 @@
|
||||
//
|
||||
// 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_TIMOUT_SOCKET_HPP
|
||||
#define BOOST_BEAST_CORE_IMPL_TIMOUT_SOCKET_HPP
|
||||
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/experimental/core/detail/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
|
||||
{
|
||||
Handler h_;
|
||||
basic_timeout_socket& s_;
|
||||
detail::timeout_work_guard work_;
|
||||
|
||||
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_)
|
||||
{
|
||||
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_)
|
||||
{
|
||||
s_.sock_.async_write_some(b, std::move(*this));
|
||||
}
|
||||
|
||||
using allocator_type =
|
||||
boost::asio::associated_allocator_t<Handler>;
|
||||
|
||||
allocator_type
|
||||
get_allocator() const noexcept
|
||||
{
|
||||
return (boost::asio::get_associated_allocator)(h_);
|
||||
}
|
||||
|
||||
using executor_type =
|
||||
boost::asio::associated_executor_t<Handler, decltype(
|
||||
std::declval<basic_timeout_socket<Protocol>&>().get_executor())>;
|
||||
|
||||
executor_type
|
||||
get_executor() const noexcept
|
||||
{
|
||||
return (boost::asio::get_associated_executor)(
|
||||
h_, s_.get_executor());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(async_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, async_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(f, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
if(s_.expired_)
|
||||
{
|
||||
BOOST_ASSERT(ec == boost::asio::error::operation_aborted);
|
||||
ec = boost::asio::error::timed_out;
|
||||
}
|
||||
else
|
||||
{
|
||||
work_.complete();
|
||||
}
|
||||
h_(ec, bytes_transferred);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
basic_timeout_socket<Protocol, Executor>::
|
||||
timer::
|
||||
timer(basic_timeout_socket& s)
|
||||
: detail::timeout_object(s.ex_.context())
|
||||
, s_(s)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
auto
|
||||
basic_timeout_socket<Protocol, Executor>::
|
||||
timer::
|
||||
operator=(timer&&)
|
||||
-> timer&
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
void
|
||||
basic_timeout_socket<Protocol, Executor>::
|
||||
timer::
|
||||
on_timeout()
|
||||
{
|
||||
boost::asio::post(
|
||||
s_.ex_,
|
||||
[this]()
|
||||
{
|
||||
s_.expired_ = true;
|
||||
s_.sock_.cancel();
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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_(*this)
|
||||
, wr_timer_(*this)
|
||||
, sock_(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Protocol, class Executor>
|
||||
template<class MutableBufferSequence, class ReadHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||
void(boost::system::error_code, std::size_t))
|
||||
basic_timeout_socket<Protocol, Executor>::
|
||||
async_read_some(
|
||||
MutableBufferSequence const& buffers,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(boost::asio::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(boost::system::error_code, std::size_t))
|
||||
basic_timeout_socket<Protocol, Executor>::
|
||||
async_write_some(
|
||||
ConstBufferSequence const& buffers,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(boost::asio::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();
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
40
include/boost/beast/experimental/core/timeout_service.hpp
Normal file
40
include/boost/beast/experimental/core/timeout_service.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// 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/asio/execution_context.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <chrono>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
void
|
||||
set_timeout_service_options(
|
||||
boost::asio::io_context& ctx, // VFALCO should be execution_context
|
||||
std::chrono::seconds interval);
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/experimental/core/impl/timeout_service.ipp>
|
||||
|
||||
#endif
|
240
include/boost/beast/experimental/core/timeout_socket.hpp
Normal file
240
include/boost/beast/experimental/core/timeout_socket.hpp
Normal file
@@ -0,0 +1,240 @@
|
||||
//
|
||||
// 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/detail/timeout_service.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/basic_stream_socket.hpp>
|
||||
#include <boost/asio/executor.hpp>
|
||||
#include <chrono>
|
||||
|
||||
namespace boost {
|
||||
namespace asio {
|
||||
namespace ip {
|
||||
class tcp;
|
||||
} // ip
|
||||
} // asio
|
||||
} // boost
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** 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 = boost::asio::executor
|
||||
>
|
||||
class basic_timeout_socket
|
||||
{
|
||||
template<class> class async_op;
|
||||
|
||||
class timer : public detail::timeout_object
|
||||
{
|
||||
basic_timeout_socket& s_;
|
||||
|
||||
public:
|
||||
explicit timer(basic_timeout_socket& s);
|
||||
timer& operator=(timer&& other);
|
||||
void on_timeout() override;
|
||||
};
|
||||
|
||||
Executor ex_; // must come first
|
||||
timer rd_timer_;
|
||||
timer wr_timer_;
|
||||
boost::asio::basic_stream_socket<Protocol> sock_;
|
||||
bool expired_ = false;
|
||||
|
||||
public:
|
||||
/// The type of the next layer.
|
||||
using next_layer_type = boost::asio::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;
|
||||
|
||||
// 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&,
|
||||
boost::asio::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()
|
||||
{
|
||||
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
|
||||
{
|
||||
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()
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
boost::asio::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
|
||||
boost::asio::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<
|
||||
boost::asio::ip::tcp,
|
||||
boost::asio::io_context::executor_type>;
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/experimental/core/impl/timeout_socket.hpp>
|
||||
|
||||
#endif
|
@@ -22,6 +22,9 @@ 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")
|
||||
|
@@ -13,6 +13,9 @@ local SOURCES =
|
||||
icy_stream.cpp
|
||||
ssl_stream.cpp
|
||||
stream.cpp
|
||||
timeout_socket.cpp
|
||||
timeout_service.cpp
|
||||
timeout_work_guard.cpp
|
||||
;
|
||||
|
||||
local RUN_TESTS ;
|
||||
|
35
test/beast/experimental/timeout_service.cpp
Normal file
35
test/beast/experimental/timeout_service.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// 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/unit_test/suite.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
class timeout_service_test
|
||||
: public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
boost::asio::io_context ctx;
|
||||
set_timeout_service_options(ctx,
|
||||
std::chrono::seconds(1));
|
||||
pass();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(beast,core,timeout_service);
|
||||
|
||||
} // beast
|
||||
} // boost
|
181
test/beast/experimental/timeout_socket.cpp
Normal file
181
test/beast/experimental/timeout_socket.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
//
|
||||
// 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/experimental/core/timeout_socket.hpp>
|
||||
|
||||
#include <boost/beast/experimental/core/timeout_service.hpp>
|
||||
|
||||
#include <boost/beast/test/yield_to.hpp>
|
||||
#include <boost/beast/unit_test/suite.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <thread>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
class timeout_socket_test
|
||||
: public beast::unit_test::suite
|
||||
, public test::enable_yield_to
|
||||
{
|
||||
public:
|
||||
class server
|
||||
{
|
||||
std::ostream& log_;
|
||||
boost::asio::io_context ioc_;
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
boost::asio::ip::tcp::socket socket_;
|
||||
std::thread t_;
|
||||
|
||||
void
|
||||
fail(error_code ec, string_view what)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
log_ << what << ": " << ec.message() << "\n";
|
||||
}
|
||||
|
||||
public:
|
||||
server(
|
||||
boost::asio::ip::tcp::endpoint ep,
|
||||
std::ostream& log)
|
||||
: log_(log)
|
||||
, ioc_(1)
|
||||
, acceptor_(ioc_)
|
||||
, socket_(ioc_)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
|
||||
acceptor_.open(ep.protocol(), ec);
|
||||
if(ec)
|
||||
{
|
||||
fail(ec, "open");
|
||||
return;
|
||||
}
|
||||
|
||||
acceptor_.set_option(
|
||||
boost::asio::socket_base::reuse_address(true), ec);
|
||||
if(ec)
|
||||
{
|
||||
fail(ec, "set_option");
|
||||
return;
|
||||
}
|
||||
|
||||
acceptor_.bind(ep, ec);
|
||||
if(ec)
|
||||
{
|
||||
fail(ec, "bind");
|
||||
return;
|
||||
}
|
||||
|
||||
acceptor_.listen(
|
||||
boost::asio::socket_base::max_listen_connections, ec);
|
||||
if(ec)
|
||||
{
|
||||
fail(ec, "listen");
|
||||
return;
|
||||
}
|
||||
|
||||
acceptor_.async_accept(socket_,
|
||||
[this](error_code ec){ this->on_accept(ec); });
|
||||
|
||||
t_ = std::thread([this]{ ioc_.run(); });
|
||||
}
|
||||
|
||||
~server()
|
||||
{
|
||||
ioc_.stop();
|
||||
t_.join();
|
||||
}
|
||||
|
||||
private:
|
||||
class session
|
||||
: public std::enable_shared_from_this<session>
|
||||
{
|
||||
boost::asio::ip::tcp::socket socket_;
|
||||
std::ostream& log_;
|
||||
|
||||
public:
|
||||
session(
|
||||
boost::asio::ip::tcp::socket sock,
|
||||
std::ostream& log)
|
||||
: socket_(std::move(sock))
|
||||
, log_(log)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
socket_.async_wait(
|
||||
boost::asio::socket_base::wait_read,
|
||||
std::bind(
|
||||
&session::on_read,
|
||||
shared_from_this(),
|
||||
std::placeholders::_1));
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
on_read(error_code ec)
|
||||
{
|
||||
boost::ignore_unused(ec);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
on_accept(error_code ec)
|
||||
{
|
||||
if(! acceptor_.is_open())
|
||||
return;
|
||||
if(ec)
|
||||
fail(ec, "accept");
|
||||
else
|
||||
std::make_shared<session>(
|
||||
std::move(socket_), log_)->run();
|
||||
acceptor_.async_accept(socket_,
|
||||
[this](error_code ec){ this->on_accept(ec); });
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
testAsync()
|
||||
{
|
||||
boost::asio::ip::tcp::endpoint ep(
|
||||
boost::asio::ip::make_address("127.0.0.1"), 8080);
|
||||
server srv(ep, log);
|
||||
{
|
||||
boost::asio::io_context ioc;
|
||||
set_timeout_service_options(
|
||||
ioc, std::chrono::seconds(1));
|
||||
timeout_socket s(ioc);
|
||||
s.next_layer().connect(ep);
|
||||
char buf[32];
|
||||
s.async_read_some(boost::asio::buffer(buf),
|
||||
[&](error_code ec, std::size_t n)
|
||||
{
|
||||
log << "read_some: " << ec.message() << "\n";
|
||||
boost::ignore_unused(ec, n);
|
||||
});
|
||||
ioc.run();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
testAsync();
|
||||
|
||||
pass();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(beast,core,timeout_socket);
|
||||
|
||||
} // beast
|
||||
} // boost
|
11
test/beast/experimental/timeout_work_guard.cpp
Normal file
11
test/beast/experimental/timeout_work_guard.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
//
|
||||
// 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/detail/timeout_work_guard.hpp>
|
Reference in New Issue
Block a user