Skip to content

Instantly share code, notes, and snippets.

@HugoGuiroux
Last active June 29, 2020 16:32
Show Gist options
  • Select an option

  • Save HugoGuiroux/556a8564b40baa4d360d2f55d63302a6 to your computer and use it in GitHub Desktop.

Select an option

Save HugoGuiroux/556a8564b40baa4d360d2f55d63302a6 to your computer and use it in GitHub Desktop.
Boost stream parser
/*
* An example of stream wrapper around an ssl stream supporting both HTTP and WebSocket requests.
*
* The current version is mainly a copy/paste of the existing boost::asio::ssl stream.
* The filtering logic is inside the mutate method, which takes an iterator on the MutableBufferSequence (it can be greatly improved obviously).
* Currently, only asynchronous reads are mutated, but using mutable for synchronous reads should be easy to do.
*
* Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
* Copyright (c) 2005-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
* Copyright (c) 2018 Hugo Guiroux
* 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)
*/
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/core/buffers_adapter.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ssl.hpp>
#include <type_traits>
#include <boost/beast/core/buffers_to_string.hpp>
#include <iostream>
#include <boost/beast/websocket/role.hpp>
#include <boost/beast/websocket/teardown.hpp>
#include <boost/asio.hpp>
/// Provides stream-oriented functionality using SSL.
/**
* The stream class template provides asynchronous and blocking stream-oriented
* functionality using SSL.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe. The application must also ensure that all
* asynchronous operations are performed within the same implicit or explicit
* strand.
*
* @par Example
* To use the SSL stream template with an ip::tcp::socket, you would write:
* @code
* boost::asio::io_context io_context;
* boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
* boost::asio::ssl::stream<asio:ip::tcp::socket> sock(io_context, ctx);
* @endcode
*
* @par Concepts:
* AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream.
*/
template <typename NextStream>
class FilterStream
{
template<class, class> class read_op;
class automata;
public:
/// The native handle type of the SSL stream.
using native_handle_type = typename NextStream::native_handle_type;
/// The context type
using context = boost::asio::ssl::context;
/// The type of the next layer.
using next_layer_type = typename std::remove_reference<NextStream>::type;
/// The type of the lowest layer.
using lowest_layer_type = boost::beast::get_lowest_layer<next_layer_type>;
/// The type of the executor associated with the object.
using executor_type = typename next_layer_type::executor_type;
FilterStream(FilterStream&&) = default;
FilterStream(FilterStream const&) = default;
FilterStream& operator=(FilterStream&&) = default;
FilterStream& operator=(FilterStream const&) = default;
/** Constructor
Arguments, if any, are forwarded to the next layer's constructor.
*/
template<class... Args>
explicit
FilterStream(Args&&... args) : next_layer_(std::forward<Args>(args)...) {}
/** Destructor
The treatment of pending operations will be the same as that
of the next layer.
*/
~FilterStream() = default;
/// Get the executor associated with the object.
/**
* This function may be used to obtain the executor object that the stream
* uses to dispatch handlers for asynchronous operations.
*
* @return A copy of the executor that stream will use to dispatch handlers.
*/
executor_type get_executor() noexcept
{
return next_layer_.get_executor();
}
#if !defined(BOOST_ASIO_NO_DEPRECATED)
/// (Deprecated: Use get_executor().) Get the io_context associated with the
/// object.
boost::asio::io_context& get_io_context()
{
return next_layer_.get_io_context();
}
/// (Deprecated: Use get_executor().) Get the io_context associated with the
/// object.
boost::asio::io_context& get_io_service()
{
return next_layer_.get_io_service();
}
#endif // !defined(BOOST_ASIO_NO_DEPRECATED)
/// Get the underlying implementation in the native type.
/**
* This function may be used to obtain the underlying implementation of the
* context. This is intended to allow access to context functionality that is
* not otherwise provided.
*
* @par Example
* The native_handle() function returns a pointer of type @c SSL* that is
* suitable for passing to functions such as @c SSL_get_verify_result and
* @c SSL_get_peer_certificate:
* @code
* boost::asio::ssl::stream<asio:ip::tcp::socket> sock(io_context, ctx);
*
* // ... establish connection and perform handshake ...
*
* if (X509* cert = SSL_get_peer_certificate(sock.native_handle()))
* {
* if (SSL_get_verify_result(sock.native_handle()) == X509_V_OK)
* {
* // ...
* }
* }
* @endcode
*/
native_handle_type native_handle()
{
return next_layer_.native_handle();
}
/// Get a reference to the next layer.
/**
* This function returns a reference to the next layer in a stack of stream
* layers.
*
* @return A reference to the next layer in the stack of stream layers.
* Ownership is not transferred to the caller.
*/
const next_layer_type& next_layer() const
{
return next_layer_;
}
/// Get a reference to the next layer.
/**
* This function returns a reference to the next layer in a stack of stream
* layers.
*
* @return A reference to the next layer in the stack of stream layers.
* Ownership is not transferred to the caller.
*/
next_layer_type& next_layer()
{
return next_layer_;
}
/// Get a reference to the lowest layer.
/**
* This function returns a reference to the lowest layer in a stack of
* stream layers.
*
* @return A reference to the lowest layer in the stack of stream layers.
* Ownership is not transferred to the caller.
*/
lowest_layer_type& lowest_layer()
{
return next_layer_.lowest_layer();
}
/// Get a reference to the lowest layer.
/**
* This function returns a reference to the lowest layer in a stack of
* stream layers.
*
* @return A reference to the lowest layer in the stack of stream layers.
* Ownership is not transferred to the caller.
*/
const lowest_layer_type& lowest_layer() const
{
return next_layer_.lowest_layer();
}
/// Set the peer verification mode.
/**
* This function may be used to configure the peer verification mode used by
* the stream. The new mode will override the mode inherited from the context.
*
* @param v A bitmask of peer verification modes. See @ref verify_mode for
* available values.
*
* @throws boost::system::system_error Thrown on failure.
*
* @note Calls @c SSL_set_verify.
*/
void set_verify_mode(boost::asio::ssl::verify_mode v)
{
next_layer_.set_verify_mode(v);
}
/// Set the peer verification mode.
/**
* This function may be used to configure the peer verification mode used by
* the stream. The new mode will override the mode inherited from the context.
*
* @param v A bitmask of peer verification modes. See @ref verify_mode for
* available values.
*
* @param ec Set to indicate what error occurred, if any.
*
* @note Calls @c SSL_set_verify.
*/
BOOST_ASIO_SYNC_OP_VOID set_verify_mode(
boost::asio::ssl::verify_mode v, boost::system::error_code& ec)
{
next_layer_.set_verify_mode(v, ec);
BOOST_ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Set the peer verification depth.
/**
* This function may be used to configure the maximum verification depth
* allowed by the stream.
*
* @param depth Maximum depth for the certificate chain verification that
* shall be allowed.
*
* @throws boost::system::system_error Thrown on failure.
*
* @note Calls @c SSL_set_verify_depth.
*/
void set_verify_depth(int depth)
{
next_layer_.set_verify_depth(depth);
}
/// Set the peer verification depth.
/**
* This function may be used to configure the maximum verification depth
* allowed by the stream.
*
* @param depth Maximum depth for the certificate chain verification that
* shall be allowed.
*
* @param ec Set to indicate what error occurred, if any.
*
* @note Calls @c SSL_set_verify_depth.
*/
BOOST_ASIO_SYNC_OP_VOID set_verify_depth(
int depth, boost::system::error_code& ec)
{
next_layer_.set_verify_depth(depth, ec);
BOOST_ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Set the callback used to verify peer certificates.
/**
* This function is used to specify a callback function that will be called
* by the implementation when it needs to verify a peer certificate.
*
* @param callback The function object to be used for verifying a certificate.
* The function signature of the handler must be:
* @code bool verify_callback(
* bool preverified, // True if the certificate passed pre-verification.
* verify_context& ctx // The peer certificate and other context.
* ); @endcode
* The return value of the callback is true if the certificate has passed
* verification, false otherwise.
*
* @throws boost::system::system_error Thrown on failure.
*
* @note Calls @c SSL_set_verify.
*/
template <typename VerifyCallback>
void set_verify_callback(VerifyCallback callback)
{
next_layer_.set_verify_callback(callback);
}
/// Set the callback used to verify peer certificates.
/**
* This function is used to specify a callback function that will be called
* by the implementation when it needs to verify a peer certificate.
*
* @param callback The function object to be used for verifying a certificate.
* The function signature of the handler must be:
* @code bool verify_callback(
* bool preverified, // True if the certificate passed pre-verification.
* verify_context& ctx // The peer certificate and other context.
* ); @endcode
* The return value of the callback is true if the certificate has passed
* verification, false otherwise.
*
* @param ec Set to indicate what error occurred, if any.
*
* @note Calls @c SSL_set_verify.
*/
template <typename VerifyCallback>
BOOST_ASIO_SYNC_OP_VOID set_verify_callback(VerifyCallback callback,
boost::system::error_code& ec)
{
next_layer_.set_verify_callback(callback, ec);
BOOST_ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Perform SSL handshaking.
/**
* This function is used to perform SSL handshaking on the stream. The
* function call will block until handshaking is complete or an error occurs.
*
* @param type The type of handshaking to be performed, i.e. as a client or as
* a server.
*
* @throws boost::system::system_error Thrown on failure.
*/
void handshake(boost::asio::ssl::stream_base::handshake_type type)
{
next_layer_.handshake(type);
}
/// Perform SSL handshaking.
/**
* This function is used to perform SSL handshaking on the stream. The
* function call will block until handshaking is complete or an error occurs.
*
* @param type The type of handshaking to be performed, i.e. as a client or as
* a server.
*
* @param ec Set to indicate what error occurred, if any.
*/
BOOST_ASIO_SYNC_OP_VOID handshake(boost::asio::ssl::stream_base::handshake_type type,
boost::system::error_code& ec)
{
next_layer_.handshake(type, ec);
BOOST_ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Perform SSL handshaking.
/**
* This function is used to perform SSL handshaking on the stream. The
* function call will block until handshaking is complete or an error occurs.
*
* @param type The type of handshaking to be performed, i.e. as a client or as
* a server.
*
* @param buffers The buffered data to be reused for the handshake.
*
* @throws boost::system::system_error Thrown on failure.
*/
template <typename ConstBufferSequence>
void handshake(boost::asio::ssl::stream_base::handshake_type type, const ConstBufferSequence& buffers)
{
next_layer_.handshake(type, buffers);
}
/// Perform SSL handshaking.
/**
* This function is used to perform SSL handshaking on the stream. The
* function call will block until handshaking is complete or an error occurs.
*
* @param type The type of handshaking to be performed, i.e. as a client or as
* a server.
*
* @param buffers The buffered data to be reused for the handshake.
*
* @param ec Set to indicate what error occurred, if any.
*/
template <typename ConstBufferSequence>
BOOST_ASIO_SYNC_OP_VOID handshake(boost::asio::ssl::stream_base::handshake_type type,
const ConstBufferSequence& buffers, boost::system::error_code& ec)
{
next_layer_.handshake(type, buffers, ec);
BOOST_ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Start an asynchronous SSL handshake.
/**
* This function is used to asynchronously perform an SSL handshake on the
* stream. This function call always returns immediately.
*
* @param type The type of handshaking to be performed, i.e. as a client or as
* a server.
*
* @param handler The handler to be called when the handshake operation
* completes. Copies will be made of the handler as required. The equivalent
* function signature of the handler must be:
* @code void handler(
* const boost::system::error_code& error // Result of operation.
* ); @endcode
*/
template <typename HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(HandshakeHandler,
void (boost::system::error_code))
async_handshake(boost::asio::ssl::stream_base::handshake_type type,
BOOST_ASIO_MOVE_ARG(HandshakeHandler) handler)
{
return next_layer_.async_handshake(type, handler);
}
/// Start an asynchronous SSL handshake.
/**
* This function is used to asynchronously perform an SSL handshake on the
* stream. This function call always returns immediately.
*
* @param type The type of handshaking to be performed, i.e. as a client or as
* a server.
*
* @param buffers The buffered data to be reused for the handshake. Although
* the buffers object may be copied as necessary, ownership of the underlying
* buffers is retained by the caller, which must guarantee that they remain
* valid until the handler is called.
*
* @param handler The handler to be called when the handshake operation
* completes. Copies will be made of the handler as required. The equivalent
* function signature of the handler must be:
* @code void handler(
* const boost::system::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Amount of buffers used in handshake.
* ); @endcode
*/
template <typename ConstBufferSequence, typename BufferedHandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler,
void (boost::system::error_code, std::size_t))
async_handshake(boost::asio::ssl::stream_base::handshake_type type, const ConstBufferSequence& buffers,
BOOST_ASIO_MOVE_ARG(BufferedHandshakeHandler) handler)
{
return next_layer_.async_handshake(type, buffers, handler);
}
/// Shut down SSL on the stream.
/**
* This function is used to shut down SSL on the stream. The function call
* will block until SSL has been shut down or an error occurs.
*
* @throws boost::system::system_error Thrown on failure.
*/
void shutdown()
{
next_layer_.shutdown();
}
/// Shut down SSL on the stream.
/**
* This function is used to shut down SSL on the stream. The function call
* will block until SSL has been shut down or an error occurs.
*
* @param ec Set to indicate what error occurred, if any.
*/
BOOST_ASIO_SYNC_OP_VOID shutdown(boost::system::error_code& ec)
{
next_layer_.shutdown(ec);
BOOST_ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Asynchronously shut down SSL on the stream.
/**
* This function is used to asynchronously shut down SSL on the stream. This
* function call always returns immediately.
*
* @param handler The handler to be called when the handshake operation
* completes. Copies will be made of the handler as required. The equivalent
* function signature of the handler must be:
* @code void handler(
* const boost::system::error_code& error // Result of operation.
* ); @endcode
*/
template <typename ShutdownHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ShutdownHandler,
void (boost::system::error_code))
async_shutdown(BOOST_ASIO_MOVE_ARG(ShutdownHandler) handler)
{
return next_layer_.async_shutdown(handler);
}
/// Write some data to the stream.
/**
* This function is used to write data on the stream. The function call will
* block until one or more bytes of data has been written successfully, or
* until an error occurs.
*
* @param buffers The data to be written.
*
* @returns The number of bytes written.
*
* @throws boost::system::system_error Thrown on failure.
*
* @note The write_some operation may not transmit all of the data to the
* peer. Consider using the @ref write function if you need to ensure that all
* data is written before the blocking operation completes.
*/
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers)
{
return next_layer_.write_some(buffers);
}
/// Write some data to the stream.
/**
* This function is used to write data on the stream. The function call will
* block until one or more bytes of data has been written successfully, or
* until an error occurs.
*
* @param buffers The data to be written to the stream.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes written. Returns 0 if an error occurred.
*
* @note The write_some operation may not transmit all of the data to the
* peer. Consider using the @ref write function if you need to ensure that all
* data is written before the blocking operation completes.
*/
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers,
boost::system::error_code& ec)
{
return next_layer_.write_some(buffers, ec);
}
/// Start an asynchronous write.
/**
* This function is used to asynchronously write one or more bytes of data to
* the stream. The function call always returns immediately.
*
* @param buffers The data to be written to the stream. Although the buffers
* object may be copied as necessary, ownership of the underlying buffers is
* retained by the caller, which must guarantee that they remain valid until
* the handler is called.
*
* @param handler The handler to be called when the write operation completes.
* Copies will be made of the handler as required. The equivalent function
* signature of the handler must be:
* @code void handler(
* const boost::system::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes written.
* ); @endcode
*
* @note The async_write_some operation may not transmit all of the data to
* the peer. Consider using the @ref async_write function if you need to
* ensure that all data is written before the blocking operation completes.
*/
template <typename ConstBufferSequence, typename WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
void (boost::system::error_code, std::size_t))
async_write_some(const ConstBufferSequence& buffers,
BOOST_ASIO_MOVE_ARG(WriteHandler) handler)
{
return next_layer_.async_write_some(buffers, std::forward<WriteHandler>(handler));
}
/// Read some data from the stream.
/**
* This function is used to read data from the stream. The function call will
* block until one or more bytes of data has been read successfully, or until
* an error occurs.
*
* @param buffers The buffers into which the data will be read.
*
* @returns The number of bytes read.
*
* @throws boost::system::system_error Thrown on failure.
*
* @note The read_some operation may not read all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that the
* requested amount of data is read before the blocking operation completes.
*/
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers)
{
return next_layer_.read_some(buffers);
}
/// Read some data from the stream.
/**
* This function is used to read data from the stream. The function call will
* block until one or more bytes of data has been read successfully, or until
* an error occurs.
*
* @param buffers The buffers into which the data will be read.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes read. Returns 0 if an error occurred.
*
* @note The read_some operation may not read all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that the
* requested amount of data is read before the blocking operation completes.
*/
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers,
boost::system::error_code& ec)
{
return next_layer_.read_some(buffers, ec);
}
/// Start an asynchronous read.
/**
* This function is used to asynchronously read one or more bytes of data from
* the stream. The function call always returns immediately.
*
* @param buffers The buffers into which the data will be read. Although the
* buffers object may be copied as necessary, ownership of the underlying
* buffers is retained by the caller, which must guarantee that they remain
* valid until the handler is called.
*
* @param handler The handler to be called when the read operation completes.
* Copies will be made of the handler as required. The equivalent function
* signature of the handler must be:
* @code void handler(
* const boost::system::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes read.
* ); @endcode
*
* @note The async_read_some operation may not read all of the requested
* number of bytes. Consider using the @ref async_read function if you need to
* ensure that the requested amount of data is read before the asynchronous
* operation completes.
*/
template <typename MutableBufferSequence, typename ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (boost::system::error_code, std::size_t))
async_read_some(const MutableBufferSequence& buffers,
BOOST_ASIO_MOVE_ARG(ReadHandler) handler);
enum Status {
OUT,
Setcookie,
sEtcookie,
seTcookie,
setDashcookie,
setCookie,
setcOokie,
setcoOkie,
setcooKie,
setcookIe,
setcookiE,
IN
};
/*
* The filtering logic (illustrative purpose, can be greatly improved)
*/
template<class BufferSequence, typename ByteType = char>
void mutate(boost::asio::buffers_iterator<BufferSequence> it) {
switch(status_) {
case OUT:
if (*it == 'S') {
status_ = Setcookie;
} else {
status_ = OUT;
}
break;
case Setcookie:
if (*it == 'e') {
status_ = sEtcookie;
} else {
status_ = OUT;
}
break;
case sEtcookie:
if (*it == 't') {
status_ = seTcookie;
} else {
status_ = OUT;
}
break;
case seTcookie:
if (*it == '-') {
status_ = setDashcookie;
} else {
status_ = OUT;
}
break;
case setDashcookie:
if (*it == 'C') {
status_ = setCookie;
} else {
status_ = OUT;
}
break;
case setCookie:
if (*it == 'o') {
status_ = setcOokie;
} else {
status_ = OUT;
}
break;
case setcOokie:
if (*it == 'o') {
status_ = setcoOkie;
} else {
status_ = OUT;
}
break;
case setcoOkie:
if (*it == 'k') {
status_ = setcooKie;
} else {
status_ = OUT;
}
break;
case setcooKie:
if (*it == 'i') {
status_ = setcookIe;
} else {
status_ = OUT;
}
break;
case setcookIe:
if (*it == 'e') {
status_ = setcookiE;
} else {
status_ = OUT;
}
break;
case setcookiE:
if (*it == ':') {
status_ = IN;
} else {
status_ = OUT;
}
break;
case IN:
if (*it == '\01') {
*it = 'x';
} else if (*it == ';' || *it == '\n') {
status_ = OUT;
}
break;
}
};
private:
NextStream next_layer_;
enum Status status_= OUT;
};
template<class NextLayer>
template<class MutableBufferSequence, class Handler>
class FilterStream<NextLayer>::read_op
: public boost::asio::coroutine
{
using alloc_type = typename
#if defined(BOOST_NO_CXX11_ALLOCATOR)
boost::asio::associated_allocator_t<Handler>::template
rebind<char>::other;
#else
std::allocator_traits<boost::asio::associated_allocator_t<Handler>>
::template rebind_alloc<char>;
#endif
struct data
{
FilterStream<NextLayer>& s;
MutableBufferSequence const b;
bool match = false;
data(
Handler const&,
FilterStream<NextLayer>& s_,
MutableBufferSequence const& b_)
: s(s_)
, b(b_)
{
}
};
boost::beast::handler_ptr<data, Handler> d_;
public:
read_op(read_op&&) = default;
read_op(read_op const&) = delete;
template<class DeducedHandler, class... Args>
read_op(
DeducedHandler&& h,
FilterStream<NextLayer>& s,
MutableBufferSequence const& b)
: d_(std::forward<DeducedHandler>(h), s, b)
{
}
using allocator_type =
boost::asio::associated_allocator_t<Handler>;
allocator_type
get_allocator() const noexcept
{
return (boost::asio::get_associated_allocator)(d_.handler());
}
using executor_type = boost::asio::associated_executor_t<
Handler, decltype(std::declval<NextLayer&>().get_executor())>;
executor_type
get_executor() const noexcept
{
return (boost::asio::get_associated_executor)(
d_.handler(), d_->s.get_executor());
}
void
operator()(
boost::system::error_code ec,
std::size_t bytes_transferred);
template<class Function>
friend
void asio_handler_invoke(Function&& f, read_op* op)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(f, std::addressof(op->d_.handler()));
}
};
template<class NextLayer>
template<class MutableBufferSequence, class Handler>
void
FilterStream<NextLayer>::
read_op<MutableBufferSequence, Handler>::
operator()(boost::system::error_code ec, std::size_t bytes_transferred) {
auto &d = *d_;
BOOST_ASIO_CORO_REENTER(*this) {
BOOST_ASIO_CORO_YIELD d.s.next_layer().async_read_some(d.b, std::move(*this));
if (!ec && bytes_transferred) {
auto b = boost::asio::buffers_begin(d.b);
auto e = boost::asio::buffers_end(d.b);
for (auto it = b; it != e; ++it) {
d.s.mutate(it);
}
}
d_.invoke(ec, bytes_transferred);
}
}
template <typename NextLayer>
template <typename MutableBufferSequence, typename ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
void (boost::system::error_code, std::size_t))
FilterStream<NextLayer>::async_read_some(const MutableBufferSequence& buffers,
BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
{
using namespace boost::beast;
BOOST_BEAST_HANDLER_INIT(
ReadHandler, void(boost::system::error_code, std::size_t));
FilterStream<NextLayer>::read_op<
MutableBufferSequence,
BOOST_ASIO_HANDLER_TYPE(
ReadHandler, void(boost::system::error_code, std::size_t))>{
std::move(init.completion_handler), *this, buffers}(
{}, 0);
return init.result.get();
}
// Necessary for WebSockets
template<class NextStream>
void
teardown(
boost::beast::websocket::role_type type,
FilterStream<NextStream>& stream,
boost::system::error_code& ec)
{
using boost::beast::websocket::teardown;
teardown(type, stream.next_layer(), ec);
}
template<class NextStream, class TeardownHandler>
void
async_teardown(
boost::beast::websocket::role_type type,
FilterStream<NextStream>& stream,
TeardownHandler&& handler)
{
using boost::beast::websocket::async_teardown;
async_teardown(type, stream.next_layer(), std::forward<TeardownHandler>(handler));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment