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:
Vinnie Falco
2018-07-28 12:01:15 -07:00
parent f4c3e52a8c
commit ef0658f678
16 changed files with 1193 additions and 1 deletions

View File

@@ -1,3 +1,9 @@
Version 187:
* Add experimental timeout_socket
--------------------------------------------------------------------------------
Version 186: Version 186:
* basic_fields uses intrusive base hooks * basic_fields uses intrusive base hooks

View File

@@ -27,12 +27,16 @@
* ([issue 1091]) Fix timer on websocket upgrade in examples * ([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 * ([issue 1267]) Fix parsing of out-of-bounds hex values
* Workaround for http-server-fast and libstdc++ * Workaround for http-server-fast and libstdc++
[*Experimental]
* Add [link beast.ref.boost__beast__timeout_socket `timeout_socket`]

View File

@@ -302,16 +302,19 @@
<entry valign="top"> <entry valign="top">
<bridgehead renderas="sect3">Classes</bridgehead> <bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1"> <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__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__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__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__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__test__stream">test::stream</link></member>
<member><link linkend="beast.ref.boost__beast__timeout_socket">timeout_socket</link></member>
</simplelist> </simplelist>
</entry> </entry>
<entry valign="top"> <entry valign="top">
<bridgehead renderas="sect3">Functions</bridgehead> <bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1"> <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> <member><link linkend="beast.ref.boost__beast__test__connect">test::connect</link></member>
</simplelist> </simplelist>
</entry> </entry>

View 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
//
#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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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

View 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

View 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

View 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

View File

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

View File

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

View 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

View 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

View 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>