Add close_socket, beast_close_socket customization:

This algorithm allows a socket-like object to be closed in
a generic context. The customization point enables user-defined
types to define the close algorithm to use with `close_socket`.
This commit is contained in:
Vinnie Falco
2019-01-28 22:05:01 -08:00
parent 6a658b5c3a
commit 49deee4a69
15 changed files with 361 additions and 62 deletions

View File

@@ -2,6 +2,7 @@ Version 208:
* Add get_lowest_layer free function * Add get_lowest_layer free function
* Add lowest_layer_type metafunction * Add lowest_layer_type metafunction
* Add close_socket, beast_close_socket customization
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@@ -60,6 +60,15 @@ facilities for authoring and working with layered streams:
read and write streams, and additionally implements configurable buffering read and write streams, and additionally implements configurable buffering
for reads. for reads.
]] ]]
[[
[link beast.ref.boost__beast__close_socket `close_socket`]
][
This function closes a socket by performing an unqualified call
to the
[link beast.ref.boost__beast__beast_close_socket `beast_close_socket`]
customization point, allowing sockets to be closed in generic
contexts in an extensible fashion.
]]
[[ [[
[link beast.ref.boost__beast__flat_stream `flat_stream`] [link beast.ref.boost__beast__flat_stream `flat_stream`]
][ ][
@@ -67,20 +76,6 @@ facilities for authoring and working with layered streams:
a limitation of `net::ssl::stream`. It is used in the implementation a limitation of `net::ssl::stream`. It is used in the implementation
of [link beast.ref.boost__beast__ssl_stream `ssl_stream`]. of [link beast.ref.boost__beast__ssl_stream `ssl_stream`].
]] ]]
[[
[link beast.ref.boost__beast__http__icy_stream `http::icy_stream`]
][
An ICY stream transparently converts the non-standard "ICY 200 OK"
HTTP response from Shoutcast servers into a conforming 200 level
HTTP response.
]]
[[
[link beast.ref.boost__beast__ssl_stream `ssl_stream`]
][
The SSL stream is a drop-in replacement for `net::ssl::stream` which
allows for move-construction and move-assignment, and also implements
a work-around for a performance limitation in the original SSL stream.
]]
[[ [[
[link beast.ref.boost__beast__get_lowest_layer `get_lowest_layer`] [link beast.ref.boost__beast__get_lowest_layer `get_lowest_layer`]
][ ][
@@ -92,6 +87,13 @@ facilities for authoring and working with layered streams:
[code_core_3_layers_4] [code_core_3_layers_4]
]] ]]
[[
[link beast.ref.boost__beast__http__icy_stream `http::icy_stream`]
][
An ICY stream transparently converts the non-standard "ICY 200 OK"
HTTP response from Shoutcast servers into a conforming 200 level
HTTP response.
]]
[[ [[
[link beast.ref.boost__beast__lowest_layer_type `lowest_layer_type`] [link beast.ref.boost__beast__lowest_layer_type `lowest_layer_type`]
][ ][
@@ -100,6 +102,13 @@ facilities for authoring and working with layered streams:
of reference returned by of reference returned by
[link beast.ref.boost__beast__get_lowest_layer `get_lowest_layer`] [link beast.ref.boost__beast__get_lowest_layer `get_lowest_layer`]
]] ]]
[[
[link beast.ref.boost__beast__ssl_stream `ssl_stream`]
][
The SSL stream is a drop-in replacement for `net::ssl::stream` which
allows for move-construction and move-assignment, and also implements
a work-around for a performance limitation in the original SSL stream.
]]
] ]
[heading Example] [heading Example]

View File

@@ -225,6 +225,7 @@
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__allocate_stable">allocate_stable</link></member> <member><link linkend="beast.ref.boost__beast__allocate_stable">allocate_stable</link></member>
<member><link linkend="beast.ref.boost__beast__async_connect">async_connect</link></member> <member><link linkend="beast.ref.boost__beast__async_connect">async_connect</link></member>
<member><link linkend="beast.ref.boost__beast__beast_close_socket">beast_close_socket</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_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_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__bind_handler">bind_handler</link></member>

View File

@@ -504,6 +504,15 @@ public:
#endif #endif
}; };
#if ! BOOST_BEAST_DOXYGEN
inline
void
beast_close_socket(stream& s)
{
s.close();
}
#endif
#if BOOST_BEAST_DOXYGEN #if BOOST_BEAST_DOXYGEN
/** Return a new stream connected to the given stream /** Return a new stream connected to the given stream

View File

@@ -11,35 +11,121 @@
#define BOOST_BEAST_CLOSE_SOCKET_HPP #define BOOST_BEAST_CLOSE_SOCKET_HPP
#include <boost/beast/core/detail/config.hpp> #include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/static_const.hpp>
#include <boost/asio/basic_socket.hpp> #include <boost/asio/basic_socket.hpp>
namespace boost { namespace boost {
namespace beast { namespace beast {
namespace detail { /** Default socket close function.
template<class Protocol> This function is not meant to be called directly. Instead, it
is called automatically when using @ref close_socket. To enable
closure of user-defined types or classes derived from a particular
user-defined type, this function should be overloaded in the
corresponding namespace for the type in question.
@see close_socket
*/
template<class Protocol BOOST_ASIO_SVC_TPARAM>
void void
beast_close_socket( beast_close_socket(
net::basic_socket<Protocol>& sock) net::basic_socket<Protocol BOOST_ASIO_SVC_TPARAM>& sock)
{ {
boost::system::error_code ec; boost::system::error_code ec;
sock.close(ec); sock.close(ec);
} }
namespace detail {
struct close_socket_impl
{
template<class T>
void
operator()(T& t) const
{
using beast::beast_close_socket;
beast_close_socket(t);
}
};
} // detail } // detail
/** Close a socket. /** Close a socket or socket-like object.
@param sock The socket to close. This function attempts to close an object representing a socket.
In this context, a socket is an object for which an unqualified
call to the function `void beast_close_socket(Socket&)` is
well-defined. The function `beast_close_socket` is a
<em>customization point</em>, allowing user-defined types to
provide an algorithm for performing the close operation by
overloading this function for the type in question.
Since the customization point is a function call, the normal
rules for finding the correct overload are applied including
the rules for argument-dependent lookup ("ADL"). This permits
classes derived from a type for which a customization is provided
to inherit the customization point.
An overload for the networking class template `net::basic_socket`
is provided, which implements the close algorithm for all socket-like
objects (hence the name of this customization point). When used
in conjunction with @ref get_lowest_layer, a generic algorithm
operating on a layered stream can perform a closure of the underlying
socket without knowing the exact list of concrete types.
@par Example 1
The following generic function synchronously sends a message
on the stream, then closes the socket.
@code
template <class WriteStream>
void hello_and_close (WriteStream& stream)
{
net::write(stream, net::const_buffer("Hello, world!", 13));
close_socket(get_lowest_layer(stream));
}
@endcode
To enable closure of user defined types, it is necessary to provide
an overload of the function `beast_close_socket` for the type.
@par Example 2
The following code declares a user-defined type which contains a
private socket, and provides an overload of the customization
point which closes the private socket.
@code
class my_socket
{
net::ip::tcp::socket sock_;
public:
my_socket(net::io_context& ioc)
: sock_(ioc)
{
}
friend void beast_close_socket(my_socket& s)
{
error_code ec;
s.sock_.close(ec);
// ignore the error
}
};
@endcode
@param sock The socket to close. If the customization point is not
defined for the type of this object, or one of its base classes,
then a compiler error results.
@see beast_close_socket
*/ */
#if BOOST_BEAST_DOXYGEN
template<class Socket> template<class Socket>
void void
close_socket(Socket& sock) close_socket(Socket& sock);
{ #else
using detail::beast_close_socket; BOOST_BEAST_INLINE_VARIABLE(close_socket, detail::close_socket_impl)
beast_close_socket(sock); #endif
}
} // beast } // beast
} // boost } // boost

View File

@@ -73,4 +73,11 @@ namespace net = boost::asio;
# define BOOST_BEAST_DECL inline # define BOOST_BEAST_DECL inline
#endif #endif
#define BOOST_BEAST_INLINE_VARIABLE(name, type) \
inline namespace \
{ \
constexpr auto &name = \
::boost::beast::detail::static_const<type>::value; \
}
#endif #endif

View File

@@ -17,46 +17,38 @@ namespace boost {
namespace beast { namespace beast {
namespace detail { namespace detail {
template<class T, class = void> template <class T>
struct has_next_layer : std::false_type std::false_type has_next_layer_impl(void*) {}
{
};
template<class T> template <class T>
struct has_next_layer<T, boost::void_t< auto has_next_layer_impl(decltype(nullptr)) ->
decltype(std::declval<T>().next_layer())>> decltype(std::declval<T&>().next_layer(), std::true_type{})
: std::true_type
{ {
}; }
template<class T, class = void> template <class T>
using has_next_layer = decltype(has_next_layer_impl<T>(nullptr));
//---
template<class T, bool = has_next_layer<T>::value>
struct lowest_layer_type_impl struct lowest_layer_type_impl
{ {
using type = typename std::remove_reference<T>::type; using type = typename std::remove_reference<T>::type;
}; };
template<class T> template<class T>
struct lowest_layer_type_impl<T, boost::void_t< struct lowest_layer_type_impl<T, true>
decltype(std::declval<T>().next_layer())>>
{ {
using type = typename lowest_layer_type_impl< using type = typename lowest_layer_type_impl<
decltype(std::declval<T>().next_layer())>::type; decltype(std::declval<T&>().next_layer())>::type;
}; };
template<class T> template<class T>
using lowest_layer_type = typename using lowest_layer_type = typename
lowest_layer_type_impl<T>::type; lowest_layer_type_impl<T>::type;
template<class T> //---
lowest_layer_type<T>&
get_lowest_layer_impl(
T& t, std::true_type) noexcept
{
using type = typename std::decay<
decltype(t.next_layer())>::type;
return get_lowest_layer_impl(t.next_layer(),
has_next_layer<type>{});
}
template<class T> template<class T>
T& T&
@@ -66,6 +58,16 @@ get_lowest_layer_impl(
return t; return t;
} }
template<class T>
lowest_layer_type<T>&
get_lowest_layer_impl(
T& t, std::true_type) noexcept
{
return get_lowest_layer_impl(t.next_layer(),
has_next_layer<typename std::decay<
decltype(t.next_layer())>::type>{});
}
} // detail } // detail
} // beast } // beast
} // boost } // boost

View File

@@ -0,0 +1,42 @@
//
// Copyright (c) 2019 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_DETAIL_STATIC_CONST_HPP
#define BOOST_BEAST_DETAIL_STATIC_CONST_HPP
/* This is a derivative work, original copyright:
Copyright Eric Niebler 2013-present
Use, modification and distribution is subject to 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)
Project home: https://github.com/ericniebler/range-v3
*/
namespace boost {
namespace beast {
namespace detail {
template<typename T>
struct static_const
{
static constexpr T value {};
};
template<typename T>
constexpr T static_const<T>::value;
} // detail
} // beast
} // boost
#endif

View File

@@ -73,6 +73,8 @@ using lowest_layer_type = detail::lowest_layer_type<T>;
@endcode @endcode
@param t The layer in a stack of layered objects for which the lowest layer is returned. @param t The layer in a stack of layered objects for which the lowest layer is returned.
@see @ref close_socket, @ref lowest_layer_type
*/ */
template<class T> template<class T>
lowest_layer_type<T>& lowest_layer_type<T>&

View File

@@ -289,9 +289,10 @@ struct is_sync_write_stream : std::false_type {};
template<class T> template<class T>
struct is_sync_write_stream<T, detail::void_t<decltype( struct is_sync_write_stream<T, detail::void_t<decltype(
(
std::declval<std::size_t&>() = std::declval<T&>().write_some( std::declval<std::size_t&>() = std::declval<T&>().write_some(
std::declval<detail::ConstBufferSequence>()), std::declval<detail::ConstBufferSequence>()))
std::declval<std::size_t&>() = std::declval<T&>().write_some( ,std::declval<std::size_t&>() = std::declval<T&>().write_some(
std::declval<detail::ConstBufferSequence>(), std::declval<detail::ConstBufferSequence>(),
std::declval<boost::system::error_code&>()) std::declval<boost::system::error_code&>())
)>> : std::true_type {}; )>> : std::true_type {};

View File

@@ -0,0 +1,44 @@
//
// 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/static_const.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/beast/core/close_socket.hpp>
namespace boost {
namespace beast {
struct foo_impl
{
void operator()() const noexcept
{
BEAST_PASS();
}
};
constexpr auto& bar =
boost::beast::detail::static_const<detail::close_socket_impl>::value;
class static_const_test : public beast::unit_test::suite
{
public:
void
run() override
{
pass();
}
};
BEAST_DEFINE_TESTSUITE(beast,core,static_const);
} // beast
} // boost

View File

@@ -11,9 +11,12 @@
#include <boost/beast/core/close_socket.hpp> #include <boost/beast/core/close_socket.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp> #include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/beast/_experimental/test/stream.hpp>
#include <boost/beast/core/get_lowest_layer.hpp> #include <boost/beast/core/get_lowest_layer.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/asio/ip/tcp.hpp> #include <boost/asio/ip/tcp.hpp>
#include <boost/asio/write.hpp>
#include <utility> #include <utility>
namespace boost { namespace boost {
@@ -52,18 +55,72 @@ public:
BEAST_EXPECT(! sock.is_open()); BEAST_EXPECT(! sock.is_open());
} }
{ {
layer<net::ip::tcp::socket> layer(ioc); using type = layer<net::ip::tcp::socket>;
type layer(ioc);
layer.next_layer().open(net::ip::tcp::v4()); layer.next_layer().open(net::ip::tcp::v4());
BEAST_EXPECT(layer.next_layer().is_open()); BEAST_EXPECT(layer.next_layer().is_open());
BOOST_STATIC_ASSERT(detail::has_next_layer<type>::value);
BOOST_STATIC_ASSERT((std::is_same<
typename std::decay<decltype(get_lowest_layer(layer))>::type,
lowest_layer_type<decltype(layer)>>::value));
BOOST_STATIC_ASSERT(std::is_same<net::ip::tcp::socket&,
decltype(get_lowest_layer(layer))>::value);
BOOST_STATIC_ASSERT(std::is_same<net::ip::tcp::socket,
lowest_layer_type<decltype(layer)>>::value);
close_socket(get_lowest_layer(layer)); close_socket(get_lowest_layer(layer));
BEAST_EXPECT(! layer.next_layer().is_open()); BEAST_EXPECT(! layer.next_layer().is_open());
} }
{
test::stream ts(ioc);
close_socket(ts);
}
} }
//--------------------------------------------------------------------------
template <class WriteStream>
void hello_and_close (WriteStream& stream)
{
net::write(stream, net::const_buffer("Hello, world!", 13));
close_socket(get_lowest_layer(stream));
}
class my_socket
{
net::ip::tcp::socket sock_;
public:
my_socket(net::io_context& ioc)
: sock_(ioc)
{
}
friend void beast_close_socket(my_socket& s)
{
error_code ec;
s.sock_.close(ec);
// ignore the error
}
};
void
testJavadocs()
{
BEAST_EXPECT(&close_socket_test::template hello_and_close<net::ip::tcp::socket>);
{
net::io_context ioc;
my_socket s(ioc);
close_socket(s);
}
}
//--------------------------------------------------------------------------
void void
run() override run() override
{ {
testClose(); testClose();
testJavadocs();
} }
}; };

View File

@@ -34,7 +34,7 @@ public:
} }
template<class T> template<class T>
std::size_t write_some(T const&, error_code&) std::size_t write_some(T const&, boost::system::error_code&)
{ {
return 0; return 0;
} }
@@ -163,8 +163,9 @@ public:
void void
testJavadoc() testJavadoc()
{ {
using type = write_stream<without>; write_stream<without> s;
type s; BOOST_STATIC_ASSERT(
is_sync_write_stream<without>::value);
BOOST_STATIC_ASSERT(std::is_same< BOOST_STATIC_ASSERT(std::is_same<
decltype(get_lowest_layer(s)), without&>::value); decltype(get_lowest_layer(s)), without&>::value);

View File

@@ -35,15 +35,6 @@ public:
is_mutable_dynamic_buffer< is_mutable_dynamic_buffer<
static_buffer_base>::value); static_buffer_base>::value);
#if ! defined( BOOST_LIBSTDCXX_VERSION ) || BOOST_LIBSTDCXX_VERSION >= 50000
# ifndef BOOST_ASIO_ENABLE_BUFFER_DEBUGGING
BOOST_STATIC_ASSERT(std::is_trivially_copyable<
static_buffer_base::const_buffers_type>::value);
BOOST_STATIC_ASSERT(std::is_trivially_copyable<
static_buffer_base::mutable_data_type>::value);
# endif
#endif
void void
testDynamicBuffer() testDynamicBuffer()
{ {

View File

@@ -10,6 +10,7 @@
// Test that header file is self-contained. // Test that header file is self-contained.
#include <boost/beast/core/type_traits.hpp> #include <boost/beast/core/type_traits.hpp>
#include <boost/beast/_experimental/test/stream.hpp>
#include <boost/asio/ip/tcp.hpp> #include <boost/asio/ip/tcp.hpp>
#include <boost/asio/streambuf.hpp> #include <boost/asio/streambuf.hpp>
#include <boost/asio/detail/consuming_buffers.hpp> #include <boost/asio/detail/consuming_buffers.hpp>
@@ -82,6 +83,46 @@ BOOST_STATIC_ASSERT(! is_completion_handler<H, void(void)>::value);
// stream concepts // stream concepts
// //
struct sync_write_stream
{
net::io_context&
get_io_service();
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers);
template<class ConstBufferSequence>
std::size_t
write_some(
ConstBufferSequence const& buffers, error_code& ec);
};
struct sync_read_stream
{
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers);
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers,
error_code& ec);
};
struct sync_stream : sync_read_stream, sync_write_stream
{
};
BOOST_STATIC_ASSERT(! is_sync_read_stream<sync_write_stream>::value);
BOOST_STATIC_ASSERT(! is_sync_write_stream<sync_read_stream>::value);
BOOST_STATIC_ASSERT(is_sync_read_stream<sync_read_stream>::value);
BOOST_STATIC_ASSERT(is_sync_write_stream<sync_write_stream>::value);
BOOST_STATIC_ASSERT(is_sync_read_stream<sync_stream>::value);
BOOST_STATIC_ASSERT(is_sync_write_stream<sync_stream>::value);
namespace { namespace {
using stream_type = net::ip::tcp::socket; using stream_type = net::ip::tcp::socket;
@@ -106,6 +147,11 @@ BOOST_STATIC_ASSERT(! is_async_write_stream<not_a_stream>::value);
BOOST_STATIC_ASSERT(! is_sync_read_stream<not_a_stream>::value); BOOST_STATIC_ASSERT(! is_sync_read_stream<not_a_stream>::value);
BOOST_STATIC_ASSERT(! is_sync_write_stream<not_a_stream>::value); BOOST_STATIC_ASSERT(! is_sync_write_stream<not_a_stream>::value);
BOOST_STATIC_ASSERT(is_sync_read_stream<test::stream>::value);
BOOST_STATIC_ASSERT(is_sync_write_stream<test::stream>::value);
BOOST_STATIC_ASSERT(is_async_read_stream<test::stream>::value);
BOOST_STATIC_ASSERT(is_async_write_stream<test::stream>::value);
} // (anonymous) } // (anonymous)
} // beast } // beast