Last active
June 29, 2020 16:32
-
-
Save HugoGuiroux/556a8564b40baa4d360d2f55d63302a6 to your computer and use it in GitHub Desktop.
Boost stream parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * 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