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
<beast/websocket/ssl.hpp> 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.
This commit is contained in:
Vinnie Falco
2016-06-08 11:18:22 -04:00
parent a6cb4fdfb2
commit 19f15046b0
7 changed files with 131 additions and 76 deletions

View File

@@ -85,6 +85,7 @@
<member><link linkend="beast.ref.websocket__ping_data">ping_data</link></member> <member><link linkend="beast.ref.websocket__ping_data">ping_data</link></member>
<member><link linkend="beast.ref.websocket__stream">stream</link></member> <member><link linkend="beast.ref.websocket__stream">stream</link></member>
<member><link linkend="beast.ref.websocket__reason_string">reason_string</link></member> <member><link linkend="beast.ref.websocket__reason_string">reason_string</link></member>
<member><link linkend="beast.ref.websocket__teardown_tag">teardown_tag</link></member>
</simplelist> </simplelist>
<bridgehead renderas="sect3">Options</bridgehead> <bridgehead renderas="sect3">Options</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">

View File

@@ -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 The interface to Beast's WebSocket implementation is a single template
class [link beast.ref.websocket__stream `beast::websocket::stream`] which 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 operations are performed, or
[link beast.types.streams.AsyncStream [*`AsyncStream`]] if asynchronous [link beast.types.streams.AsyncStream [*`AsyncStream`]] if asynchronous
operations are performed, or both. Arguments supplied during construction are operations are performed, or both. Arguments supplied during construction are
passed to next layer's constructor. Here we declare two websockets which have passed to next layer's constructor. Here we declare a websocket stream over
ownership of the next layer: a TCP/IP socket with ownership of the socket:
``` ```
boost::asio::io_service ios; boost::asio::io_service ios;
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios); beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
beast::websocket::stream<
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> 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 <beast/websocket/ssl.hpp>
#include <beast/websocket.hpp>
#include <boost/asio/ssl.hpp>
boost::asio::io_service ios;
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ws(ios, ctx);
```
[note
When creating websocket stream objects using SSL, it is necessary
to include the file `<beast/websocket/ssl.hpp>`.
]
[heading Non-owning references]
For servers that can handshake in multiple protocols, it may be desired 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: to wrap an object that already exists. This socket can be moved in:
``` ```
boost::asio::ip::tcp::socket&& sock; boost::asio::ip::tcp::socket&& sock;
... ...
beast::websocket::stream< beast::websocket::stream<boost::asio::ip::tcp::socket> ws(std::move(sock));
boost::asio::ip::tcp::socket> ws(std::move(sock));
``` ```
Or, the wrapper can be constructed with a non-owning reference. In 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. permitting callers to interact directly with its interface.
``` ```
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
beast::websocket::stream< beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws(ios, ctx);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws(ios, ctx);
... ...
ws.next_layer().shutdown(); // ssl::stream shutdown ws.next_layer().shutdown(); // ssl::stream shutdown
``` ```

View File

@@ -161,18 +161,20 @@ public:
friend friend
void void
teardown(fail_stream<NextLayer>& stream, teardown(websocket::teardown_tag,
fail_stream<NextLayer>& stream,
boost::system::error_code& ec) boost::system::error_code& ec)
{ {
if(stream.pfc_->fail(ec)) if(stream.pfc_->fail(ec))
return; return;
websocket_helpers::call_teardown(stream.next_layer(), ec); beast::websocket_helpers::call_teardown(stream.next_layer(), ec);
} }
template<class TeardownHandler> template<class TeardownHandler>
friend friend
void void
async_teardown(fail_stream<NextLayer>& stream, async_teardown(websocket::teardown_tag,
fail_stream<NextLayer>& stream,
TeardownHandler&& handler) TeardownHandler&& handler)
{ {
error_code ec; error_code ec;
@@ -182,7 +184,7 @@ public:
bind_handler(std::move(handler), ec)); bind_handler(std::move(handler), ec));
return; return;
} }
websocket_helpers::call_async_teardown( beast::websocket_helpers::call_async_teardown(
stream.next_layer(), std::forward<TeardownHandler>(handler)); stream.next_layer(), std::forward<TeardownHandler>(handler));
} }
}; };

View File

@@ -129,7 +129,7 @@ operator()(error_code ec, bool again)
template<class AsyncStream> template<class AsyncStream>
void void
teardown( teardown(teardown_tag,
boost::asio::ssl::stream<AsyncStream>& stream, boost::asio::ssl::stream<AsyncStream>& stream,
error_code& ec) error_code& ec)
{ {
@@ -138,7 +138,7 @@ teardown(
template<class AsyncStream, class TeardownHandler> template<class AsyncStream, class TeardownHandler>
void void
async_teardown( async_teardown(teardown_tag,
boost::asio::ssl::stream<AsyncStream>& stream, boost::asio::ssl::stream<AsyncStream>& stream,
TeardownHandler&& handler) TeardownHandler&& handler)
{ {

View File

@@ -128,7 +128,7 @@ operator()(error_code ec, std::size_t, bool again)
inline inline
void void
teardown( teardown(teardown_tag,
boost::asio::ip::tcp::socket& socket, boost::asio::ip::tcp::socket& socket,
error_code& ec) error_code& ec)
{ {
@@ -151,7 +151,7 @@ teardown(
template<class TeardownHandler> template<class TeardownHandler>
inline inline
void void
async_teardown( async_teardown(teardown_tag,
boost::asio::ip::tcp::socket& socket, boost::asio::ip::tcp::socket& socket,
TeardownHandler&& handler) TeardownHandler&& handler)
{ {

View File

@@ -31,7 +31,7 @@ namespace websocket {
*/ */
template<class SyncStream> template<class SyncStream>
void void
teardown( teardown(teardown_tag,
boost::asio::ssl::stream<SyncStream>& stream, boost::asio::ssl::stream<SyncStream>& stream,
error_code& ec); error_code& ec);
@@ -62,7 +62,7 @@ teardown(
template<class AsyncStream, class TeardownHandler> template<class AsyncStream, class TeardownHandler>
inline inline
void void
async_teardown( async_teardown(teardown_tag,
boost::asio::ssl::stream<AsyncStream>& stream, boost::asio::ssl::stream<AsyncStream>& stream,
TeardownHandler&& handler); TeardownHandler&& handler);

View File

@@ -13,8 +13,17 @@
#include <type_traits> #include <type_traits>
namespace beast { namespace beast {
namespace websocket { 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. /** Tear down a connection.
This tears down a connection. The implementation will call This tears down a connection. The implementation will call
@@ -30,7 +39,19 @@ namespace websocket {
*/ */
template<class Socket> template<class Socket>
void 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 <beast/websocket/ssl.hpp>.
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. /** Start tearing down a connection.
@@ -49,7 +70,8 @@ teardown(Socket& socket, error_code& ec) = delete;
function signature of the handler must be: function signature of the handler must be:
@code void handler( @code void handler(
error_code const& error // result of operation error_code const& error // result of operation
); @endcode );
@endcode
Regardless of whether the asynchronous operation completes Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a this function. Invocation of the handler will be performed in a
@@ -58,57 +80,19 @@ teardown(Socket& socket, error_code& ec) = delete;
*/ */
template<class Socket, class TeardownHandler> template<class Socket, class TeardownHandler>
void 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 <beast/websocket/ssl.hpp>.
//------------------------------------------------------------------------------ If you are creating an instance of beast::websocket::stream with your
own user defined type, you must provide an overload of teardown with
/** Tear down a `boost::asio::ip::tcp::socket`. the corresponding signature (including the teardown_tag).
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 static_assert(sizeof(Socket)==-1,
teardown( "Unknown Socket type in async_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<class TeardownHandler>
void
async_teardown(
boost::asio::ip::tcp::socket& socket,
TeardownHandler&& handler);
} // websocket } // websocket
@@ -127,7 +111,7 @@ void
call_teardown(Socket& socket, error_code& ec) call_teardown(Socket& socket, error_code& ec)
{ {
using websocket::teardown; using websocket::teardown;
teardown(socket, ec); teardown(websocket::teardown_tag{}, socket, ec);
} }
template<class Socket, class TeardownHandler> template<class Socket, class TeardownHandler>
@@ -136,12 +120,65 @@ void
call_async_teardown(Socket& socket, TeardownHandler&& handler) call_async_teardown(Socket& socket, TeardownHandler&& handler)
{ {
using websocket::async_teardown; using websocket::async_teardown;
async_teardown(socket, async_teardown(websocket::teardown_tag{}, socket,
std::forward<TeardownHandler>(handler)); std::forward<TeardownHandler>(handler));
} }
} // websocket_helpers } // 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<class TeardownHandler>
void
async_teardown(teardown_tag,
boost::asio::ip::tcp::socket& socket, TeardownHandler&& handler);
} // websocket
} // beast } // beast
#include <beast/websocket/impl/teardown.ipp> #include <beast/websocket/impl/teardown.ipp>