#pragma once #include "error.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace beast = boost::beast; // from namespace http = beast::http; // from namespace net = boost::asio; // from namespace ssl = boost::asio::ssl; // from namespace websocket = beast::websocket; using tcp = boost::asio::ip::tcp; // from using namespace std::placeholders; // Server session, asynchronous, proxying class websocket_session: public std::enable_shared_from_this { boost::asio::io_context& ioc_; boost::asio::ip::tcp::resolver resolver_; boost::beast::websocket::stream> ws_in_; boost::beast::flat_buffer buffer_in_; boost::beast::websocket::stream ws_app_; boost::beast::flat_buffer buffer_out_; std::string host_; std::string port_; public: explicit websocket_session(boost::asio::io_context& ioc, beast::ssl_stream&& stream): ioc_(ioc), resolver_(boost::asio::make_strand(ioc_)), ws_in_(std::move(stream)), ws_app_(boost::asio::make_strand(ioc_)), host_{"::1"}, port_{"9876"} { } // // The initial setup path // // Start the asynchronous accept operation template void do_accept_in(http::request> req) { // Set suggested timeout settings for the websocket ws_in_.set_option( websocket::stream_base::timeout::suggested( beast::role_type::server)); // Set a decorator to change the Server of the handshake ws_in_.set_option(websocket::stream_base::decorator( [](websocket::response_type& res) { res.set(http::field::server, std::string{"Reichwein.IT Webserver"}); })); // Accept the websocket handshake ws_in_.async_accept( req, beast::bind_front_handler( &websocket_session::on_accept_in, shared_from_this())); } private: void on_accept_in(beast::error_code ec) { if (ec) return fail(ec, "accept in"); resolver_.async_resolve(host_, port_, beast::bind_front_handler(&websocket_session::on_resolve_app, shared_from_this())); } void on_resolve_app(beast::error_code ec, tcp::resolver::results_type results) { if (ec) return fail(ec, "resolve app"); beast::get_lowest_layer(ws_app_).async_connect(results, beast::bind_front_handler(&websocket_session::on_connect_app, shared_from_this())); } void on_connect_app(beast::error_code ec, tcp::resolver::results_type::endpoint_type endpoint) { if (ec) return fail(ec, "connect app"); beast::get_lowest_layer(ws_app_).expires_never(); host_ += ':' + std::to_string(endpoint.port()); // Set suggested timeout settings for the websocket ws_app_.set_option( websocket::stream_base::timeout::suggested( beast::role_type::client)); ws_app_.set_option(boost::beast::websocket::stream_base::decorator( [](boost::beast::websocket::request_type& req) { req.set(boost::beast::http::field::user_agent, "Reichwein.IT Webserver Websocket client"); })); ws_app_.async_handshake(host_, "/", beast::bind_front_handler(&websocket_session::on_handshake_app, shared_from_this())); } void on_handshake_app(beast::error_code ec) { if (ec) return fail(ec, "handshake app"); // Start reading messages from both sides, asynchronously do_read_in(); do_read_app(); } // // The input path (client,ws_in_ -> app,ws_app_) via // void do_read_in() { // Read a message into our buffer ws_in_.async_read( buffer_in_, beast::bind_front_handler( &websocket_session::on_read_in, shared_from_this())); } void on_read_in( beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); // This indicates that the websocket_session was closed if (ec == websocket::error::closed) return; if (ec) fail(ec, "read in"); ws_app_.text(ws_in_.got_text()); do_write_app(); } void do_write_app() { ws_app_.async_write(buffer_in_.data(), beast::bind_front_handler( &websocket_session::on_write_app, shared_from_this())); } void on_write_app(beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if (ec) fail(ec, "write app"); buffer_in_.consume(buffer_in_.size()); // Do another read do_read_in(); } // // The output path (app,ws_app_ -> client,ws_in_) // void do_read_app() { // Read a message into our buffer ws_app_.async_read( buffer_out_, beast::bind_front_handler( &websocket_session::on_read_app, shared_from_this())); } void on_read_app(beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if (ec == websocket::error::closed) return; if (ec) fail(ec, "read app"); do_write_out(); } void do_write_out() { ws_in_.async_write(buffer_out_.data(), beast::bind_front_handler( &websocket_session::on_write_out, shared_from_this())); } void on_write_out( beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if(ec) return fail(ec, "write out"); // Clear the buffer buffer_out_.consume(buffer_out_.size()); // Do another read do_read_app(); } }; // class