diff --git a/CHANGELOG.md b/CHANGELOG.md index dabaf57d..31e8f718 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ Version XXX: +* Websocket SSL `teardown` also tears down underlying TCP. * Update WebSocket examples to set TLS SNI. * Add handler tracking locations to websocket. * Add handler tracking locations to tcp teardown. @@ -10,6 +11,15 @@ Version XXX: * Add handler tracking locations to basic_stream. * Add handler tracking locations to http operations. +API Changes: + +* Previously, `teardown` and `async_teardown` of a websocket connection over SSL + would only shut down the SSL layer, leaving the TCP layer connected. Now the + SSL teardown will tear down both the SSL and TCP layers, cleanly shutting down + the TCP connection and closing the socket. + Should the client expect the TCP socket to remain connected, users will need to + re-engineer their code. + -------------------------------------------------------------------------------- Version 300: diff --git a/include/boost/beast/websocket/impl/ssl.hpp b/include/boost/beast/websocket/impl/ssl.hpp index f5530226..48f445f5 100644 --- a/include/boost/beast/websocket/impl/ssl.hpp +++ b/include/boost/beast/websocket/impl/ssl.hpp @@ -11,6 +11,9 @@ #define BOOST_BEAST_WEBSOCKET_IMPL_SSL_HPP #include +#include +#include +#include namespace boost { namespace beast { @@ -33,24 +36,71 @@ namespace beast { template void teardown( - role_type, - net::ssl::stream& stream, + role_type role, + boost::asio::ssl::stream& stream, error_code& ec) { stream.shutdown(ec); + using boost::beast::websocket::teardown; + error_code ec2; + teardown(role, stream.next_layer(), ec ? ec2 : ec); } +namespace detail { + +template +struct ssl_shutdown_op + : boost::asio::coroutine +{ + ssl_shutdown_op( + boost::asio::ssl::stream& s, + role_type role) + : s_(s) + , role_(role) + { + } + + template + void + operator()(Self& self, error_code ec = {}, std::size_t bytes_transferred = 0) + { + BOOST_ASIO_CORO_REENTER(*this) + { + BOOST_ASIO_CORO_YIELD + s_.async_shutdown(std::move(self)); + ec_ = ec; + + using boost::beast::websocket::async_teardown; + BOOST_ASIO_CORO_YIELD + async_teardown(role_, s_.next_layer(), std::move(self)); + if (!ec_) + ec_ = ec; + + self.complete(ec_); + } + } + +private: + boost::asio::ssl::stream& s_; + role_type role_; + error_code ec_; +}; + +} // detail + template< class AsyncStream, class TeardownHandler> void async_teardown( - role_type, - net::ssl::stream& stream, + role_type role, + boost::asio::ssl::stream& stream, TeardownHandler&& handler) { - stream.async_shutdown( - std::forward(handler)); + return boost::asio::async_compose( + detail::ssl_shutdown_op(stream, role), + handler, + stream); } } // beast