| 1 | // |
| 2 | // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) |
| 3 | // |
| 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying |
| 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| 6 | // |
| 7 | // Official repository: https://github.com/boostorg/beast |
| 8 | // |
| 9 | |
| 10 | #ifndef BOOST_BEAST_STREAM_TRAITS_HPP |
| 11 | #define BOOST_BEAST_STREAM_TRAITS_HPP |
| 12 | |
| 13 | #include <boost/beast/core/detail/config.hpp> |
| 14 | #include <boost/beast/core/detail/static_const.hpp> |
| 15 | #include <boost/beast/core/detail/stream_traits.hpp> |
| 16 | #include <boost/asio/basic_socket.hpp> |
| 17 | |
| 18 | namespace boost { |
| 19 | namespace beast { |
| 20 | |
| 21 | /** A trait to determine the lowest layer type of a stack of stream layers. |
| 22 | |
| 23 | If `t.next_layer()` is well-defined for an object `t` of type `T`, |
| 24 | then `lowest_layer_type<T>` will be an alias for |
| 25 | `lowest_layer_type<decltype(t.next_layer())>`, |
| 26 | otherwise it will be the type |
| 27 | `std::remove_reference<T>`. |
| 28 | |
| 29 | @param T The type to determine the lowest layer type of. |
| 30 | |
| 31 | @return The type of the lowest layer. |
| 32 | */ |
| 33 | template<class T> |
| 34 | #if BOOST_BEAST_DOXYGEN |
| 35 | using lowest_layer_type = __see_below__; |
| 36 | #else |
| 37 | using lowest_layer_type = detail::lowest_layer_type<T>; |
| 38 | #endif |
| 39 | |
| 40 | /** Return the lowest layer in a stack of stream layers. |
| 41 | |
| 42 | If `t.next_layer()` is well-defined, returns |
| 43 | `get_lowest_layer(t.next_layer())`. Otherwise, it returns `t`. |
| 44 | |
| 45 | A stream layer is an object of class type which wraps another object through |
| 46 | composition, and meets some or all of the named requirements of the wrapped |
| 47 | type while optionally changing behavior. Examples of stream layers include |
| 48 | `net::ssl::stream` or @ref beast::websocket::stream. The owner of a stream |
| 49 | layer can interact directly with the wrapper, by passing it to stream |
| 50 | algorithms. Or, the owner can obtain a reference to the wrapped object by |
| 51 | calling `next_layer()` and accessing its members. This is necessary when it is |
| 52 | desired to access functionality in the next layer which is not available |
| 53 | in the wrapper. For example, @ref websocket::stream permits reading and |
| 54 | writing, but in order to establish the underlying connection, members |
| 55 | of the wrapped stream (such as `connect`) must be invoked directly. |
| 56 | |
| 57 | Usually the last object in the chain of composition is the concrete socket |
| 58 | object (for example, a `net::basic_socket` or a class derived from it). |
| 59 | The function @ref get_lowest_layer exists to easily obtain the concrete |
| 60 | socket when it is desired to perform an action that is not prescribed by |
| 61 | a named requirement, such as changing a socket option, cancelling all |
| 62 | pending asynchronous I/O, or closing the socket (perhaps by using |
| 63 | @ref close_socket). |
| 64 | |
| 65 | @par Example |
| 66 | @code |
| 67 | // Set non-blocking mode on a stack of stream |
| 68 | // layers with a regular socket at the lowest layer. |
| 69 | template <class Stream> |
| 70 | void set_non_blocking (Stream& stream) |
| 71 | { |
| 72 | error_code ec; |
| 73 | // A compile error here means your lowest layer is not the right type! |
| 74 | get_lowest_layer(stream).non_blocking(true, ec); |
| 75 | if(ec) |
| 76 | throw system_error{ec}; |
| 77 | } |
| 78 | @endcode |
| 79 | |
| 80 | @param t The layer in a stack of layered objects for which the lowest layer is returned. |
| 81 | |
| 82 | @see close_socket, lowest_layer_type |
| 83 | */ |
| 84 | template<class T> |
| 85 | lowest_layer_type<T>& |
| 86 | get_lowest_layer(T& t) noexcept |
| 87 | { |
| 88 | return detail::get_lowest_layer_impl( |
| 89 | t, detail::has_next_layer<T>{}); |
| 90 | } |
| 91 | |
| 92 | //------------------------------------------------------------------------------ |
| 93 | |
| 94 | /** A trait to determine the return type of get_executor. |
| 95 | |
| 96 | This type alias will be the type of values returned by |
| 97 | by calling member `get_exector` on an object of type `T&`. |
| 98 | |
| 99 | @param T The type to query |
| 100 | |
| 101 | @return The type of values returned from `get_executor`. |
| 102 | */ |
| 103 | // Workaround for ICE on gcc 4.8 |
| 104 | #if BOOST_BEAST_DOXYGEN |
| 105 | template<class T> |
| 106 | using executor_type = __see_below__; |
| 107 | #elif BOOST_WORKAROUND(BOOST_GCC, < 40900) |
| 108 | template<class T> |
| 109 | using executor_type = |
| 110 | typename std::decay<T>::type::executor_type; |
| 111 | #else |
| 112 | template<class T> |
| 113 | using executor_type = |
| 114 | typename std::decay<decltype(std::declval<T&>().get_executor())>::type; |
| 115 | #endif |
| 116 | |
| 117 | /** Determine if `T` has the `get_executor` member function. |
| 118 | |
| 119 | Metafunctions are used to perform compile time checking of template |
| 120 | types. This type will be `std::true_type` if `T` has the member |
| 121 | function with the correct signature, else type will be `std::false_type`. |
| 122 | |
| 123 | @par Example |
| 124 | |
| 125 | Use with tag dispatching: |
| 126 | |
| 127 | @code |
| 128 | template<class T> |
| 129 | void maybe_hello(T const& t, std::true_type) |
| 130 | { |
| 131 | net::post( |
| 132 | t.get_executor(), |
| 133 | [] |
| 134 | { |
| 135 | std::cout << "Hello, world!" << std::endl; |
| 136 | }); |
| 137 | } |
| 138 | |
| 139 | template<class T> |
| 140 | void maybe_hello(T const&, std::false_type) |
| 141 | { |
| 142 | // T does not have get_executor |
| 143 | } |
| 144 | |
| 145 | template<class T> |
| 146 | void maybe_hello(T const& t) |
| 147 | { |
| 148 | maybe_hello(t, has_get_executor<T>{}); |
| 149 | } |
| 150 | @endcode |
| 151 | |
| 152 | Use with `static_assert`: |
| 153 | |
| 154 | @code |
| 155 | struct stream |
| 156 | { |
| 157 | using executor_type = net::io_context::executor_type; |
| 158 | executor_type get_executor() noexcept; |
| 159 | }; |
| 160 | |
| 161 | static_assert(has_get_executor<stream>::value, "Missing get_executor member"); |
| 162 | @endcode |
| 163 | */ |
| 164 | #if BOOST_BEAST_DOXYGEN |
| 165 | template<class T> |
| 166 | using has_get_executor = __see_below__; |
| 167 | #else |
| 168 | template<class T, class = void> |
| 169 | struct has_get_executor : std::false_type {}; |
| 170 | |
| 171 | template<class T> |
| 172 | struct has_get_executor<T, boost::void_t<decltype( |
| 173 | std::declval<T&>().get_executor())>> : std::true_type {}; |
| 174 | #endif |
| 175 | |
| 176 | //------------------------------------------------------------------------------ |
| 177 | |
| 178 | /** Determine if at type meets the requirements of <em>SyncReadStream</em>. |
| 179 | |
| 180 | Metafunctions are used to perform compile time checking of template |
| 181 | types. This type will be `std::true_type` if `T` meets the requirements, |
| 182 | else the type will be `std::false_type`. |
| 183 | |
| 184 | @par Example |
| 185 | Use with `static_assert`: |
| 186 | @code |
| 187 | template<class SyncReadStream> |
| 188 | void f(SyncReadStream& stream) |
| 189 | { |
| 190 | static_assert(is_sync_read_stream<SyncReadStream>::value, |
| 191 | "SyncReadStream type requirements not met"); |
| 192 | ... |
| 193 | @endcode |
| 194 | |
| 195 | Use with `std::enable_if` (SFINAE): |
| 196 | @code |
| 197 | template<class SyncReadStream> |
| 198 | typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type |
| 199 | f(SyncReadStream& stream); |
| 200 | @endcode |
| 201 | */ |
| 202 | #if BOOST_BEAST_DOXYGEN |
| 203 | template<class T> |
| 204 | using is_sync_read_stream = __see_below__; |
| 205 | #else |
| 206 | template<class T, class = void> |
| 207 | struct is_sync_read_stream : std::false_type {}; |
| 208 | |
| 209 | template<class T> |
| 210 | struct is_sync_read_stream<T, boost::void_t<decltype( |
| 211 | std::declval<std::size_t&>() = std::declval<T&>().read_some( |
| 212 | std::declval<detail::MutableBufferSequence>()), |
| 213 | std::declval<std::size_t&>() = std::declval<T&>().read_some( |
| 214 | std::declval<detail::MutableBufferSequence>(), |
| 215 | std::declval<boost::system::error_code&>()) |
| 216 | )>> : std::true_type {}; |
| 217 | #endif |
| 218 | |
| 219 | /** Determine if `T` meets the requirements of <em>SyncWriteStream</em>. |
| 220 | |
| 221 | Metafunctions are used to perform compile time checking of template |
| 222 | types. This type will be `std::true_type` if `T` meets the requirements, |
| 223 | else the type will be `std::false_type`. |
| 224 | |
| 225 | @par Example |
| 226 | |
| 227 | Use with `static_assert`: |
| 228 | |
| 229 | @code |
| 230 | template<class SyncReadStream> |
| 231 | void f(SyncReadStream& stream) |
| 232 | { |
| 233 | static_assert(is_sync_read_stream<SyncReadStream>::value, |
| 234 | "SyncReadStream type requirements not met"); |
| 235 | ... |
| 236 | @endcode |
| 237 | |
| 238 | Use with `std::enable_if` (SFINAE): |
| 239 | |
| 240 | @code |
| 241 | template<class SyncReadStream> |
| 242 | typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type |
| 243 | f(SyncReadStream& stream); |
| 244 | @endcode |
| 245 | */ |
| 246 | #if BOOST_BEAST_DOXYGEN |
| 247 | template<class T> |
| 248 | using is_sync_write_stream = __see_below__; |
| 249 | #else |
| 250 | template<class T, class = void> |
| 251 | struct is_sync_write_stream : std::false_type {}; |
| 252 | |
| 253 | template<class T> |
| 254 | struct is_sync_write_stream<T, boost::void_t<decltype( |
| 255 | ( |
| 256 | std::declval<std::size_t&>() = std::declval<T&>().write_some( |
| 257 | std::declval<detail::ConstBufferSequence>())) |
| 258 | ,std::declval<std::size_t&>() = std::declval<T&>().write_some( |
| 259 | std::declval<detail::ConstBufferSequence>(), |
| 260 | std::declval<boost::system::error_code&>()) |
| 261 | )>> : std::true_type {}; |
| 262 | #endif |
| 263 | |
| 264 | /** Determine if `T` meets the requirements of @b SyncStream. |
| 265 | |
| 266 | Metafunctions are used to perform compile time checking of template |
| 267 | types. This type will be `std::true_type` if `T` meets the requirements, |
| 268 | else the type will be `std::false_type`. |
| 269 | |
| 270 | @par Example |
| 271 | |
| 272 | Use with `static_assert`: |
| 273 | |
| 274 | @code |
| 275 | template<class SyncStream> |
| 276 | void f(SyncStream& stream) |
| 277 | { |
| 278 | static_assert(is_sync_stream<SyncStream>::value, |
| 279 | "SyncStream type requirements not met"); |
| 280 | ... |
| 281 | @endcode |
| 282 | |
| 283 | Use with `std::enable_if` (SFINAE): |
| 284 | |
| 285 | @code |
| 286 | template<class SyncStream> |
| 287 | typename std::enable_if<is_sync_stream<SyncStream>::value>::type |
| 288 | f(SyncStream& stream); |
| 289 | @endcode |
| 290 | */ |
| 291 | #if BOOST_BEAST_DOXYGEN |
| 292 | template<class T> |
| 293 | using is_sync_stream = __see_below__; |
| 294 | #else |
| 295 | template<class T> |
| 296 | using is_sync_stream = std::integral_constant<bool, |
| 297 | is_sync_read_stream<T>::value && is_sync_write_stream<T>::value>; |
| 298 | #endif |
| 299 | |
| 300 | //------------------------------------------------------------------------------ |
| 301 | |
| 302 | /** Determine if `T` meets the requirements of <em>AsyncReadStream</em>. |
| 303 | |
| 304 | Metafunctions are used to perform compile time checking of template |
| 305 | types. This type will be `std::true_type` if `T` meets the requirements, |
| 306 | else the type will be `std::false_type`. |
| 307 | |
| 308 | @par Example |
| 309 | |
| 310 | Use with `static_assert`: |
| 311 | |
| 312 | @code |
| 313 | template<class AsyncReadStream> |
| 314 | void f(AsyncReadStream& stream) |
| 315 | { |
| 316 | static_assert(is_async_read_stream<AsyncReadStream>::value, |
| 317 | "AsyncReadStream type requirements not met"); |
| 318 | ... |
| 319 | @endcode |
| 320 | |
| 321 | Use with `std::enable_if` (SFINAE): |
| 322 | |
| 323 | @code |
| 324 | template<class AsyncReadStream> |
| 325 | typename std::enable_if<is_async_read_stream<AsyncReadStream>::value>::type |
| 326 | f(AsyncReadStream& stream); |
| 327 | @endcode |
| 328 | */ |
| 329 | #if BOOST_BEAST_DOXYGEN |
| 330 | template<class T> |
| 331 | using is_async_read_stream = __see_below__; |
| 332 | #else |
| 333 | template<class T, class = void> |
| 334 | struct is_async_read_stream : std::false_type {}; |
| 335 | |
| 336 | template<class T> |
| 337 | struct is_async_read_stream<T, boost::void_t<decltype( |
| 338 | std::declval<T&>().async_read_some( |
| 339 | std::declval<detail::MutableBufferSequence>(), |
| 340 | std::declval<detail::ReadHandler>()) |
| 341 | )>> : std::integral_constant<bool, |
| 342 | has_get_executor<T>::value |
| 343 | > {}; |
| 344 | #endif |
| 345 | |
| 346 | /** Determine if `T` meets the requirements of <em>AsyncWriteStream</em>. |
| 347 | |
| 348 | Metafunctions are used to perform compile time checking of template |
| 349 | types. This type will be `std::true_type` if `T` meets the requirements, |
| 350 | else the type will be `std::false_type`. |
| 351 | |
| 352 | @par Example |
| 353 | |
| 354 | Use with `static_assert`: |
| 355 | |
| 356 | @code |
| 357 | template<class AsyncWriteStream> |
| 358 | void f(AsyncWriteStream& stream) |
| 359 | { |
| 360 | static_assert(is_async_write_stream<AsyncWriteStream>::value, |
| 361 | "AsyncWriteStream type requirements not met"); |
| 362 | ... |
| 363 | @endcode |
| 364 | |
| 365 | Use with `std::enable_if` (SFINAE): |
| 366 | |
| 367 | @code |
| 368 | template<class AsyncWriteStream> |
| 369 | typename std::enable_if<is_async_write_stream<AsyncWriteStream>::value>::type |
| 370 | f(AsyncWriteStream& stream); |
| 371 | @endcode |
| 372 | */ |
| 373 | #if BOOST_BEAST_DOXYGEN |
| 374 | template<class T> |
| 375 | using is_async_write_stream = __see_below__; |
| 376 | #else |
| 377 | template<class T, class = void> |
| 378 | struct is_async_write_stream : std::false_type {}; |
| 379 | |
| 380 | template<class T> |
| 381 | struct is_async_write_stream<T, boost::void_t<decltype( |
| 382 | std::declval<T&>().async_write_some( |
| 383 | std::declval<detail::ConstBufferSequence>(), |
| 384 | std::declval<detail::WriteHandler>()) |
| 385 | )>> : std::integral_constant<bool, |
| 386 | has_get_executor<T>::value |
| 387 | > {}; |
| 388 | #endif |
| 389 | |
| 390 | /** Determine if `T` meets the requirements of @b AsyncStream. |
| 391 | |
| 392 | Metafunctions are used to perform compile time checking of template |
| 393 | types. This type will be `std::true_type` if `T` meets the requirements, |
| 394 | else the type will be `std::false_type`. |
| 395 | |
| 396 | @par Example |
| 397 | |
| 398 | Use with `static_assert`: |
| 399 | |
| 400 | @code |
| 401 | template<class AsyncStream> |
| 402 | void f(AsyncStream& stream) |
| 403 | { |
| 404 | static_assert(is_async_stream<AsyncStream>::value, |
| 405 | "AsyncStream type requirements not met"); |
| 406 | ... |
| 407 | @endcode |
| 408 | |
| 409 | Use with `std::enable_if` (SFINAE): |
| 410 | |
| 411 | @code |
| 412 | template<class AsyncStream> |
| 413 | typename std::enable_if<is_async_stream<AsyncStream>::value>::type |
| 414 | f(AsyncStream& stream); |
| 415 | @endcode |
| 416 | */ |
| 417 | #if BOOST_BEAST_DOXYGEN |
| 418 | template<class T> |
| 419 | using is_async_stream = __see_below__; |
| 420 | #else |
| 421 | template<class T> |
| 422 | using is_async_stream = std::integral_constant<bool, |
| 423 | is_async_read_stream<T>::value && is_async_write_stream<T>::value>; |
| 424 | #endif |
| 425 | |
| 426 | //------------------------------------------------------------------------------ |
| 427 | |
| 428 | /** Default socket close function. |
| 429 | |
| 430 | This function is not meant to be called directly. Instead, it |
| 431 | is called automatically when using @ref close_socket. To enable |
| 432 | closure of user-defined types or classes derived from a particular |
| 433 | user-defined type, this function should be overloaded in the |
| 434 | corresponding namespace for the type in question. |
| 435 | |
| 436 | @see close_socket |
| 437 | */ |
| 438 | template< |
| 439 | class Protocol, |
| 440 | class Executor> |
| 441 | void |
| 442 | beast_close_socket( |
| 443 | net::basic_socket< |
| 444 | Protocol, Executor>& sock) |
| 445 | { |
| 446 | boost::system::error_code ec; |
| 447 | sock.close(ec); |
| 448 | } |
| 449 | |
| 450 | namespace detail { |
| 451 | |
| 452 | struct close_socket_impl |
| 453 | { |
| 454 | template<class T> |
| 455 | void |
| 456 | operator()(T& t) const |
| 457 | { |
| 458 | using beast::beast_close_socket; |
| 459 | beast_close_socket(t); |
| 460 | } |
| 461 | }; |
| 462 | |
| 463 | } // detail |
| 464 | |
| 465 | /** Close a socket or socket-like object. |
| 466 | |
| 467 | This function attempts to close an object representing a socket. |
| 468 | In this context, a socket is an object for which an unqualified |
| 469 | call to the function `void beast_close_socket(Socket&)` is |
| 470 | well-defined. The function `beast_close_socket` is a |
| 471 | <em>customization point</em>, allowing user-defined types to |
| 472 | provide an algorithm for performing the close operation by |
| 473 | overloading this function for the type in question. |
| 474 | |
| 475 | Since the customization point is a function call, the normal |
| 476 | rules for finding the correct overload are applied including |
| 477 | the rules for argument-dependent lookup ("ADL"). This permits |
| 478 | classes derived from a type for which a customization is provided |
| 479 | to inherit the customization point. |
| 480 | |
| 481 | An overload for the networking class template `net::basic_socket` |
| 482 | is provided, which implements the close algorithm for all socket-like |
| 483 | objects (hence the name of this customization point). When used |
| 484 | in conjunction with @ref get_lowest_layer, a generic algorithm |
| 485 | operating on a layered stream can perform a closure of the underlying |
| 486 | socket without knowing the exact list of concrete types. |
| 487 | |
| 488 | @par Example 1 |
| 489 | The following generic function synchronously sends a message |
| 490 | on the stream, then closes the socket. |
| 491 | @code |
| 492 | template <class WriteStream> |
| 493 | void hello_and_close (WriteStream& stream) |
| 494 | { |
| 495 | net::write(stream, net::const_buffer("Hello, world!", 13)); |
| 496 | close_socket(get_lowest_layer(stream)); |
| 497 | } |
| 498 | @endcode |
| 499 | |
| 500 | To enable closure of user defined types, it is necessary to provide |
| 501 | an overload of the function `beast_close_socket` for the type. |
| 502 | |
| 503 | @par Example 2 |
| 504 | The following code declares a user-defined type which contains a |
| 505 | private socket, and provides an overload of the customization |
| 506 | point which closes the private socket. |
| 507 | @code |
| 508 | class my_socket |
| 509 | { |
| 510 | net::ip::tcp::socket sock_; |
| 511 | |
| 512 | public: |
| 513 | my_socket(net::io_context& ioc) |
| 514 | : sock_(ioc) |
| 515 | { |
| 516 | } |
| 517 | |
| 518 | friend void beast_close_socket(my_socket& s) |
| 519 | { |
| 520 | error_code ec; |
| 521 | s.sock_.close(ec); |
| 522 | // ignore the error |
| 523 | } |
| 524 | }; |
| 525 | @endcode |
| 526 | |
| 527 | @param sock The socket to close. If the customization point is not |
| 528 | defined for the type of this object, or one of its base classes, |
| 529 | then a compiler error results. |
| 530 | |
| 531 | @see beast_close_socket |
| 532 | */ |
| 533 | #if BOOST_BEAST_DOXYGEN |
| 534 | template<class Socket> |
| 535 | void |
| 536 | close_socket(Socket& sock); |
| 537 | #else |
| 538 | BOOST_BEAST_INLINE_VARIABLE(close_socket, detail::close_socket_impl) |
| 539 | #endif |
| 540 | |
| 541 | } // beast |
| 542 | } // boost |
| 543 | |
| 544 | #endif |
| 545 | |