diff --git a/CHANGELOG.md b/CHANGELOG.md index 80364f46..8cf696ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Version 208: * Add get_lowest_layer free function * Add lowest_layer_type metafunction +* Add close_socket, beast_close_socket customization -------------------------------------------------------------------------------- diff --git a/doc/qbk/03_core/3_layers.qbk b/doc/qbk/03_core/3_layers.qbk index 8778e000..744f7782 100644 --- a/doc/qbk/03_core/3_layers.qbk +++ b/doc/qbk/03_core/3_layers.qbk @@ -60,6 +60,15 @@ facilities for authoring and working with layered streams: read and write streams, and additionally implements configurable buffering 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`] ][ @@ -67,20 +76,6 @@ facilities for authoring and working with layered streams: a limitation of `net::ssl::stream`. It is used in the implementation 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`] ][ @@ -92,6 +87,13 @@ facilities for authoring and working with layered streams: [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`] ][ @@ -100,6 +102,13 @@ facilities for authoring and working with layered streams: of reference returned by [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] diff --git a/doc/qbk/quickref.xml b/doc/qbk/quickref.xml index cad0d9cd..b68d4016 100644 --- a/doc/qbk/quickref.xml +++ b/doc/qbk/quickref.xml @@ -225,6 +225,7 @@ allocate_stable async_connect + beast_close_socket bind_back_handler bind_front_handler bind_handler diff --git a/include/boost/beast/_experimental/test/stream.hpp b/include/boost/beast/_experimental/test/stream.hpp index c1c0f519..d452483a 100644 --- a/include/boost/beast/_experimental/test/stream.hpp +++ b/include/boost/beast/_experimental/test/stream.hpp @@ -504,6 +504,15 @@ public: #endif }; +#if ! BOOST_BEAST_DOXYGEN +inline +void +beast_close_socket(stream& s) +{ + s.close(); +} +#endif + #if BOOST_BEAST_DOXYGEN /** Return a new stream connected to the given stream diff --git a/include/boost/beast/core/close_socket.hpp b/include/boost/beast/core/close_socket.hpp index 3dcf2be8..29f32cf4 100644 --- a/include/boost/beast/core/close_socket.hpp +++ b/include/boost/beast/core/close_socket.hpp @@ -11,35 +11,121 @@ #define BOOST_BEAST_CLOSE_SOCKET_HPP #include +#include #include namespace boost { namespace beast { -namespace detail { +/** Default socket close function. -template + 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 void beast_close_socket( - net::basic_socket& sock) + net::basic_socket& sock) { boost::system::error_code ec; sock.close(ec); } +namespace detail { + +struct close_socket_impl +{ + template + void + operator()(T& t) const + { + using beast::beast_close_socket; + beast_close_socket(t); + } +}; + } // 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 + customization point, 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 + 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 void -close_socket(Socket& sock) -{ - using detail::beast_close_socket; - beast_close_socket(sock); -} +close_socket(Socket& sock); +#else +BOOST_BEAST_INLINE_VARIABLE(close_socket, detail::close_socket_impl) +#endif } // beast } // boost diff --git a/include/boost/beast/core/detail/config.hpp b/include/boost/beast/core/detail/config.hpp index a96092f4..2476d83b 100644 --- a/include/boost/beast/core/detail/config.hpp +++ b/include/boost/beast/core/detail/config.hpp @@ -73,4 +73,11 @@ namespace net = boost::asio; # define BOOST_BEAST_DECL inline #endif +#define BOOST_BEAST_INLINE_VARIABLE(name, type) \ + inline namespace \ + { \ + constexpr auto &name = \ + ::boost::beast::detail::static_const::value; \ + } + #endif diff --git a/include/boost/beast/core/detail/get_lowest_layer.hpp b/include/boost/beast/core/detail/get_lowest_layer.hpp index 8d71f304..12bd3776 100644 --- a/include/boost/beast/core/detail/get_lowest_layer.hpp +++ b/include/boost/beast/core/detail/get_lowest_layer.hpp @@ -17,46 +17,38 @@ namespace boost { namespace beast { namespace detail { -template -struct has_next_layer : std::false_type -{ -}; +template +std::false_type has_next_layer_impl(void*) {} -template -struct has_next_layer().next_layer())>> - : std::true_type +template +auto has_next_layer_impl(decltype(nullptr)) -> + decltype(std::declval().next_layer(), std::true_type{}) { -}; +} -template +template +using has_next_layer = decltype(has_next_layer_impl(nullptr)); + +//--- + +template::value> struct lowest_layer_type_impl { using type = typename std::remove_reference::type; }; template -struct lowest_layer_type_impl().next_layer())>> +struct lowest_layer_type_impl { using type = typename lowest_layer_type_impl< - decltype(std::declval().next_layer())>::type; + decltype(std::declval().next_layer())>::type; }; template using lowest_layer_type = typename lowest_layer_type_impl::type; -template -lowest_layer_type& -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{}); -} +//--- template T& @@ -66,6 +58,16 @@ get_lowest_layer_impl( return t; } +template +lowest_layer_type& +get_lowest_layer_impl( + T& t, std::true_type) noexcept +{ + return get_lowest_layer_impl(t.next_layer(), + has_next_layer::type>{}); +} + } // detail } // beast } // boost diff --git a/include/boost/beast/core/detail/static_const.hpp b/include/boost/beast/core/detail/static_const.hpp new file mode 100644 index 00000000..ff487eaf --- /dev/null +++ b/include/boost/beast/core/detail/static_const.hpp @@ -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 +struct static_const +{ + static constexpr T value {}; +}; + +template +constexpr T static_const::value; + +} // detail +} // beast +} // boost + +#endif diff --git a/include/boost/beast/core/get_lowest_layer.hpp b/include/boost/beast/core/get_lowest_layer.hpp index 6cf92469..2ca3e77a 100644 --- a/include/boost/beast/core/get_lowest_layer.hpp +++ b/include/boost/beast/core/get_lowest_layer.hpp @@ -73,6 +73,8 @@ using lowest_layer_type = detail::lowest_layer_type; @endcode @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 lowest_layer_type& diff --git a/include/boost/beast/core/type_traits.hpp b/include/boost/beast/core/type_traits.hpp index 91b50c73..17c91d5e 100644 --- a/include/boost/beast/core/type_traits.hpp +++ b/include/boost/beast/core/type_traits.hpp @@ -289,9 +289,10 @@ struct is_sync_write_stream : std::false_type {}; template struct is_sync_write_stream() = std::declval().write_some( - std::declval()), - std::declval() = std::declval().write_some( + std::declval())) + ,std::declval() = std::declval().write_some( std::declval(), std::declval()) )>> : std::true_type {}; diff --git a/test/beast/core/_detail_static_const.cpp b/test/beast/core/_detail_static_const.cpp new file mode 100644 index 00000000..9c15f42f --- /dev/null +++ b/test/beast/core/_detail_static_const.cpp @@ -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 + +#include + +#include + +namespace boost { +namespace beast { + +struct foo_impl +{ + void operator()() const noexcept + { + BEAST_PASS(); + } +}; + +constexpr auto& bar = + boost::beast::detail::static_const::value; + +class static_const_test : public beast::unit_test::suite +{ +public: + void + run() override + { + pass(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,core,static_const); + +} // beast +} // boost diff --git a/test/beast/core/close_socket.cpp b/test/beast/core/close_socket.cpp index 13e8f660..13229128 100644 --- a/test/beast/core/close_socket.cpp +++ b/test/beast/core/close_socket.cpp @@ -11,9 +11,12 @@ #include #include +#include #include +#include #include +#include #include namespace boost { @@ -52,18 +55,72 @@ public: BEAST_EXPECT(! sock.is_open()); } { - layer layer(ioc); + using type = layer; + type layer(ioc); layer.next_layer().open(net::ip::tcp::v4()); BEAST_EXPECT(layer.next_layer().is_open()); + BOOST_STATIC_ASSERT(detail::has_next_layer::value); + BOOST_STATIC_ASSERT((std::is_same< + typename std::decay::type, + lowest_layer_type>::value)); + BOOST_STATIC_ASSERT(std::is_same::value); + BOOST_STATIC_ASSERT(std::is_same>::value); close_socket(get_lowest_layer(layer)); BEAST_EXPECT(! layer.next_layer().is_open()); } + { + test::stream ts(ioc); + close_socket(ts); + } } + //-------------------------------------------------------------------------- + + template + 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::io_context ioc; + my_socket s(ioc); + close_socket(s); + } + } + + //-------------------------------------------------------------------------- + void run() override { testClose(); + testJavadocs(); } }; diff --git a/test/beast/core/get_lowest_layer.cpp b/test/beast/core/get_lowest_layer.cpp index 8e8af654..7cb784f2 100644 --- a/test/beast/core/get_lowest_layer.cpp +++ b/test/beast/core/get_lowest_layer.cpp @@ -34,7 +34,7 @@ public: } template - std::size_t write_some(T const&, error_code&) + std::size_t write_some(T const&, boost::system::error_code&) { return 0; } @@ -163,8 +163,9 @@ public: void testJavadoc() { - using type = write_stream; - type s; + write_stream s; + BOOST_STATIC_ASSERT( + is_sync_write_stream::value); BOOST_STATIC_ASSERT(std::is_same< decltype(get_lowest_layer(s)), without&>::value); diff --git a/test/beast/core/static_buffer.cpp b/test/beast/core/static_buffer.cpp index 96143a0b..afbd2ad4 100644 --- a/test/beast/core/static_buffer.cpp +++ b/test/beast/core/static_buffer.cpp @@ -35,15 +35,6 @@ public: is_mutable_dynamic_buffer< 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 testDynamicBuffer() { diff --git a/test/beast/core/type_traits.cpp b/test/beast/core/type_traits.cpp index 1821edf3..dbfdc5bf 100644 --- a/test/beast/core/type_traits.cpp +++ b/test/beast/core/type_traits.cpp @@ -10,6 +10,7 @@ // Test that header file is self-contained. #include +#include #include #include #include @@ -82,6 +83,46 @@ BOOST_STATIC_ASSERT(! is_completion_handler::value); // stream concepts // +struct sync_write_stream +{ + net::io_context& + get_io_service(); + + template + std::size_t + write_some(ConstBufferSequence const& buffers); + + template + std::size_t + write_some( + ConstBufferSequence const& buffers, error_code& ec); +}; + +struct sync_read_stream +{ + template + std::size_t + read_some(MutableBufferSequence const& buffers); + + template + 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::value); +BOOST_STATIC_ASSERT(! is_sync_write_stream::value); + +BOOST_STATIC_ASSERT(is_sync_read_stream::value); +BOOST_STATIC_ASSERT(is_sync_write_stream::value); + +BOOST_STATIC_ASSERT(is_sync_read_stream::value); +BOOST_STATIC_ASSERT(is_sync_write_stream::value); + namespace { using stream_type = net::ip::tcp::socket; @@ -106,6 +147,11 @@ BOOST_STATIC_ASSERT(! is_async_write_stream::value); BOOST_STATIC_ASSERT(! is_sync_read_stream::value); BOOST_STATIC_ASSERT(! is_sync_write_stream::value); +BOOST_STATIC_ASSERT(is_sync_read_stream::value); +BOOST_STATIC_ASSERT(is_sync_write_stream::value); +BOOST_STATIC_ASSERT(is_async_read_stream::value); +BOOST_STATIC_ASSERT(is_async_write_stream::value); + } // (anonymous) } // beast