diff --git a/CHANGELOG.md b/CHANGELOG.md index f62d9eea..840b8fd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Version 215: * Add experimental test/handler.hpp * Rename to async_op_base::invoke_now * Add async_op_base::invoke +* Remove CppCon2018 example -------------------------------------------------------------------------------- diff --git a/doc/qbk/02_examples/0_examples.qbk b/doc/qbk/02_examples/0_examples.qbk index b33bcce9..370fa54b 100644 --- a/doc/qbk/02_examples/0_examples.qbk +++ b/doc/qbk/02_examples/0_examples.qbk @@ -215,9 +215,9 @@ and illustrate the implementation of advanced features. This talk was given at [@https://cppcon.org CppCon 2018]. In this presentation, we develop a multi-user chat server written in C++ using Beast WebSocket, which uses a provided chat client written in HTML and -JavaScript. The source files for this example are located at -[source_file example/cppcon2018]. - +JavaScript. An improved, multi-threaded version of this program is +included here +[source_file example/websocket/server/chat-multi]. [block''' diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index c2704007..950c29db 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -8,7 +8,6 @@ # add_subdirectory (advanced) -add_subdirectory (cppcon2018) add_subdirectory (http) add_subdirectory (websocket) diff --git a/example/Jamfile b/example/Jamfile index be63be5f..cccbfe4b 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -8,7 +8,6 @@ # build-project advanced ; -build-project cppcon2018 ; build-project http ; build-project websocket ; diff --git a/example/cppcon2018/CMakeLists.txt b/example/cppcon2018/CMakeLists.txt deleted file mode 100644 index c4d938e8..00000000 --- a/example/cppcon2018/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -# -# Copyright (c) 2016-2017 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 -# - -GroupSources(include/boost/beast beast) -GroupSources(example/cppcon2018 "/") - -file (GLOB APP_FILES - beast.hpp - http_session.cpp - http_session.hpp - Jamfile - listener.cpp - listener.hpp - main.cpp - net.hpp - shared_state.cpp - shared_state.hpp - websocket_session.cpp - websocket_session.hpp - chat_client.html - README.md -) - -source_group ("" FILES ${APP_FILES}) - -add_executable (websocket-chat-server - ${APP_FILES} - ${BOOST_BEAST_FILES} -) - -set_property(TARGET websocket-chat-server PROPERTY FOLDER "example-cppcon2018") diff --git a/example/cppcon2018/Jamfile b/example/cppcon2018/Jamfile deleted file mode 100644 index a22014e9..00000000 --- a/example/cppcon2018/Jamfile +++ /dev/null @@ -1,19 +0,0 @@ -# -# Copyright (c) 2016-2017 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 -# - -exe websocket-chat-server : - http_session.cpp - listener.cpp - main.cpp - shared_state.cpp - websocket_session.cpp - : - coverage:no - ubasan:no - ; diff --git a/example/cppcon2018/README.md b/example/cppcon2018/README.md deleted file mode 100644 index 4e62b87a..00000000 --- a/example/cppcon2018/README.md +++ /dev/null @@ -1,23 +0,0 @@ -*This repository contains the presentation file and compiling -source code for the CppCon2018 talk.* - -# Get Rich Quick! Using Boost.Beast WebSockets and Networking TS - -Do you want to make a lot of money? You'll see some examples of free -browser and server based WebSocket programs which have earned their -respective individual authors tens of millions of dollars in no time -at all. Perhaps after seeing this talk in person, you'll write the -next massively successful WebSocket app! - -The WebSocket protocol powers the interactive web by enabling two-way -messaging between the browser and the web server. The Boost.Beast -library implements this protocol on top of the industry standard -Boost.Asio library which models the Networking Technical Specification -proposed for the ISO C++ Standard. - -This presentation introduces Networking TS concepts and algorithms, -how to read their requirements, and how to use them in your programs. -We will build from scratch a multi-user chat server in C++11 using -Beast, and the corresponding browser-based chat client in HTML and -JavaScript. No prior knowledge or understanding of Beast or Asio is -required, the talk is suited for everyone. diff --git a/example/cppcon2018/beast.hpp b/example/cppcon2018/beast.hpp deleted file mode 100644 index 54e12597..00000000 --- a/example/cppcon2018/beast.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright (c) 2018 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/vinniefalco/CppCon2018 -// - -#ifndef CPPCON2018_BEAST_HPP -#define CPPCON2018_BEAST_HPP - -#include - -namespace beast = boost::beast; // from -namespace http = beast::http; // from -namespace websocket = beast::websocket; // from - -#endif diff --git a/example/cppcon2018/chat_client.html b/example/cppcon2018/chat_client.html deleted file mode 100644 index 3913a069..00000000 --- a/example/cppcon2018/chat_client.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - WebSocket Chat - CppCon2018 - - -

WebSocket Chat

-

Source code: https://github.com/vinniefalco/CppCon2018

- - Server URI: - -
- Your Name:
- -

-
-  
- Message
- - -
- - - diff --git a/example/cppcon2018/http_session.cpp b/example/cppcon2018/http_session.cpp deleted file mode 100644 index 11415726..00000000 --- a/example/cppcon2018/http_session.cpp +++ /dev/null @@ -1,349 +0,0 @@ -// -// Copyright (c) 2018 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/vinniefalco/CppCon2018 -// - -#include "http_session.hpp" -#include "websocket_session.hpp" -#include -#include - -#define BOOST_NO_CXX14_GENERIC_LAMBDAS - -//------------------------------------------------------------------------------ - -// Return a reasonable mime type based on the extension of a file. -beast::string_view -mime_type(beast::string_view path) -{ - using beast::iequals; - auto const ext = [&path] - { - auto const pos = path.rfind("."); - if(pos == beast::string_view::npos) - return beast::string_view{}; - return path.substr(pos); - }(); - if(iequals(ext, ".htm")) return "text/html"; - if(iequals(ext, ".html")) return "text/html"; - if(iequals(ext, ".php")) return "text/html"; - if(iequals(ext, ".css")) return "text/css"; - if(iequals(ext, ".txt")) return "text/plain"; - if(iequals(ext, ".js")) return "application/javascript"; - if(iequals(ext, ".json")) return "application/json"; - if(iequals(ext, ".xml")) return "application/xml"; - if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; - if(iequals(ext, ".flv")) return "video/x-flv"; - if(iequals(ext, ".png")) return "image/png"; - if(iequals(ext, ".jpe")) return "image/jpeg"; - if(iequals(ext, ".jpeg")) return "image/jpeg"; - if(iequals(ext, ".jpg")) return "image/jpeg"; - if(iequals(ext, ".gif")) return "image/gif"; - if(iequals(ext, ".bmp")) return "image/bmp"; - if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; - if(iequals(ext, ".tiff")) return "image/tiff"; - if(iequals(ext, ".tif")) return "image/tiff"; - if(iequals(ext, ".svg")) return "image/svg+xml"; - if(iequals(ext, ".svgz")) return "image/svg+xml"; - return "application/text"; -} - -// Append an HTTP rel-path to a local filesystem path. -// The returned path is normalized for the platform. -std::string -path_cat( - beast::string_view base, - beast::string_view path) -{ - if(base.empty()) - return std::string(path); - std::string result(base); -#if BOOST_MSVC - char constexpr path_separator = '\\'; - if(result.back() == path_separator) - result.resize(result.size() - 1); - result.append(path.data(), path.size()); - for(auto& c : result) - if(c == '/') - c = path_separator; -#else - char constexpr path_separator = '/'; - if(result.back() == path_separator) - result.resize(result.size() - 1); - result.append(path.data(), path.size()); -#endif - return result; -} - -// This function produces an HTTP response for the given -// request. The type of the response object depends on the -// contents of the request, so the interface requires the -// caller to pass a generic lambda for receiving the response. -template< - class Body, class Allocator, - class Send> -void -handle_request( - beast::string_view doc_root, - http::request>&& req, - Send&& send) -{ - // Returns a bad request response - auto const bad_request = - [&req](beast::string_view why) - { - http::response res{http::status::bad_request, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = std::string(why); - res.prepare_payload(); - return res; - }; - - // Returns a not found response - auto const not_found = - [&req](beast::string_view target) - { - http::response res{http::status::not_found, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = "The resource '" + std::string(target) + "' was not found."; - res.prepare_payload(); - return res; - }; - - // Returns a server error response - auto const server_error = - [&req](beast::string_view what) - { - http::response res{http::status::internal_server_error, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = "An error occurred: '" + std::string(what) + "'"; - res.prepare_payload(); - return res; - }; - - // Make sure we can handle the method - if( req.method() != http::verb::get && - req.method() != http::verb::head) - return send(bad_request("Unknown HTTP-method")); - - // Request path must be absolute and not contain "..". - if( req.target().empty() || - req.target()[0] != '/' || - req.target().find("..") != beast::string_view::npos) - return send(bad_request("Illegal request-target")); - - // Build the path to the requested file - std::string path = path_cat(doc_root, req.target()); - if(req.target().back() == '/') - path.append("index.html"); - - // Attempt to open the file - beast::error_code ec; - http::file_body::value_type body; - body.open(path.c_str(), beast::file_mode::scan, ec); - - // Handle the case where the file doesn't exist - if(ec == boost::system::errc::no_such_file_or_directory) - return send(not_found(req.target())); - - // Handle an unknown error - if(ec) - return send(server_error(ec.message())); - - // Cache the size since we need it after the move - auto const size = body.size(); - - // Respond to HEAD request - if(req.method() == http::verb::head) - { - http::response res{http::status::ok, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, mime_type(path)); - res.content_length(size); - res.keep_alive(req.keep_alive()); - return send(std::move(res)); - } - - // Respond to GET request - http::response res{ - std::piecewise_construct, - std::make_tuple(std::move(body)), - std::make_tuple(http::status::ok, req.version())}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, mime_type(path)); - res.content_length(size); - res.keep_alive(req.keep_alive()); - return send(std::move(res)); -} - -//------------------------------------------------------------------------------ - -http_session:: -http_session( - tcp::socket socket, - std::shared_ptr const& state) - : socket_(std::move(socket)) - , state_(state) -{ -} - -void -http_session:: -run() -{ - // Read a request - http::async_read(socket_, buffer_, req_, - std::bind( - &http_session::on_read, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2)); -} - -// Report a failure -void -http_session:: -fail(beast::error_code ec, char const* what) -{ - // Don't report on canceled operations - if(ec == net::error::operation_aborted) - return; - - std::cerr << what << ": " << ec.message() << "\n"; -} - -template -void -http_session:: -send_lambda:: -operator()(http::message&& msg) const -{ - // The lifetime of the message has to extend - // for the duration of the async operation so - // we use a shared_ptr to manage it. - auto sp = std::make_shared< - http::message>(std::move(msg)); - - // Write the response - auto self = self_.shared_from_this(); - http::async_write( - self_.socket_, - *sp, - [self, sp](beast::error_code ec, std::size_t bytes) - { - self->on_write(ec, bytes, sp->need_eof()); - }); -} - -void -http_session:: -on_read(beast::error_code ec, std::size_t) -{ - // This means they closed the connection - if(ec == http::error::end_of_stream) - { - socket_.shutdown(tcp::socket::shutdown_send, ec); - return; - } - - // Handle the error, if any - if(ec) - return fail(ec, "read"); - - // See if it is a WebSocket Upgrade - if(websocket::is_upgrade(req_)) - { - // Create a WebSocket session by transferring the socket - std::make_shared( - std::move(socket_), state_)->run(std::move(req_)); - return; - } - - // Send the response -#ifndef BOOST_NO_CXX14_GENERIC_LAMBDAS - // - // The following code requires generic - // lambdas, available in C++14 and later. - // - handle_request( - state_->doc_root(), - std::move(req_), - [this](auto&& response) - { - // The lifetime of the message has to extend - // for the duration of the async operation so - // we use a shared_ptr to manage it. - using response_type = typename std::decay::type; - auto sp = std::make_shared(std::forward(response)); - - #if 0 - // NOTE This causes an ICE in gcc 7.3 - // Write the response - http::async_write(this->socket_, *sp, - [self = shared_from_this(), sp]( - beast::error_code ec, std::size_t bytes) - { - self->on_write(ec, bytes, sp->need_eof()); - }); - #else - // Write the response - auto self = shared_from_this(); - http::async_write(this->socket_, *sp, - [self, sp]( - beast::error_code ec, std::size_t bytes) - { - self->on_write(ec, bytes, sp->need_eof()); - }); - #endif - }); -#else - // - // This code uses the function object type send_lambda in - // place of a generic lambda which is not available in C++11 - // - handle_request( - state_->doc_root(), - std::move(req_), - send_lambda(*this)); - -#endif -} - -void -http_session:: -on_write(beast::error_code ec, std::size_t, bool close) -{ - // Handle the error, if any - if(ec) - return fail(ec, "write"); - - if(close) - { - // This means we should close the connection, usually because - // the response indicated the "Connection: close" semantic. - socket_.shutdown(tcp::socket::shutdown_send, ec); - return; - } - - // Clear contents of the request message, - // otherwise the read behavior is undefined. - req_ = {}; - - // Read another request - http::async_read(socket_, buffer_, req_, - std::bind( - &http_session::on_read, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2)); -} diff --git a/example/cppcon2018/http_session.hpp b/example/cppcon2018/http_session.hpp deleted file mode 100644 index d40ab87f..00000000 --- a/example/cppcon2018/http_session.hpp +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright (c) 2018 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/vinniefalco/CppCon2018 -// - -#ifndef CPPCON2018_HTTP_SESSION_HPP -#define CPPCON2018_HTTP_SESSION_HPP - -#include "net.hpp" -#include "beast.hpp" -#include "shared_state.hpp" -#include -#include - -/** Represents an established HTTP connection -*/ -class http_session : public std::enable_shared_from_this -{ - tcp::socket socket_; - beast::flat_buffer buffer_; - std::shared_ptr state_; - http::request req_; - - struct send_lambda - { - http_session& self_; - - explicit - send_lambda(http_session& self) - : self_(self) - { - } - - template - void - operator()(http::message&& msg) const; - }; - - void fail(beast::error_code ec, char const* what); - void on_read(beast::error_code ec, std::size_t); - void on_write(beast::error_code ec, std::size_t, bool close); - -public: - http_session( - tcp::socket socket, - std::shared_ptr const& state); - - void run(); -}; - -#endif diff --git a/example/cppcon2018/listener.cpp b/example/cppcon2018/listener.cpp deleted file mode 100644 index c38c353b..00000000 --- a/example/cppcon2018/listener.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// -// Copyright (c) 2018 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/vinniefalco/CppCon2018 -// - -#include "listener.hpp" -#include "http_session.hpp" -#include - -listener:: -listener( - net::io_context& ioc, - tcp::endpoint endpoint, - std::shared_ptr const& state) - : acceptor_(ioc) - , socket_(ioc) - , state_(state) -{ - beast::error_code ec; - - // Open the acceptor - acceptor_.open(endpoint.protocol(), ec); - if(ec) - { - fail(ec, "open"); - return; - } - - // Allow address reuse - acceptor_.set_option(net::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( - net::socket_base::max_listen_connections, ec); - if(ec) - { - fail(ec, "listen"); - return; - } -} - -void -listener:: -run() -{ - // Start accepting a connection - acceptor_.async_accept( - socket_, - std::bind( - &listener::on_accept, - shared_from_this(), - std::placeholders::_1)); -} - -// Report a failure -void -listener:: -fail(beast::error_code ec, char const* what) -{ - // Don't report on canceled operations - if(ec == net::error::operation_aborted) - return; - std::cerr << what << ": " << ec.message() << "\n"; -} - -// Handle a connection -void -listener:: -on_accept(beast::error_code ec) -{ - if(ec) - return fail(ec, "accept"); - else - // Launch a new session for this connection - std::make_shared( - std::move(socket_), - state_)->run(); - - // Accept another connection - acceptor_.async_accept( - socket_, - std::bind( - &listener::on_accept, - shared_from_this(), - std::placeholders::_1)); -} diff --git a/example/cppcon2018/listener.hpp b/example/cppcon2018/listener.hpp deleted file mode 100644 index 47d86ce4..00000000 --- a/example/cppcon2018/listener.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (c) 2018 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/vinniefalco/CppCon2018 -// - -#ifndef CPPCON2018_LISTENER_HPP -#define CPPCON2018_LISTENER_HPP - -#include "beast.hpp" -#include "net.hpp" -#include -#include - -// Forward declaration -class shared_state; - -// Accepts incoming connections and launches the sessions -class listener : public std::enable_shared_from_this -{ - tcp::acceptor acceptor_; - tcp::socket socket_; - std::shared_ptr state_; - - void fail(beast::error_code ec, char const* what); - void on_accept(beast::error_code ec); - -public: - listener( - net::io_context& ioc, - tcp::endpoint endpoint, - std::shared_ptr const& state); - - // Start accepting incoming connections - void run(); -}; - -#endif diff --git a/example/cppcon2018/main.cpp b/example/cppcon2018/main.cpp deleted file mode 100644 index 79c4d9de..00000000 --- a/example/cppcon2018/main.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// -// Copyright (c) 2018 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/vinniefalco/CppCon2018 -// - -//------------------------------------------------------------------------------ -/* - WebSocket chat server - - This implements a multi-user chat room using WebSocket. -*/ -//------------------------------------------------------------------------------ - -#include "listener.hpp" -#include "shared_state.hpp" -#include -#include - -int -main(int argc, char* argv[]) -{ - // Check command line arguments. - if (argc != 4) - { - std::cerr << - "Usage: websocket-chat-server
\n" << - "Example:\n" << - " websocket-chat-server 0.0.0.0 8080 .\n"; - return EXIT_FAILURE; - } - auto address = net::ip::make_address(argv[1]); - auto port = static_cast(std::atoi(argv[2])); - auto doc_root = argv[3]; - - // The io_context is required for all I/O - net::io_context ioc; - - // Create and launch a listening port - std::make_shared( - ioc, - tcp::endpoint{address, port}, - std::make_shared(doc_root))->run(); - - // Capture SIGINT and SIGTERM to perform a clean shutdown - net::signal_set signals(ioc, SIGINT, SIGTERM); - signals.async_wait( - [&ioc](boost::system::error_code const&, int) - { - // Stop the io_context. This will cause run() - // to return immediately, eventually destroying the - // io_context and any remaining handlers in it. - ioc.stop(); - }); - - // Run the I/O service on the main thread - ioc.run(); - - // (If we get here, it means we got a SIGINT or SIGTERM) - - return EXIT_SUCCESS; -} diff --git a/example/cppcon2018/net.hpp b/example/cppcon2018/net.hpp deleted file mode 100644 index 4879aa17..00000000 --- a/example/cppcon2018/net.hpp +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright (c) 2018 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/vinniefalco/CppCon2018 -// - -#ifndef CPPCON2018_ASIO_HPP -#define CPPCON2018_ASIO_HPP - -#include - -namespace net = boost::asio; // from -using tcp = boost::asio::ip::tcp; // from - -#endif diff --git a/example/cppcon2018/shared_state.cpp b/example/cppcon2018/shared_state.cpp deleted file mode 100644 index a31d0e1a..00000000 --- a/example/cppcon2018/shared_state.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (c) 2018 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/vinniefalco/CppCon2018 -// - -#include "shared_state.hpp" -#include "websocket_session.hpp" - -shared_state:: -shared_state(std::string doc_root) - : doc_root_(std::move(doc_root)) -{ -} - -void -shared_state:: -join(websocket_session& session) -{ - sessions_.insert(&session); -} - -void -shared_state:: -leave(websocket_session& session) -{ - sessions_.erase(&session); -} - -void -shared_state:: -send(std::string message) -{ - auto const ss = std::make_shared(std::move(message)); - - for(auto session : sessions_) - session->send(ss); -} diff --git a/example/cppcon2018/shared_state.hpp b/example/cppcon2018/shared_state.hpp deleted file mode 100644 index b00bf11a..00000000 --- a/example/cppcon2018/shared_state.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// -// Copyright (c) 2018 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/vinniefalco/CppCon2018 -// - -#ifndef CPPCON2018_SHARED_STATE_HPP -#define CPPCON2018_SHARED_STATE_HPP - -#include -#include -#include - -// Forward declaration -class websocket_session; - -// Represents the shared server state -class shared_state -{ - std::string doc_root_; - - // This simple method of tracking - // sessions only works with an implicit - // strand (i.e. a single-threaded server) - std::unordered_set sessions_; - -public: - explicit - shared_state(std::string doc_root); - - std::string const& - doc_root() const noexcept - { - return doc_root_; - } - - void join (websocket_session& session); - void leave (websocket_session& session); - void send (std::string message); -}; - -#endif diff --git a/example/cppcon2018/websocket_session.cpp b/example/cppcon2018/websocket_session.cpp deleted file mode 100644 index 2f87ff96..00000000 --- a/example/cppcon2018/websocket_session.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// -// Copyright (c) 2018 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/vinniefalco/CppCon2018 -// - -#include "websocket_session.hpp" -#include - -websocket_session:: -websocket_session( - tcp::socket socket, - std::shared_ptr const& state) - : ws_(std::move(socket)) - , state_(state) -{ -} - -websocket_session:: -~websocket_session() -{ - // Remove this session from the list of active sessions - state_->leave(*this); -} - -void -websocket_session:: -fail(beast::error_code ec, char const* what) -{ - // Don't report these - if( ec == net::error::operation_aborted || - ec == websocket::error::closed) - return; - - std::cerr << what << ": " << ec.message() << "\n"; -} - -void -websocket_session:: -on_accept(beast::error_code ec) -{ - // Handle the error, if any - if(ec) - return fail(ec, "accept"); - - // Add this session to the list of active sessions - state_->join(*this); - - // Read a message - ws_.async_read( - buffer_, - std::bind( - &websocket_session::on_read, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2)); -} - -void -websocket_session:: -on_read(beast::error_code ec, std::size_t) -{ - // Handle the error, if any - if(ec) - return fail(ec, "read"); - - // Send to all connections - state_->send(beast::buffers_to_string(buffer_.data())); - - // Clear the buffer - buffer_.consume(buffer_.size()); - - // Read another message - ws_.async_read( - buffer_, - std::bind( - &websocket_session::on_read, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2)); -} - -void -websocket_session:: -send(std::shared_ptr const& ss) -{ - // Always add to queue - queue_.push_back(ss); - - // Are we already writing? - if(queue_.size() > 1) - return; - - // We are not currently writing, so send this immediately - ws_.async_write( - net::buffer(*queue_.front()), - std::bind( - &websocket_session::on_write, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2)); -} - -void -websocket_session:: -on_write(beast::error_code ec, std::size_t) -{ - // Handle the error, if any - if(ec) - return fail(ec, "write"); - - // Remove the string from the queue - queue_.erase(queue_.begin()); - - // Send the next message if any - if(! queue_.empty()) - ws_.async_write( - net::buffer(*queue_.front()), - std::bind( - &websocket_session::on_write, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2)); -} diff --git a/example/cppcon2018/websocket_session.hpp b/example/cppcon2018/websocket_session.hpp deleted file mode 100644 index 9b5e71a1..00000000 --- a/example/cppcon2018/websocket_session.hpp +++ /dev/null @@ -1,69 +0,0 @@ -// -// Copyright (c) 2018 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/vinniefalco/CppCon2018 -// - -#ifndef CPPCON2018_WEBSOCKET_SESSION_HPP -#define CPPCON2018_WEBSOCKET_SESSION_HPP - -#include "net.hpp" -#include "beast.hpp" -#include "shared_state.hpp" - -#include -#include -#include -#include - -// Forward declaration -class shared_state; - -/** Represents an active WebSocket connection to the server -*/ -class websocket_session : public std::enable_shared_from_this -{ - beast::flat_buffer buffer_; - websocket::stream ws_; - std::shared_ptr state_; - std::vector> queue_; - - void fail(beast::error_code ec, char const* what); - void on_accept(beast::error_code ec); - void on_read(beast::error_code ec, std::size_t bytes_transferred); - void on_write(beast::error_code ec, std::size_t bytes_transferred); - -public: - websocket_session( - tcp::socket socket, - std::shared_ptr const& state); - - ~websocket_session(); - - template - void - run(http::request> req); - - // Send a message - void - send(std::shared_ptr const& ss); -}; - -template -void -websocket_session:: -run(http::request> req) -{ - // Accept the websocket handshake - ws_.async_accept( - req, - std::bind( - &websocket_session::on_accept, - shared_from_this(), - std::placeholders::_1)); -} - -#endif