From 19f15046b0b1ffcca5470c93aaeb65df1300c613 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Wed, 8 Jun 2016 11:18:22 -0400 Subject: [PATCH] Fixes and documentation for teardown and use with SSL: This solves a problem where clang and gcc locate the deleted version of teardown and async_teardown instead of the overloaded version. It requires overloads to add `teardown_tag` into the signature so that the rules for argument dependent lookup can find the right function. Improve documentation of teardown requirements The documentation is updated to clearly explain the need for including to use SSL streams with WebSocket. The default implementations of teardown and async_teardown now use static_assert to alert the user of improper usage, with comments providing guidance for resolving the error. --- doc/quickref.xml | 1 + doc/websocket.qbk | 37 ++++-- extras/beast/test/fail_stream.hpp | 14 ++- include/beast/websocket/impl/ssl.ipp | 4 +- include/beast/websocket/impl/teardown.ipp | 4 +- include/beast/websocket/ssl.hpp | 4 +- include/beast/websocket/teardown.hpp | 143 ++++++++++++++-------- 7 files changed, 131 insertions(+), 76 deletions(-) diff --git a/doc/quickref.xml b/doc/quickref.xml index d025b356..7662a357 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -85,6 +85,7 @@ ping_data stream reason_string + teardown_tag Options diff --git a/doc/websocket.qbk b/doc/websocket.qbk index da2808cc..ff9c235e 100644 --- a/doc/websocket.qbk +++ b/doc/websocket.qbk @@ -66,7 +66,7 @@ both Boost.Asio and the WebSocket protocol specification described in -[section:creating Creating the socket] +[section:creation Creation] The interface to Beast's WebSocket implementation is a single template class [link beast.ref.websocket__stream `beast::websocket::stream`] which @@ -75,24 +75,40 @@ of [link beast.types.streams.SyncStream [*`SyncReadStream`]] if synchronous operations are performed, or [link beast.types.streams.AsyncStream [*`AsyncStream`]] if asynchronous operations are performed, or both. Arguments supplied during construction are -passed to next layer's constructor. Here we declare two websockets which have -ownership of the next layer: +passed to next layer's constructor. Here we declare a websocket stream over +a TCP/IP socket with ownership of the socket: ``` boost::asio::io_service ios; beast::websocket::stream ws(ios); - -boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); -beast::websocket::stream< - boost::asio::ssl::stream> wss(ios, ctx); ``` +[heading Using SSL] + +To use WebSockets over SSL, choose an SSL stream for the next layer template +argument when constructing the stream. +``` +#include +#include +#include + +boost::asio::io_service ios; +boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); +beast::websocket::stream ws(ios, ctx); +``` + +[note + When creating websocket stream objects using SSL, it is necessary + to include the file ``. +] + +[heading Non-owning references] + For servers that can handshake in multiple protocols, it may be desired to wrap an object that already exists. This socket can be moved in: ``` boost::asio::ip::tcp::socket&& sock; ... - beast::websocket::stream< - boost::asio::ip::tcp::socket> ws(std::move(sock)); + beast::websocket::stream ws(std::move(sock)); ``` Or, the wrapper can be constructed with a non-owning reference. In @@ -108,8 +124,7 @@ The layer being wrapped can be accessed through the websocket's "next layer", permitting callers to interact directly with its interface. ``` boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); - beast::websocket::stream< - boost::asio::ssl::stream> ws(ios, ctx); + beast::websocket::stream> ws(ios, ctx); ... ws.next_layer().shutdown(); // ssl::stream shutdown ``` diff --git a/extras/beast/test/fail_stream.hpp b/extras/beast/test/fail_stream.hpp index 1c3a34e2..47742d06 100644 --- a/extras/beast/test/fail_stream.hpp +++ b/extras/beast/test/fail_stream.hpp @@ -161,19 +161,21 @@ public: friend void - teardown(fail_stream& stream, - boost::system::error_code& ec) + teardown(websocket::teardown_tag, + fail_stream& stream, + boost::system::error_code& ec) { if(stream.pfc_->fail(ec)) return; - websocket_helpers::call_teardown(stream.next_layer(), ec); + beast::websocket_helpers::call_teardown(stream.next_layer(), ec); } template friend void - async_teardown(fail_stream& stream, - TeardownHandler&& handler) + async_teardown(websocket::teardown_tag, + fail_stream& stream, + TeardownHandler&& handler) { error_code ec; if(stream.pfc_->fail(ec)) @@ -182,7 +184,7 @@ public: bind_handler(std::move(handler), ec)); return; } - websocket_helpers::call_async_teardown( + beast::websocket_helpers::call_async_teardown( stream.next_layer(), std::forward(handler)); } }; diff --git a/include/beast/websocket/impl/ssl.ipp b/include/beast/websocket/impl/ssl.ipp index 681ecd34..a7cd3259 100644 --- a/include/beast/websocket/impl/ssl.ipp +++ b/include/beast/websocket/impl/ssl.ipp @@ -129,7 +129,7 @@ operator()(error_code ec, bool again) template void -teardown( +teardown(teardown_tag, boost::asio::ssl::stream& stream, error_code& ec) { @@ -138,7 +138,7 @@ teardown( template void -async_teardown( +async_teardown(teardown_tag, boost::asio::ssl::stream& stream, TeardownHandler&& handler) { diff --git a/include/beast/websocket/impl/teardown.ipp b/include/beast/websocket/impl/teardown.ipp index fa7133b2..1d0389b0 100644 --- a/include/beast/websocket/impl/teardown.ipp +++ b/include/beast/websocket/impl/teardown.ipp @@ -128,7 +128,7 @@ operator()(error_code ec, std::size_t, bool again) inline void -teardown( +teardown(teardown_tag, boost::asio::ip::tcp::socket& socket, error_code& ec) { @@ -151,7 +151,7 @@ teardown( template inline void -async_teardown( +async_teardown(teardown_tag, boost::asio::ip::tcp::socket& socket, TeardownHandler&& handler) { diff --git a/include/beast/websocket/ssl.hpp b/include/beast/websocket/ssl.hpp index 5ed57222..ef94ed9a 100644 --- a/include/beast/websocket/ssl.hpp +++ b/include/beast/websocket/ssl.hpp @@ -31,7 +31,7 @@ namespace websocket { */ template void -teardown( +teardown(teardown_tag, boost::asio::ssl::stream& stream, error_code& ec); @@ -62,7 +62,7 @@ teardown( template inline void -async_teardown( +async_teardown(teardown_tag, boost::asio::ssl::stream& stream, TeardownHandler&& handler); diff --git a/include/beast/websocket/teardown.hpp b/include/beast/websocket/teardown.hpp index ecb6dba3..95a54f5e 100644 --- a/include/beast/websocket/teardown.hpp +++ b/include/beast/websocket/teardown.hpp @@ -13,8 +13,17 @@ #include namespace beast { + namespace websocket { +/** Tag type used to find teardown and async_teardown overloads + + Overloads of @ref teardown and @async_teardown for user defined + types must take a value of type @ref teardown_tag in the first + argument in order to be found by the implementation. +*/ +struct teardown_tag {}; + /** Tear down a connection. This tears down a connection. The implementation will call @@ -30,7 +39,19 @@ namespace websocket { */ template void -teardown(Socket& socket, error_code& ec) = delete; +teardown(teardown_tag, Socket& socket, error_code& ec) +{ +/* + If you are trying to use OpenSSL and this goes off, you need to + add an include for . + + If you are creating an instance of beast::websocket::stream with your + own user defined type, you must provide an overload of teardown with + the corresponding signature (including the teardown_tag). +*/ + static_assert(sizeof(Socket)==-1, + "Unknown Socket type in teardown."); +}; /** Start tearing down a connection. @@ -49,7 +70,8 @@ teardown(Socket& socket, error_code& ec) = delete; function signature of the handler must be: @code void handler( error_code const& error // result of operation - ); @endcode + ); + @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 @@ -58,57 +80,19 @@ teardown(Socket& socket, error_code& ec) = delete; */ template void -async_teardown(Socket& socket, TeardownHandler&& handler) = delete; +async_teardown(teardown_tag, Socket& socket, TeardownHandler&& handler) +{ +/* + If you are trying to use OpenSSL and this goes off, you need to + add an include for . -//------------------------------------------------------------------------------ - -/** Tear down a `boost::asio::ip::tcp::socket`. - - This tears down a connection. The implementation will call - the overload of this function based on the `Stream` parameter - used to consruct the socket. When `Stream` is a user defined - type, and not a `boost::asio::ip::tcp::socket` or any - `boost::asio::ssl::stream`, callers are responsible for - providing a suitable overload of this function. - - @param socket The socket to tear down. - - @param ec Set to the error if any occurred. + If you are creating an instance of beast::websocket::stream with your + own user defined type, you must provide an overload of teardown with + the corresponding signature (including the teardown_tag). */ -void -teardown( - boost::asio::ip::tcp::socket& socket, - error_code& ec); - -/** Start tearing down a `boost::asio::ip::tcp::socket`. - - This begins tearing down a connection asynchronously. - The implementation will call the overload of this function - based on the `Stream` parameter used to consruct the socket. - When `Stream` is a user defined type, and not a - `boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`, - callers are responsible for providing a suitable overload - of this function. - - @param socket The socket to tear down. - - @param handler The handler to be called when the request completes. - Copies will be made of the handler as required. The equivalent - function signature of the handler must be: - @code void handler( - error_code const& error // result of operation - ); @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_service::post(). - -*/ -template -void -async_teardown( - boost::asio::ip::tcp::socket& socket, - TeardownHandler&& handler); + static_assert(sizeof(Socket)==-1, + "Unknown Socket type in async_teardown."); +} } // websocket @@ -127,7 +111,7 @@ void call_teardown(Socket& socket, error_code& ec) { using websocket::teardown; - teardown(socket, ec); + teardown(websocket::teardown_tag{}, socket, ec); } template @@ -136,12 +120,65 @@ void call_async_teardown(Socket& socket, TeardownHandler&& handler) { using websocket::async_teardown; - async_teardown(socket, + async_teardown(websocket::teardown_tag{}, socket, std::forward(handler)); } } // websocket_helpers +//------------------------------------------------------------------------------ + +namespace websocket { + +/** Tear down a `boost::asio::ip::tcp::socket`. + + This tears down a connection. The implementation will call + the overload of this function based on the `Stream` parameter + used to consruct the socket. When `Stream` is a user defined + type, and not a `boost::asio::ip::tcp::socket` or any + `boost::asio::ssl::stream`, callers are responsible for + providing a suitable overload of this function. + + @param socket The socket to tear down. + + @param ec Set to the error if any occurred. +*/ +void +teardown(teardown_tag, + boost::asio::ip::tcp::socket& socket, error_code& ec); + +/** Start tearing down a `boost::asio::ip::tcp::socket`. + + This begins tearing down a connection asynchronously. + The implementation will call the overload of this function + based on the `Stream` parameter used to consruct the socket. + When `Stream` is a user defined type, and not a + `boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`, + callers are responsible for providing a suitable overload + of this function. + + @param socket The socket to tear down. + + @param handler The handler to be called when the request completes. + Copies will be made of the handler as required. The equivalent + function signature of the handler must be: + @code void handler( + error_code const& error // result of operation + ); + @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_service::post(). + +*/ +template +void +async_teardown(teardown_tag, + boost::asio::ip::tcp::socket& socket, TeardownHandler&& handler); + +} // websocket + } // beast #include