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-30 16:49:09 -08:00
parent 2d3912751b
commit 6caca92f0e
28 changed files with 3313 additions and 1969 deletions
+5 -2
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
+3
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
+500
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
+132
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
@@ -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
-3
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")
-3
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 ;
@@ -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
@@ -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>