// // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) // // 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) // // Official repository: https://github.com/boostorg/beast // //------------------------------------------------------------------------------ // // Example: WebSocket server, asynchronous // //------------------------------------------------------------------------------ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //------------------------------------------------------------------------------ // Report a failure void fail(boost::beast::error_code ec, char const* what) { std::cerr << what << ": " << ec.message() << "\n"; } // Echoes back all received WebSocket messages class session : public std::enable_shared_from_this { boost::beast::websocket::stream ws_; boost::beast::flat_buffer buffer_; public: // Take ownership of the socket explicit session(boost::asio::ip::tcp::socket&& socket) : ws_(std::move(socket)) { } // Get on the correct executor void run() { // We need to be executing within a strand to perform async operations // on the I/O objects in this session. Although not strictly necessary // for single-threaded contexts, this example code is written to be // thread-safe by default. boost::asio::dispatch( ws_.get_executor(), boost::beast::bind_front_handler( &session::on_run, shared_from_this())); } // Start the asynchronous operation void on_run() { // Set suggested timeout settings for the websocket ws_.set_option(boost::beast::websocket::stream_base::timeout::suggested( boost::beast::role_type::server)); // Set a decorator to change the Server of the handshake ws_.set_option(boost::beast::websocket::stream_base::decorator( [](boost::beast::websocket::response_type& res) { res.set( boost::beast::http::field::server, std::string(BOOST_BEAST_VERSION_STRING) + " websocket-server-async"); })); // Accept the websocket handshake ws_.async_accept(boost::beast::bind_front_handler( &session::on_accept, shared_from_this())); } void on_accept(boost::beast::error_code ec) { if (ec) return fail(ec, "accept"); // Read a message do_read(); } void do_read() { // Read a message into our buffer ws_.async_read( buffer_, boost::beast::bind_front_handler( &session::on_read, shared_from_this())); } void on_read(boost::beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); // This indicates that the session was closed if (ec == boost::beast::websocket::error::closed) return; if (ec) fail(ec, "read"); // Echo the message ws_.text(ws_.got_text()); ws_.async_write( buffer_.data(), boost::beast::bind_front_handler( &session::on_write, shared_from_this())); } void on_write(boost::beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if (ec) return fail(ec, "write"); // Clear the buffer buffer_.consume(buffer_.size()); // Do another read do_read(); } }; //------------------------------------------------------------------------------ // Accepts incoming connections and launches the sessions class listener : public std::enable_shared_from_this { boost::asio::io_context& ioc_; boost::asio::ip::tcp::acceptor acceptor_; public: listener( boost::asio::io_context& ioc, boost::asio::ip::tcp::endpoint endpoint) : ioc_(ioc), acceptor_(ioc) { boost::beast::error_code ec; // Open the acceptor acceptor_.open(endpoint.protocol(), ec); if (ec) { fail(ec, "open"); return; } // Allow address reuse acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec); if (ec) { fail(ec, "set_option"); return; } // Bind to the server address acceptor_.bind(endpoint, ec); if (ec) { fail(ec, "bind"); return; } // Start listening for connections acceptor_.listen(boost::asio::socket_base::max_listen_connections, ec); if (ec) { fail(ec, "listen"); return; } } // Start accepting incoming connections void run() { do_accept(); } private: void do_accept() { // The new connection gets its own strand acceptor_.async_accept( boost::asio::make_strand(ioc_), boost::beast::bind_front_handler( &listener::on_accept, shared_from_this())); } void on_accept(boost::beast::error_code ec, boost::asio::ip::tcp::socket socket) { if (ec) { fail(ec, "accept"); } else { // Create the session and run it std::make_shared(std::move(socket))->run(); } // Accept another connection do_accept(); } }; std::optional parse_config(const char* filename) { try { std::ifstream in(filename, std::ios::in | std::ios::binary); if (in) { std::stringstream contents; contents << in.rdbuf(); in.close(); std::cout << contents.str() << std::endl; boost::json::value value = boost::json::parse(contents.str()); return value.as_object(); } } catch (std::exception const& e) { std::cout << e.what() << std::endl; } return {}; } //------------------------------------------------------------------------------ int main(int argc, char* argv[]) { // Check command line arguments. if (argc != 5) { std::cerr << "Usage: websocket-server-async
" " \n" << "Example:\n" << " websocket-server-async 0.0.0.0 8080 1\n"; return EXIT_FAILURE; } auto const address = boost::asio::ip::make_address(argv[1]); auto const port = static_cast(std::atoi(argv[2])); auto const threads = std::max(1, std::atoi(argv[3])); auto const config = parse_config(argv[4]); if (!config) { std::cerr << "couldnt parse config. Exiting..." << std::endl; return EXIT_FAILURE; } auto cassConfig = (*config).at("database").as_object().at("cassandra").as_object(); std::cout << cassConfig << std::endl; CassandraFlatMapBackend backend{cassConfig}; backend.open(); boost::json::array sources = (*config).at("etl_sources").as_array(); if (!sources.size()) { std::cerr << "no etl sources listed in config. exiting..." << std::endl; return EXIT_FAILURE; } NetworkValidatedLedgers nwvl; ETLSource source{sources[0].as_object(), backend, nwvl}; source.start(); // source.loadInitialLedger(60000000); // The io_context is required for all I/O boost::asio::io_context ioc{threads}; // Create and launch a listening port std::make_shared( ioc, boost::asio::ip::tcp::endpoint{address, port}) ->run(); // Run the I/O service on the requested number of threads std::vector v; v.reserve(threads - 1); for (auto i = threads - 1; i > 0; --i) v.emplace_back([&ioc] { ioc.run(); }); ioc.run(); return EXIT_SUCCESS; }