diff options
Diffstat (limited to 'src/boost/libs/asio/example')
281 files changed, 30360 insertions, 0 deletions
diff --git a/src/boost/libs/asio/example/cpp03/allocation/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/allocation/Jamfile.v2 new file mode 100644 index 000000000..f47511bdb --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/allocation/Jamfile.v2 @@ -0,0 +1,30 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe server + : server.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/allocation/server.cpp b/src/boost/libs/asio/example/cpp03/allocation/server.cpp new file mode 100644 index 000000000..7a0e4966f --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/allocation/server.cpp @@ -0,0 +1,285 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <iostream> +#include <boost/aligned_storage.hpp> +#include <boost/array.hpp> +#include <boost/bind/bind.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +// Class to manage the memory to be used for handler-based custom allocation. +// It contains a single block of memory which may be returned for allocation +// requests. If the memory is in use when an allocation request is made, the +// allocator delegates allocation to the global heap. +class handler_memory + : private boost::noncopyable +{ +public: + handler_memory() + : in_use_(false) + { + } + + void* allocate(std::size_t size) + { + if (!in_use_ && size < storage_.size) + { + in_use_ = true; + return storage_.address(); + } + else + { + return ::operator new(size); + } + } + + void deallocate(void* pointer) + { + if (pointer == storage_.address()) + { + in_use_ = false; + } + else + { + ::operator delete(pointer); + } + } + +private: + // Storage space used for handler-based custom memory allocation. + boost::aligned_storage<1024> storage_; + + // Whether the handler-based custom allocation storage has been used. + bool in_use_; +}; + +// The allocator to be associated with the handler objects. This allocator only +// needs to satisfy the C++11 minimal allocator requirements, plus rebind when +// targeting C++03. +template <typename T> +class handler_allocator +{ +public: + typedef T value_type; + + explicit handler_allocator(handler_memory& mem) + : memory_(mem) + { + } + + template <typename U> + handler_allocator(const handler_allocator<U>& other) + : memory_(other.memory_) + { + } + + template <typename U> + struct rebind + { + typedef handler_allocator<U> other; + }; + + bool operator==(const handler_allocator& other) const + { + return &memory_ == &other.memory_; + } + + bool operator!=(const handler_allocator& other) const + { + return &memory_ != &other.memory_; + } + + T* allocate(std::size_t n) const + { + return static_cast<T*>(memory_.allocate(sizeof(T) * n)); + } + + void deallocate(T* p, std::size_t /*n*/) const + { + return memory_.deallocate(p); + } + +//private: + // The underlying memory. + handler_memory& memory_; +}; + +// Wrapper class template for handler objects to allow handler memory +// allocation to be customised. The allocator_type typedef and get_allocator() +// member function are used by the asynchronous operations to obtain the +// allocator. Calls to operator() are forwarded to the encapsulated handler. +template <typename Handler> +class custom_alloc_handler +{ +public: + typedef handler_allocator<Handler> allocator_type; + + custom_alloc_handler(handler_memory& m, Handler h) + : memory_(m), + handler_(h) + { + } + + allocator_type get_allocator() const + { + return allocator_type(memory_); + } + + template <typename Arg1> + void operator()(Arg1 arg1) + { + handler_(arg1); + } + + template <typename Arg1, typename Arg2> + void operator()(Arg1 arg1, Arg2 arg2) + { + handler_(arg1, arg2); + } + +private: + handler_memory& memory_; + Handler handler_; +}; + +// Helper function to wrap a handler object to add custom allocation. +template <typename Handler> +inline custom_alloc_handler<Handler> make_custom_alloc_handler( + handler_memory& m, Handler h) +{ + return custom_alloc_handler<Handler>(m, h); +} + +class session + : public boost::enable_shared_from_this<session> +{ +public: + session(boost::asio::io_context& io_context) + : socket_(io_context) + { + } + + tcp::socket& socket() + { + return socket_; + } + + void start() + { + socket_.async_read_some(boost::asio::buffer(data_), + make_custom_alloc_handler(handler_memory_, + boost::bind(&session::handle_read, + shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred))); + } + + void handle_read(const boost::system::error_code& error, + size_t bytes_transferred) + { + if (!error) + { + boost::asio::async_write(socket_, + boost::asio::buffer(data_, bytes_transferred), + make_custom_alloc_handler(handler_memory_, + boost::bind(&session::handle_write, + shared_from_this(), + boost::asio::placeholders::error))); + } + } + + void handle_write(const boost::system::error_code& error) + { + if (!error) + { + socket_.async_read_some(boost::asio::buffer(data_), + make_custom_alloc_handler(handler_memory_, + boost::bind(&session::handle_read, + shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred))); + } + } + +private: + // The socket used to communicate with the client. + tcp::socket socket_; + + // Buffer used to store data received from the client. + boost::array<char, 1024> data_; + + // The memory to use for handler-based custom memory allocation. + handler_memory handler_memory_; +}; + +typedef boost::shared_ptr<session> session_ptr; + +class server +{ +public: + server(boost::asio::io_context& io_context, short port) + : io_context_(io_context), + acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) + { + session_ptr new_session(new session(io_context_)); + acceptor_.async_accept(new_session->socket(), + boost::bind(&server::handle_accept, this, new_session, + boost::asio::placeholders::error)); + } + + void handle_accept(session_ptr new_session, + const boost::system::error_code& error) + { + if (!error) + { + new_session->start(); + } + + new_session.reset(new session(io_context_)); + acceptor_.async_accept(new_session->socket(), + boost::bind(&server::handle_accept, this, new_session, + boost::asio::placeholders::error)); + } + +private: + boost::asio::io_context& io_context_; + tcp::acceptor acceptor_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + using namespace std; // For atoi. + server s(io_context, atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/buffers/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/buffers/Jamfile.v2 new file mode 100644 index 000000000..f8dde7d80 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/buffers/Jamfile.v2 @@ -0,0 +1,30 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe server + : reference_counted.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/buffers/reference_counted.cpp b/src/boost/libs/asio/example/cpp03/buffers/reference_counted.cpp new file mode 100644 index 000000000..d43e27ff4 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/buffers/reference_counted.cpp @@ -0,0 +1,131 @@ +// +// reference_counted.cpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio.hpp> +#include <boost/bind/bind.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/shared_ptr.hpp> +#include <iostream> +#include <vector> + +using boost::asio::ip::tcp; + +// A reference-counted non-modifiable buffer class. +class shared_const_buffer +{ +public: + // Construct from a std::string. + explicit shared_const_buffer(const std::string& data) + : data_(new std::vector<char>(data.begin(), data.end())), + buffer_(boost::asio::buffer(*data_)) + { + } + + // Implement the ConstBufferSequence requirements. + typedef boost::asio::const_buffer value_type; + typedef const boost::asio::const_buffer* const_iterator; + const boost::asio::const_buffer* begin() const { return &buffer_; } + const boost::asio::const_buffer* end() const { return &buffer_ + 1; } + +private: + boost::shared_ptr<std::vector<char> > data_; + boost::asio::const_buffer buffer_; +}; + +class session + : public boost::enable_shared_from_this<session> +{ +public: + session(boost::asio::io_context& io_context) + : socket_(io_context) + { + } + + tcp::socket& socket() + { + return socket_; + } + + void start() + { + using namespace std; // For time_t, time and ctime. + time_t now = time(0); + shared_const_buffer buffer(ctime(&now)); + boost::asio::async_write(socket_, buffer, + boost::bind(&session::handle_write, shared_from_this())); + } + + void handle_write() + { + } + +private: + // The socket used to communicate with the client. + tcp::socket socket_; +}; + +typedef boost::shared_ptr<session> session_ptr; + +class server +{ +public: + server(boost::asio::io_context& io_context, short port) + : io_context_(io_context), + acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) + { + session_ptr new_session(new session(io_context_)); + acceptor_.async_accept(new_session->socket(), + boost::bind(&server::handle_accept, this, new_session, + boost::asio::placeholders::error)); + } + + void handle_accept(session_ptr new_session, + const boost::system::error_code& error) + { + if (!error) + { + new_session->start(); + } + + new_session.reset(new session(io_context_)); + acceptor_.async_accept(new_session->socket(), + boost::bind(&server::handle_accept, this, new_session, + boost::asio::placeholders::error)); + } + +private: + boost::asio::io_context& io_context_; + tcp::acceptor acceptor_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: reference_counted <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + using namespace std; // For atoi. + server s(io_context, atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/chat/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/chat/Jamfile.v2 new file mode 100644 index 000000000..342bbdcd4 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/chat/Jamfile.v2 @@ -0,0 +1,35 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/chrono//boost_chrono + <library>/boost/thread//boost_thread + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe chat_server : chat_server.cpp ; +exe chat_client : chat_client.cpp ; +exe posix_chat_client : posix_chat_client.cpp ; diff --git a/src/boost/libs/asio/example/cpp03/chat/chat_client.cpp b/src/boost/libs/asio/example/cpp03/chat/chat_client.cpp new file mode 100644 index 000000000..08b310066 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/chat/chat_client.cpp @@ -0,0 +1,178 @@ +// +// chat_client.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <deque> +#include <iostream> +#include <boost/bind/bind.hpp> +#include <boost/asio.hpp> +#include <boost/thread/thread.hpp> +#include "chat_message.hpp" + +using boost::asio::ip::tcp; + +typedef std::deque<chat_message> chat_message_queue; + +class chat_client +{ +public: + chat_client(boost::asio::io_context& io_context, + const tcp::resolver::results_type& endpoints) + : io_context_(io_context), + socket_(io_context) + { + boost::asio::async_connect(socket_, endpoints, + boost::bind(&chat_client::handle_connect, this, + boost::asio::placeholders::error)); + } + + void write(const chat_message& msg) + { + boost::asio::post(io_context_, + boost::bind(&chat_client::do_write, this, msg)); + } + + void close() + { + boost::asio::post(io_context_, + boost::bind(&chat_client::do_close, this)); + } + +private: + + void handle_connect(const boost::system::error_code& error) + { + if (!error) + { + boost::asio::async_read(socket_, + boost::asio::buffer(read_msg_.data(), chat_message::header_length), + boost::bind(&chat_client::handle_read_header, this, + boost::asio::placeholders::error)); + } + } + + void handle_read_header(const boost::system::error_code& error) + { + if (!error && read_msg_.decode_header()) + { + boost::asio::async_read(socket_, + boost::asio::buffer(read_msg_.body(), read_msg_.body_length()), + boost::bind(&chat_client::handle_read_body, this, + boost::asio::placeholders::error)); + } + else + { + do_close(); + } + } + + void handle_read_body(const boost::system::error_code& error) + { + if (!error) + { + std::cout.write(read_msg_.body(), read_msg_.body_length()); + std::cout << "\n"; + boost::asio::async_read(socket_, + boost::asio::buffer(read_msg_.data(), chat_message::header_length), + boost::bind(&chat_client::handle_read_header, this, + boost::asio::placeholders::error)); + } + else + { + do_close(); + } + } + + void do_write(chat_message msg) + { + bool write_in_progress = !write_msgs_.empty(); + write_msgs_.push_back(msg); + if (!write_in_progress) + { + boost::asio::async_write(socket_, + boost::asio::buffer(write_msgs_.front().data(), + write_msgs_.front().length()), + boost::bind(&chat_client::handle_write, this, + boost::asio::placeholders::error)); + } + } + + void handle_write(const boost::system::error_code& error) + { + if (!error) + { + write_msgs_.pop_front(); + if (!write_msgs_.empty()) + { + boost::asio::async_write(socket_, + boost::asio::buffer(write_msgs_.front().data(), + write_msgs_.front().length()), + boost::bind(&chat_client::handle_write, this, + boost::asio::placeholders::error)); + } + } + else + { + do_close(); + } + } + + void do_close() + { + socket_.close(); + } + +private: + boost::asio::io_context& io_context_; + tcp::socket socket_; + chat_message read_msg_; + chat_message_queue write_msgs_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: chat_client <host> <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + tcp::resolver resolver(io_context); + tcp::resolver::results_type endpoints = resolver.resolve(argv[1], argv[2]); + + chat_client c(io_context, endpoints); + + boost::thread t(boost::bind(&boost::asio::io_context::run, &io_context)); + + char line[chat_message::max_body_length + 1]; + while (std::cin.getline(line, chat_message::max_body_length + 1)) + { + using namespace std; // For strlen and memcpy. + chat_message msg; + msg.body_length(strlen(line)); + memcpy(msg.body(), line, msg.body_length()); + msg.encode_header(); + c.write(msg); + } + + c.close(); + t.join(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/chat/chat_message.hpp b/src/boost/libs/asio/example/cpp03/chat/chat_message.hpp new file mode 100644 index 000000000..0c22c4b23 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/chat/chat_message.hpp @@ -0,0 +1,93 @@ +// +// chat_message.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef CHAT_MESSAGE_HPP +#define CHAT_MESSAGE_HPP + +#include <cstdio> +#include <cstdlib> +#include <cstring> + +class chat_message +{ +public: + enum { header_length = 4 }; + enum { max_body_length = 512 }; + + chat_message() + : body_length_(0) + { + } + + const char* data() const + { + return data_; + } + + char* data() + { + return data_; + } + + size_t length() const + { + return header_length + body_length_; + } + + const char* body() const + { + return data_ + header_length; + } + + char* body() + { + return data_ + header_length; + } + + size_t body_length() const + { + return body_length_; + } + + void body_length(size_t new_length) + { + body_length_ = new_length; + if (body_length_ > max_body_length) + body_length_ = max_body_length; + } + + bool decode_header() + { + using namespace std; // For strncat and atoi. + char header[header_length + 1] = ""; + strncat(header, data_, header_length); + body_length_ = atoi(header); + if (body_length_ > max_body_length) + { + body_length_ = 0; + return false; + } + return true; + } + + void encode_header() + { + using namespace std; // For sprintf and memcpy. + char header[header_length + 1] = ""; + sprintf(header, "%4d", static_cast<int>(body_length_)); + memcpy(data_, header, header_length); + } + +private: + char data_[header_length + max_body_length]; + size_t body_length_; +}; + +#endif // CHAT_MESSAGE_HPP diff --git a/src/boost/libs/asio/example/cpp03/chat/chat_server.cpp b/src/boost/libs/asio/example/cpp03/chat/chat_server.cpp new file mode 100644 index 000000000..9347366fc --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/chat/chat_server.cpp @@ -0,0 +1,249 @@ +// +// chat_server.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <algorithm> +#include <cstdlib> +#include <deque> +#include <iostream> +#include <list> +#include <set> +#include <boost/bind/bind.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/asio.hpp> +#include "chat_message.hpp" + +using boost::asio::ip::tcp; + +//---------------------------------------------------------------------- + +typedef std::deque<chat_message> chat_message_queue; + +//---------------------------------------------------------------------- + +class chat_participant +{ +public: + virtual ~chat_participant() {} + virtual void deliver(const chat_message& msg) = 0; +}; + +typedef boost::shared_ptr<chat_participant> chat_participant_ptr; + +//---------------------------------------------------------------------- + +class chat_room +{ +public: + void join(chat_participant_ptr participant) + { + participants_.insert(participant); + std::for_each(recent_msgs_.begin(), recent_msgs_.end(), + boost::bind(&chat_participant::deliver, + participant, boost::placeholders::_1)); + } + + void leave(chat_participant_ptr participant) + { + participants_.erase(participant); + } + + void deliver(const chat_message& msg) + { + recent_msgs_.push_back(msg); + while (recent_msgs_.size() > max_recent_msgs) + recent_msgs_.pop_front(); + + std::for_each(participants_.begin(), participants_.end(), + boost::bind(&chat_participant::deliver, + boost::placeholders::_1, boost::ref(msg))); + } + +private: + std::set<chat_participant_ptr> participants_; + enum { max_recent_msgs = 100 }; + chat_message_queue recent_msgs_; +}; + +//---------------------------------------------------------------------- + +class chat_session + : public chat_participant, + public boost::enable_shared_from_this<chat_session> +{ +public: + chat_session(boost::asio::io_context& io_context, chat_room& room) + : socket_(io_context), + room_(room) + { + } + + tcp::socket& socket() + { + return socket_; + } + + void start() + { + room_.join(shared_from_this()); + boost::asio::async_read(socket_, + boost::asio::buffer(read_msg_.data(), chat_message::header_length), + boost::bind( + &chat_session::handle_read_header, shared_from_this(), + boost::asio::placeholders::error)); + } + + void deliver(const chat_message& msg) + { + bool write_in_progress = !write_msgs_.empty(); + write_msgs_.push_back(msg); + if (!write_in_progress) + { + boost::asio::async_write(socket_, + boost::asio::buffer(write_msgs_.front().data(), + write_msgs_.front().length()), + boost::bind(&chat_session::handle_write, shared_from_this(), + boost::asio::placeholders::error)); + } + } + + void handle_read_header(const boost::system::error_code& error) + { + if (!error && read_msg_.decode_header()) + { + boost::asio::async_read(socket_, + boost::asio::buffer(read_msg_.body(), read_msg_.body_length()), + boost::bind(&chat_session::handle_read_body, shared_from_this(), + boost::asio::placeholders::error)); + } + else + { + room_.leave(shared_from_this()); + } + } + + void handle_read_body(const boost::system::error_code& error) + { + if (!error) + { + room_.deliver(read_msg_); + boost::asio::async_read(socket_, + boost::asio::buffer(read_msg_.data(), chat_message::header_length), + boost::bind(&chat_session::handle_read_header, shared_from_this(), + boost::asio::placeholders::error)); + } + else + { + room_.leave(shared_from_this()); + } + } + + void handle_write(const boost::system::error_code& error) + { + if (!error) + { + write_msgs_.pop_front(); + if (!write_msgs_.empty()) + { + boost::asio::async_write(socket_, + boost::asio::buffer(write_msgs_.front().data(), + write_msgs_.front().length()), + boost::bind(&chat_session::handle_write, shared_from_this(), + boost::asio::placeholders::error)); + } + } + else + { + room_.leave(shared_from_this()); + } + } + +private: + tcp::socket socket_; + chat_room& room_; + chat_message read_msg_; + chat_message_queue write_msgs_; +}; + +typedef boost::shared_ptr<chat_session> chat_session_ptr; + +//---------------------------------------------------------------------- + +class chat_server +{ +public: + chat_server(boost::asio::io_context& io_context, + const tcp::endpoint& endpoint) + : io_context_(io_context), + acceptor_(io_context, endpoint) + { + start_accept(); + } + + void start_accept() + { + chat_session_ptr new_session(new chat_session(io_context_, room_)); + acceptor_.async_accept(new_session->socket(), + boost::bind(&chat_server::handle_accept, this, new_session, + boost::asio::placeholders::error)); + } + + void handle_accept(chat_session_ptr session, + const boost::system::error_code& error) + { + if (!error) + { + session->start(); + } + + start_accept(); + } + +private: + boost::asio::io_context& io_context_; + tcp::acceptor acceptor_; + chat_room room_; +}; + +typedef boost::shared_ptr<chat_server> chat_server_ptr; +typedef std::list<chat_server_ptr> chat_server_list; + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + try + { + if (argc < 2) + { + std::cerr << "Usage: chat_server <port> [<port> ...]\n"; + return 1; + } + + boost::asio::io_context io_context; + + chat_server_list servers; + for (int i = 1; i < argc; ++i) + { + using namespace std; // For atoi. + tcp::endpoint endpoint(tcp::v4(), atoi(argv[i])); + chat_server_ptr server(new chat_server(io_context, endpoint)); + servers.push_back(server); + } + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/chat/posix_chat_client.cpp b/src/boost/libs/asio/example/cpp03/chat/posix_chat_client.cpp new file mode 100644 index 000000000..85581ef6d --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/chat/posix_chat_client.cpp @@ -0,0 +1,204 @@ +// +// posix_chat_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <boost/array.hpp> +#include <boost/bind/bind.hpp> +#include <boost/asio.hpp> +#include "chat_message.hpp" + +#if defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + +using boost::asio::ip::tcp; +namespace posix = boost::asio::posix; + +class posix_chat_client +{ +public: + posix_chat_client(boost::asio::io_context& io_context, + const tcp::resolver::results_type& endpoints) + : socket_(io_context), + input_(io_context, ::dup(STDIN_FILENO)), + output_(io_context, ::dup(STDOUT_FILENO)), + input_buffer_(chat_message::max_body_length) + { + boost::asio::async_connect(socket_, endpoints, + boost::bind(&posix_chat_client::handle_connect, this, + boost::asio::placeholders::error)); + } + +private: + + void handle_connect(const boost::system::error_code& error) + { + if (!error) + { + // Read the fixed-length header of the next message from the server. + boost::asio::async_read(socket_, + boost::asio::buffer(read_msg_.data(), chat_message::header_length), + boost::bind(&posix_chat_client::handle_read_header, this, + boost::asio::placeholders::error)); + + // Read a line of input entered by the user. + boost::asio::async_read_until(input_, input_buffer_, '\n', + boost::bind(&posix_chat_client::handle_read_input, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + } + + void handle_read_header(const boost::system::error_code& error) + { + if (!error && read_msg_.decode_header()) + { + // Read the variable-length body of the message from the server. + boost::asio::async_read(socket_, + boost::asio::buffer(read_msg_.body(), read_msg_.body_length()), + boost::bind(&posix_chat_client::handle_read_body, this, + boost::asio::placeholders::error)); + } + else + { + close(); + } + } + + void handle_read_body(const boost::system::error_code& error) + { + if (!error) + { + // Write out the message we just received, terminated by a newline. + static char eol[] = { '\n' }; + boost::array<boost::asio::const_buffer, 2> buffers = {{ + boost::asio::buffer(read_msg_.body(), read_msg_.body_length()), + boost::asio::buffer(eol) }}; + boost::asio::async_write(output_, buffers, + boost::bind(&posix_chat_client::handle_write_output, this, + boost::asio::placeholders::error)); + } + else + { + close(); + } + } + + void handle_write_output(const boost::system::error_code& error) + { + if (!error) + { + // Read the fixed-length header of the next message from the server. + boost::asio::async_read(socket_, + boost::asio::buffer(read_msg_.data(), chat_message::header_length), + boost::bind(&posix_chat_client::handle_read_header, this, + boost::asio::placeholders::error)); + } + else + { + close(); + } + } + + void handle_read_input(const boost::system::error_code& error, + std::size_t length) + { + if (!error) + { + // Write the message (minus the newline) to the server. + write_msg_.body_length(length - 1); + input_buffer_.sgetn(write_msg_.body(), length - 1); + input_buffer_.consume(1); // Remove newline from input. + write_msg_.encode_header(); + boost::asio::async_write(socket_, + boost::asio::buffer(write_msg_.data(), write_msg_.length()), + boost::bind(&posix_chat_client::handle_write, this, + boost::asio::placeholders::error)); + } + else if (error == boost::asio::error::not_found) + { + // Didn't get a newline. Send whatever we have. + write_msg_.body_length(input_buffer_.size()); + input_buffer_.sgetn(write_msg_.body(), input_buffer_.size()); + write_msg_.encode_header(); + boost::asio::async_write(socket_, + boost::asio::buffer(write_msg_.data(), write_msg_.length()), + boost::bind(&posix_chat_client::handle_write, this, + boost::asio::placeholders::error)); + } + else + { + close(); + } + } + + void handle_write(const boost::system::error_code& error) + { + if (!error) + { + // Read a line of input entered by the user. + boost::asio::async_read_until(input_, input_buffer_, '\n', + boost::bind(&posix_chat_client::handle_read_input, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + else + { + close(); + } + } + + void close() + { + // Cancel all outstanding asynchronous operations. + socket_.close(); + input_.close(); + output_.close(); + } + +private: + tcp::socket socket_; + posix::stream_descriptor input_; + posix::stream_descriptor output_; + chat_message read_msg_; + chat_message write_msg_; + boost::asio::streambuf input_buffer_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: posix_chat_client <host> <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + tcp::resolver resolver(io_context); + tcp::resolver::results_type endpoints = resolver.resolve(argv[1], argv[2]); + + posix_chat_client c(io_context, endpoints); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} + +#else // defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR) +int main() {} +#endif // defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR) diff --git a/src/boost/libs/asio/example/cpp03/echo/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/echo/Jamfile.v2 new file mode 100644 index 000000000..6111d428e --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/echo/Jamfile.v2 @@ -0,0 +1,38 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/chrono//boost_chrono + <library>/boost/thread//boost_thread + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe async_tcp_echo_server : async_tcp_echo_server.cpp ; +exe async_udp_echo_server : async_udp_echo_server.cpp ; +exe blocking_tcp_echo_client : blocking_tcp_echo_client.cpp ; +exe blocking_tcp_echo_server : blocking_tcp_echo_server.cpp ; +exe blocking_udp_echo_client : blocking_udp_echo_client.cpp ; +exe blocking_udp_echo_server : blocking_udp_echo_server.cpp ; diff --git a/src/boost/libs/asio/example/cpp03/echo/async_tcp_echo_server.cpp b/src/boost/libs/asio/example/cpp03/echo/async_tcp_echo_server.cpp new file mode 100644 index 000000000..55c1cb064 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/echo/async_tcp_echo_server.cpp @@ -0,0 +1,137 @@ +// +// async_tcp_echo_server.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <iostream> +#include <boost/bind/bind.hpp> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +class session +{ +public: + session(boost::asio::io_context& io_context) + : socket_(io_context) + { + } + + tcp::socket& socket() + { + return socket_; + } + + void start() + { + socket_.async_read_some(boost::asio::buffer(data_, max_length), + boost::bind(&session::handle_read, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + +private: + void handle_read(const boost::system::error_code& error, + size_t bytes_transferred) + { + if (!error) + { + boost::asio::async_write(socket_, + boost::asio::buffer(data_, bytes_transferred), + boost::bind(&session::handle_write, this, + boost::asio::placeholders::error)); + } + else + { + delete this; + } + } + + void handle_write(const boost::system::error_code& error) + { + if (!error) + { + socket_.async_read_some(boost::asio::buffer(data_, max_length), + boost::bind(&session::handle_read, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + else + { + delete this; + } + } + + tcp::socket socket_; + enum { max_length = 1024 }; + char data_[max_length]; +}; + +class server +{ +public: + server(boost::asio::io_context& io_context, short port) + : io_context_(io_context), + acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) + { + start_accept(); + } + +private: + void start_accept() + { + session* new_session = new session(io_context_); + acceptor_.async_accept(new_session->socket(), + boost::bind(&server::handle_accept, this, new_session, + boost::asio::placeholders::error)); + } + + void handle_accept(session* new_session, + const boost::system::error_code& error) + { + if (!error) + { + new_session->start(); + } + else + { + delete new_session; + } + + start_accept(); + } + + boost::asio::io_context& io_context_; + tcp::acceptor acceptor_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: async_tcp_echo_server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + using namespace std; // For atoi. + server s(io_context, atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/echo/async_udp_echo_server.cpp b/src/boost/libs/asio/example/cpp03/echo/async_udp_echo_server.cpp new file mode 100644 index 000000000..e7d91134e --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/echo/async_udp_echo_server.cpp @@ -0,0 +1,92 @@ +// +// async_udp_echo_server.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <iostream> +#include <boost/bind/bind.hpp> +#include <boost/asio.hpp> + +using boost::asio::ip::udp; + +class server +{ +public: + server(boost::asio::io_context& io_context, short port) + : socket_(io_context, udp::endpoint(udp::v4(), port)) + { + socket_.async_receive_from( + boost::asio::buffer(data_, max_length), sender_endpoint_, + boost::bind(&server::handle_receive_from, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + + void handle_receive_from(const boost::system::error_code& error, + size_t bytes_recvd) + { + if (!error && bytes_recvd > 0) + { + socket_.async_send_to( + boost::asio::buffer(data_, bytes_recvd), sender_endpoint_, + boost::bind(&server::handle_send_to, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + else + { + socket_.async_receive_from( + boost::asio::buffer(data_, max_length), sender_endpoint_, + boost::bind(&server::handle_receive_from, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + } + + void handle_send_to(const boost::system::error_code& /*error*/, + size_t /*bytes_sent*/) + { + socket_.async_receive_from( + boost::asio::buffer(data_, max_length), sender_endpoint_, + boost::bind(&server::handle_receive_from, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + +private: + udp::socket socket_; + udp::endpoint sender_endpoint_; + enum { max_length = 1024 }; + char data_[max_length]; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: async_udp_echo_server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + using namespace std; // For atoi. + server s(io_context, atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/echo/blocking_tcp_echo_client.cpp b/src/boost/libs/asio/example/cpp03/echo/blocking_tcp_echo_client.cpp new file mode 100644 index 000000000..f97d8daaa --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/echo/blocking_tcp_echo_client.cpp @@ -0,0 +1,59 @@ +// +// blocking_tcp_echo_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +enum { max_length = 1024 }; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: blocking_tcp_echo_client <host> <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + tcp::resolver resolver(io_context); + tcp::resolver::results_type endpoints = + resolver.resolve(tcp::v4(), argv[1], argv[2]); + + tcp::socket s(io_context); + boost::asio::connect(s, endpoints); + + using namespace std; // For strlen. + std::cout << "Enter message: "; + char request[max_length]; + std::cin.getline(request, max_length); + size_t request_length = strlen(request); + boost::asio::write(s, boost::asio::buffer(request, request_length)); + + char reply[max_length]; + size_t reply_length = boost::asio::read(s, + boost::asio::buffer(reply, request_length)); + std::cout << "Reply is: "; + std::cout.write(reply, reply_length); + std::cout << "\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/echo/blocking_tcp_echo_server.cpp b/src/boost/libs/asio/example/cpp03/echo/blocking_tcp_echo_server.cpp new file mode 100644 index 000000000..b5b364378 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/echo/blocking_tcp_echo_server.cpp @@ -0,0 +1,80 @@ +// +// blocking_tcp_echo_server.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <iostream> +#include <boost/bind/bind.hpp> +#include <boost/smart_ptr.hpp> +#include <boost/asio.hpp> +#include <boost/thread/thread.hpp> + +using boost::asio::ip::tcp; + +const int max_length = 1024; + +typedef boost::shared_ptr<tcp::socket> socket_ptr; + +void session(socket_ptr sock) +{ + try + { + for (;;) + { + char data[max_length]; + + boost::system::error_code error; + size_t length = sock->read_some(boost::asio::buffer(data), error); + if (error == boost::asio::error::eof) + break; // Connection closed cleanly by peer. + else if (error) + throw boost::system::system_error(error); // Some other error. + + boost::asio::write(*sock, boost::asio::buffer(data, length)); + } + } + catch (std::exception& e) + { + std::cerr << "Exception in thread: " << e.what() << "\n"; + } +} + +void server(boost::asio::io_context& io_context, unsigned short port) +{ + tcp::acceptor a(io_context, tcp::endpoint(tcp::v4(), port)); + for (;;) + { + socket_ptr sock(new tcp::socket(io_context)); + a.accept(*sock); + boost::thread t(boost::bind(session, sock)); + } +} + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: blocking_tcp_echo_server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + using namespace std; // For atoi. + server(io_context, atoi(argv[1])); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/echo/blocking_udp_echo_client.cpp b/src/boost/libs/asio/example/cpp03/echo/blocking_udp_echo_client.cpp new file mode 100644 index 000000000..89afd03bc --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/echo/blocking_udp_echo_client.cpp @@ -0,0 +1,59 @@ +// +// blocking_udp_echo_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <boost/asio.hpp> + +using boost::asio::ip::udp; + +enum { max_length = 1024 }; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: blocking_udp_echo_client <host> <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + udp::socket s(io_context, udp::endpoint(udp::v4(), 0)); + + udp::resolver resolver(io_context); + udp::resolver::results_type endpoints = + resolver.resolve(udp::v4(), argv[1], argv[2]); + + using namespace std; // For strlen. + std::cout << "Enter message: "; + char request[max_length]; + std::cin.getline(request, max_length); + size_t request_length = strlen(request); + s.send_to(boost::asio::buffer(request, request_length), *endpoints.begin()); + + char reply[max_length]; + udp::endpoint sender_endpoint; + size_t reply_length = s.receive_from( + boost::asio::buffer(reply, max_length), sender_endpoint); + std::cout << "Reply is: "; + std::cout.write(reply, reply_length); + std::cout << "\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/echo/blocking_udp_echo_server.cpp b/src/boost/libs/asio/example/cpp03/echo/blocking_udp_echo_server.cpp new file mode 100644 index 000000000..0db7cd0e1 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/echo/blocking_udp_echo_server.cpp @@ -0,0 +1,53 @@ +// +// blocking_udp_echo_server.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <iostream> +#include <boost/asio.hpp> + +using boost::asio::ip::udp; + +enum { max_length = 1024 }; + +void server(boost::asio::io_context& io_context, unsigned short port) +{ + udp::socket sock(io_context, udp::endpoint(udp::v4(), port)); + for (;;) + { + char data[max_length]; + udp::endpoint sender_endpoint; + size_t length = sock.receive_from( + boost::asio::buffer(data, max_length), sender_endpoint); + sock.send_to(boost::asio::buffer(data, length), sender_endpoint); + } +} + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: blocking_udp_echo_server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + using namespace std; // For atoi. + server(io_context, atoi(argv[1])); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/fork/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/fork/Jamfile.v2 new file mode 100644 index 000000000..1d93fe89e --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/fork/Jamfile.v2 @@ -0,0 +1,46 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe daemon + : daemon.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe process_per_connection + : process_per_connection.cpp + /boost/system//boost_system + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/fork/daemon.cpp b/src/boost/libs/asio/example/cpp03/fork/daemon.cpp new file mode 100644 index 000000000..c69d3a805 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/fork/daemon.cpp @@ -0,0 +1,190 @@ +// +// daemon.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/udp.hpp> +#include <boost/asio/signal_set.hpp> +#include <boost/array.hpp> +#include <boost/bind/bind.hpp> +#include <ctime> +#include <iostream> +#include <syslog.h> +#include <unistd.h> + +using boost::asio::ip::udp; + +class udp_daytime_server +{ +public: + udp_daytime_server(boost::asio::io_context& io_context) + : socket_(io_context, udp::endpoint(udp::v4(), 13)) + { + start_receive(); + } + +private: + void start_receive() + { + socket_.async_receive_from( + boost::asio::buffer(recv_buffer_), remote_endpoint_, + boost::bind(&udp_daytime_server::handle_receive, + this, boost::placeholders::_1)); + } + + void handle_receive(const boost::system::error_code& ec) + { + if (!ec) + { + using namespace std; // For time_t, time and ctime; + time_t now = time(0); + std::string message = ctime(&now); + + boost::system::error_code ignored_ec; + socket_.send_to(boost::asio::buffer(message), + remote_endpoint_, 0, ignored_ec); + } + + start_receive(); + } + + udp::socket socket_; + udp::endpoint remote_endpoint_; + boost::array<char, 1> recv_buffer_; +}; + +int main() +{ + try + { + boost::asio::io_context io_context; + + // Initialise the server before becoming a daemon. If the process is + // started from a shell, this means any errors will be reported back to the + // user. + udp_daytime_server server(io_context); + + // Register signal handlers so that the daemon may be shut down. You may + // also want to register for other signals, such as SIGHUP to trigger a + // re-read of a configuration file. + boost::asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait( + boost::bind(&boost::asio::io_context::stop, &io_context)); + + // Inform the io_context that we are about to become a daemon. The + // io_context cleans up any internal resources, such as threads, that may + // interfere with forking. + io_context.notify_fork(boost::asio::io_context::fork_prepare); + + // Fork the process and have the parent exit. If the process was started + // from a shell, this returns control to the user. Forking a new process is + // also a prerequisite for the subsequent call to setsid(). + if (pid_t pid = fork()) + { + if (pid > 0) + { + // We're in the parent process and need to exit. + // + // When the exit() function is used, the program terminates without + // invoking local variables' destructors. Only global variables are + // destroyed. As the io_context object is a local variable, this means + // we do not have to call: + // + // io_context.notify_fork(boost::asio::io_context::fork_parent); + // + // However, this line should be added before each call to exit() if + // using a global io_context object. An additional call: + // + // io_context.notify_fork(boost::asio::io_context::fork_prepare); + // + // should also precede the second fork(). + exit(0); + } + else + { + syslog(LOG_ERR | LOG_USER, "First fork failed: %m"); + return 1; + } + } + + // Make the process a new session leader. This detaches it from the + // terminal. + setsid(); + + // A process inherits its working directory from its parent. This could be + // on a mounted filesystem, which means that the running daemon would + // prevent this filesystem from being unmounted. Changing to the root + // directory avoids this problem. + chdir("/"); + + // The file mode creation mask is also inherited from the parent process. + // We don't want to restrict the permissions on files created by the + // daemon, so the mask is cleared. + umask(0); + + // A second fork ensures the process cannot acquire a controlling terminal. + if (pid_t pid = fork()) + { + if (pid > 0) + { + exit(0); + } + else + { + syslog(LOG_ERR | LOG_USER, "Second fork failed: %m"); + return 1; + } + } + + // Close the standard streams. This decouples the daemon from the terminal + // that started it. + close(0); + close(1); + close(2); + + // We don't want the daemon to have any standard input. + if (open("/dev/null", O_RDONLY) < 0) + { + syslog(LOG_ERR | LOG_USER, "Unable to open /dev/null: %m"); + return 1; + } + + // Send standard output to a log file. + const char* output = "/tmp/asio.daemon.out"; + const int flags = O_WRONLY | O_CREAT | O_APPEND; + const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + if (open(output, flags, mode) < 0) + { + syslog(LOG_ERR | LOG_USER, "Unable to open output file %s: %m", output); + return 1; + } + + // Also send standard error to the same log file. + if (dup(1) < 0) + { + syslog(LOG_ERR | LOG_USER, "Unable to dup output descriptor: %m"); + return 1; + } + + // Inform the io_context that we have finished becoming a daemon. The + // io_context uses this opportunity to create any internal file descriptors + // that need to be private to the new process. + io_context.notify_fork(boost::asio::io_context::fork_child); + + // The io_context can now be used normally. + syslog(LOG_INFO | LOG_USER, "Daemon started"); + io_context.run(); + syslog(LOG_INFO | LOG_USER, "Daemon stopped"); + } + catch (std::exception& e) + { + syslog(LOG_ERR | LOG_USER, "Exception: %s", e.what()); + std::cerr << "Exception: " << e.what() << std::endl; + } +} diff --git a/src/boost/libs/asio/example/cpp03/fork/process_per_connection.cpp b/src/boost/libs/asio/example/cpp03/fork/process_per_connection.cpp new file mode 100644 index 000000000..f7f947222 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/fork/process_per_connection.cpp @@ -0,0 +1,161 @@ +// +// process_per_connection.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/signal_set.hpp> +#include <boost/asio/write.hpp> +#include <boost/array.hpp> +#include <boost/bind/bind.hpp> +#include <cstdlib> +#include <iostream> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +using boost::asio::ip::tcp; + +class server +{ +public: + server(boost::asio::io_context& io_context, unsigned short port) + : io_context_(io_context), + signal_(io_context, SIGCHLD), + acceptor_(io_context, tcp::endpoint(tcp::v4(), port)), + socket_(io_context) + { + start_signal_wait(); + start_accept(); + } + +private: + void start_signal_wait() + { + signal_.async_wait(boost::bind(&server::handle_signal_wait, this)); + } + + void handle_signal_wait() + { + // Only the parent process should check for this signal. We can determine + // whether we are in the parent by checking if the acceptor is still open. + if (acceptor_.is_open()) + { + // Reap completed child processes so that we don't end up with zombies. + int status = 0; + while (waitpid(-1, &status, WNOHANG) > 0) {} + + start_signal_wait(); + } + } + + void start_accept() + { + acceptor_.async_accept(socket_, + boost::bind(&server::handle_accept, this, boost::placeholders::_1)); + } + + void handle_accept(const boost::system::error_code& ec) + { + if (!ec) + { + // Inform the io_context that we are about to fork. The io_context cleans + // up any internal resources, such as threads, that may interfere with + // forking. + io_context_.notify_fork(boost::asio::io_context::fork_prepare); + + if (fork() == 0) + { + // Inform the io_context that the fork is finished and that this is the + // child process. The io_context uses this opportunity to create any + // internal file descriptors that must be private to the new process. + io_context_.notify_fork(boost::asio::io_context::fork_child); + + // The child won't be accepting new connections, so we can close the + // acceptor. It remains open in the parent. + acceptor_.close(); + + // The child process is not interested in processing the SIGCHLD signal. + signal_.cancel(); + + start_read(); + } + else + { + // Inform the io_context that the fork is finished (or failed) and that + // this is the parent process. The io_context uses this opportunity to + // recreate any internal resources that were cleaned up during + // preparation for the fork. + io_context_.notify_fork(boost::asio::io_context::fork_parent); + + socket_.close(); + start_accept(); + } + } + else + { + std::cerr << "Accept error: " << ec.message() << std::endl; + start_accept(); + } + } + + void start_read() + { + socket_.async_read_some(boost::asio::buffer(data_), + boost::bind(&server::handle_read, this, + boost::placeholders::_1, boost::placeholders::_2)); + } + + void handle_read(const boost::system::error_code& ec, std::size_t length) + { + if (!ec) + start_write(length); + } + + void start_write(std::size_t length) + { + boost::asio::async_write(socket_, boost::asio::buffer(data_, length), + boost::bind(&server::handle_write, this, boost::placeholders::_1)); + } + + void handle_write(const boost::system::error_code& ec) + { + if (!ec) + start_read(); + } + + boost::asio::io_context& io_context_; + boost::asio::signal_set signal_; + tcp::acceptor acceptor_; + tcp::socket socket_; + boost::array<char, 1024> data_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: process_per_connection <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + using namespace std; // For atoi. + server s(io_context, atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + } +} diff --git a/src/boost/libs/asio/example/cpp03/http/client/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/http/client/Jamfile.v2 new file mode 100644 index 000000000..eedb4068f --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/client/Jamfile.v2 @@ -0,0 +1,33 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/chrono//boost_chrono + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe async_client : async_client.cpp ; +exe sync_client : sync_client.cpp ; diff --git a/src/boost/libs/asio/example/cpp03/http/client/async_client.cpp b/src/boost/libs/asio/example/cpp03/http/client/async_client.cpp new file mode 100644 index 000000000..e9bf6baf5 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/client/async_client.cpp @@ -0,0 +1,204 @@ +// +// async_client.cpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <istream> +#include <ostream> +#include <string> +#include <boost/asio.hpp> +#include <boost/bind/bind.hpp> + +using boost::asio::ip::tcp; + +class client +{ +public: + client(boost::asio::io_context& io_context, + const std::string& server, const std::string& path) + : resolver_(io_context), + socket_(io_context) + { + // Form the request. We specify the "Connection: close" header so that the + // server will close the socket after transmitting the response. This will + // allow us to treat all data up until the EOF as the content. + std::ostream request_stream(&request_); + request_stream << "GET " << path << " HTTP/1.0\r\n"; + request_stream << "Host: " << server << "\r\n"; + request_stream << "Accept: */*\r\n"; + request_stream << "Connection: close\r\n\r\n"; + + // Start an asynchronous resolve to translate the server and service names + // into a list of endpoints. + resolver_.async_resolve(server, "http", + boost::bind(&client::handle_resolve, this, + boost::asio::placeholders::error, + boost::asio::placeholders::results)); + } + +private: + void handle_resolve(const boost::system::error_code& err, + const tcp::resolver::results_type& endpoints) + { + if (!err) + { + // Attempt a connection to each endpoint in the list until we + // successfully establish a connection. + boost::asio::async_connect(socket_, endpoints, + boost::bind(&client::handle_connect, this, + boost::asio::placeholders::error)); + } + else + { + std::cout << "Error: " << err.message() << "\n"; + } + } + + void handle_connect(const boost::system::error_code& err) + { + if (!err) + { + // The connection was successful. Send the request. + boost::asio::async_write(socket_, request_, + boost::bind(&client::handle_write_request, this, + boost::asio::placeholders::error)); + } + else + { + std::cout << "Error: " << err.message() << "\n"; + } + } + + void handle_write_request(const boost::system::error_code& err) + { + if (!err) + { + // Read the response status line. The response_ streambuf will + // automatically grow to accommodate the entire line. The growth may be + // limited by passing a maximum size to the streambuf constructor. + boost::asio::async_read_until(socket_, response_, "\r\n", + boost::bind(&client::handle_read_status_line, this, + boost::asio::placeholders::error)); + } + else + { + std::cout << "Error: " << err.message() << "\n"; + } + } + + void handle_read_status_line(const boost::system::error_code& err) + { + if (!err) + { + // Check that response is OK. + std::istream response_stream(&response_); + std::string http_version; + response_stream >> http_version; + unsigned int status_code; + response_stream >> status_code; + std::string status_message; + std::getline(response_stream, status_message); + if (!response_stream || http_version.substr(0, 5) != "HTTP/") + { + std::cout << "Invalid response\n"; + return; + } + if (status_code != 200) + { + std::cout << "Response returned with status code "; + std::cout << status_code << "\n"; + return; + } + + // Read the response headers, which are terminated by a blank line. + boost::asio::async_read_until(socket_, response_, "\r\n\r\n", + boost::bind(&client::handle_read_headers, this, + boost::asio::placeholders::error)); + } + else + { + std::cout << "Error: " << err << "\n"; + } + } + + void handle_read_headers(const boost::system::error_code& err) + { + if (!err) + { + // Process the response headers. + std::istream response_stream(&response_); + std::string header; + while (std::getline(response_stream, header) && header != "\r") + std::cout << header << "\n"; + std::cout << "\n"; + + // Write whatever content we already have to output. + if (response_.size() > 0) + std::cout << &response_; + + // Start reading remaining data until EOF. + boost::asio::async_read(socket_, response_, + boost::asio::transfer_at_least(1), + boost::bind(&client::handle_read_content, this, + boost::asio::placeholders::error)); + } + else + { + std::cout << "Error: " << err << "\n"; + } + } + + void handle_read_content(const boost::system::error_code& err) + { + if (!err) + { + // Write all of the data that has been read so far. + std::cout << &response_; + + // Continue reading remaining data until EOF. + boost::asio::async_read(socket_, response_, + boost::asio::transfer_at_least(1), + boost::bind(&client::handle_read_content, this, + boost::asio::placeholders::error)); + } + else if (err != boost::asio::error::eof) + { + std::cout << "Error: " << err << "\n"; + } + } + + tcp::resolver resolver_; + tcp::socket socket_; + boost::asio::streambuf request_; + boost::asio::streambuf response_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cout << "Usage: async_client <server> <path>\n"; + std::cout << "Example:\n"; + std::cout << " async_client www.boost.org /LICENSE_1_0.txt\n"; + return 1; + } + + boost::asio::io_context io_context; + client c(io_context, argv[1], argv[2]); + io_context.run(); + } + catch (std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/http/client/sync_client.cpp b/src/boost/libs/asio/example/cpp03/http/client/sync_client.cpp new file mode 100644 index 000000000..a3b6a5c61 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/client/sync_client.cpp @@ -0,0 +1,106 @@ +// +// sync_client.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <istream> +#include <ostream> +#include <string> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cout << "Usage: sync_client <server> <path>\n"; + std::cout << "Example:\n"; + std::cout << " sync_client www.boost.org /LICENSE_1_0.txt\n"; + return 1; + } + + boost::asio::io_context io_context; + + // Get a list of endpoints corresponding to the server name. + tcp::resolver resolver(io_context); + tcp::resolver::results_type endpoints = resolver.resolve(argv[1], "http"); + + // Try each endpoint until we successfully establish a connection. + tcp::socket socket(io_context); + boost::asio::connect(socket, endpoints); + + // Form the request. We specify the "Connection: close" header so that the + // server will close the socket after transmitting the response. This will + // allow us to treat all data up until the EOF as the content. + boost::asio::streambuf request; + std::ostream request_stream(&request); + request_stream << "GET " << argv[2] << " HTTP/1.0\r\n"; + request_stream << "Host: " << argv[1] << "\r\n"; + request_stream << "Accept: */*\r\n"; + request_stream << "Connection: close\r\n\r\n"; + + // Send the request. + boost::asio::write(socket, request); + + // Read the response status line. The response streambuf will automatically + // grow to accommodate the entire line. The growth may be limited by passing + // a maximum size to the streambuf constructor. + boost::asio::streambuf response; + boost::asio::read_until(socket, response, "\r\n"); + + // Check that response is OK. + std::istream response_stream(&response); + std::string http_version; + response_stream >> http_version; + unsigned int status_code; + response_stream >> status_code; + std::string status_message; + std::getline(response_stream, status_message); + if (!response_stream || http_version.substr(0, 5) != "HTTP/") + { + std::cout << "Invalid response\n"; + return 1; + } + if (status_code != 200) + { + std::cout << "Response returned with status code " << status_code << "\n"; + return 1; + } + + // Read the response headers, which are terminated by a blank line. + boost::asio::read_until(socket, response, "\r\n\r\n"); + + // Process the response headers. + std::string header; + while (std::getline(response_stream, header) && header != "\r") + std::cout << header << "\n"; + std::cout << "\n"; + + // Write whatever content we already have to output. + if (response.size() > 0) + std::cout << &response; + + // Read until EOF, writing data to output as we go. + boost::system::error_code error; + while (boost::asio::read(socket, response, + boost::asio::transfer_at_least(1), error)) + std::cout << &response; + if (error != boost::asio::error::eof) + throw boost::system::system_error(error); + } + catch (std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/http/doc_root/data_1K.html b/src/boost/libs/asio/example/cpp03/http/doc_root/data_1K.html new file mode 100644 index 000000000..80e07f009 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/doc_root/data_1K.html @@ -0,0 +1,28 @@ +<!-- +Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +--> + +<html> +<body> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the<br/> +</body> +</html> + +<!-- boostinspect:nounlinked --> diff --git a/src/boost/libs/asio/example/cpp03/http/doc_root/data_2K.html b/src/boost/libs/asio/example/cpp03/http/doc_root/data_2K.html new file mode 100644 index 000000000..0584cbe48 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/doc_root/data_2K.html @@ -0,0 +1,49 @@ +<!-- +Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +--> + +<html> +<body> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps ove<br/> +</body> +</html> + +<!-- boostinspect:nounlinked --> diff --git a/src/boost/libs/asio/example/cpp03/http/doc_root/data_4K.html b/src/boost/libs/asio/example/cpp03/http/doc_root/data_4K.html new file mode 100644 index 000000000..4c35214d3 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/doc_root/data_4K.html @@ -0,0 +1,91 @@ +<!-- +Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +--> + +<html> +<body> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox<br/> +</body> +</html> + +<!-- boostinspect:nounlinked --> diff --git a/src/boost/libs/asio/example/cpp03/http/doc_root/data_8K.html b/src/boost/libs/asio/example/cpp03/http/doc_root/data_8K.html new file mode 100644 index 000000000..fb5624902 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/doc_root/data_8K.html @@ -0,0 +1,175 @@ +<!-- +Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +--> + +<html> +<body> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +.... +</body> +</html> + +<!-- boostinspect:nounlinked --> diff --git a/src/boost/libs/asio/example/cpp03/http/server/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/http/server/Jamfile.v2 new file mode 100644 index 000000000..ed1e3e8ee --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/Jamfile.v2 @@ -0,0 +1,38 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe server + : connection.cpp + connection_manager.cpp + main.cpp + mime_types.cpp + reply.cpp + request_handler.cpp + request_parser.cpp + server.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + /boost/thread//boost_thread + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/http/server/connection.cpp b/src/boost/libs/asio/example/cpp03/http/server/connection.cpp new file mode 100644 index 000000000..9bc9a0780 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/connection.cpp @@ -0,0 +1,99 @@ +// +// connection.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "connection.hpp" +#include <vector> +#include <boost/bind/bind.hpp> +#include "connection_manager.hpp" +#include "request_handler.hpp" + +namespace http { +namespace server { + +connection::connection(boost::asio::io_context& io_context, + connection_manager& manager, request_handler& handler) + : socket_(io_context), + connection_manager_(manager), + request_handler_(handler) +{ +} + +boost::asio::ip::tcp::socket& connection::socket() +{ + return socket_; +} + +void connection::start() +{ + socket_.async_read_some(boost::asio::buffer(buffer_), + boost::bind(&connection::handle_read, shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); +} + +void connection::stop() +{ + socket_.close(); +} + +void connection::handle_read(const boost::system::error_code& e, + std::size_t bytes_transferred) +{ + if (!e) + { + boost::tribool result; + boost::tie(result, boost::tuples::ignore) = request_parser_.parse( + request_, buffer_.data(), buffer_.data() + bytes_transferred); + + if (result) + { + request_handler_.handle_request(request_, reply_); + boost::asio::async_write(socket_, reply_.to_buffers(), + boost::bind(&connection::handle_write, shared_from_this(), + boost::asio::placeholders::error)); + } + else if (!result) + { + reply_ = reply::stock_reply(reply::bad_request); + boost::asio::async_write(socket_, reply_.to_buffers(), + boost::bind(&connection::handle_write, shared_from_this(), + boost::asio::placeholders::error)); + } + else + { + socket_.async_read_some(boost::asio::buffer(buffer_), + boost::bind(&connection::handle_read, shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + } + else if (e != boost::asio::error::operation_aborted) + { + connection_manager_.stop(shared_from_this()); + } +} + +void connection::handle_write(const boost::system::error_code& e) +{ + if (!e) + { + // Initiate graceful connection closure. + boost::system::error_code ignored_ec; + socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); + } + + if (e != boost::asio::error::operation_aborted) + { + connection_manager_.stop(shared_from_this()); + } +} + +} // namespace server +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server/connection.hpp b/src/boost/libs/asio/example/cpp03/http/server/connection.hpp new file mode 100644 index 000000000..a701bc68f --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/connection.hpp @@ -0,0 +1,83 @@ +// +// connection.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_CONNECTION_HPP +#define HTTP_CONNECTION_HPP + +#include <boost/asio.hpp> +#include <boost/array.hpp> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include "reply.hpp" +#include "request.hpp" +#include "request_handler.hpp" +#include "request_parser.hpp" + +namespace http { +namespace server { + +class connection_manager; + +/// Represents a single connection from a client. +class connection + : public boost::enable_shared_from_this<connection>, + private boost::noncopyable +{ +public: + /// Construct a connection with the given io_context. + explicit connection(boost::asio::io_context& io_context, + connection_manager& manager, request_handler& handler); + + /// Get the socket associated with the connection. + boost::asio::ip::tcp::socket& socket(); + + /// Start the first asynchronous operation for the connection. + void start(); + + /// Stop all asynchronous operations associated with the connection. + void stop(); + +private: + /// Handle completion of a read operation. + void handle_read(const boost::system::error_code& e, + std::size_t bytes_transferred); + + /// Handle completion of a write operation. + void handle_write(const boost::system::error_code& e); + + /// Socket for the connection. + boost::asio::ip::tcp::socket socket_; + + /// The manager for this connection. + connection_manager& connection_manager_; + + /// The handler used to process the incoming request. + request_handler& request_handler_; + + /// Buffer for incoming data. + boost::array<char, 8192> buffer_; + + /// The incoming request. + request request_; + + /// The parser for the incoming request. + request_parser request_parser_; + + /// The reply to be sent back to the client. + reply reply_; +}; + +typedef boost::shared_ptr<connection> connection_ptr; + +} // namespace server +} // namespace http + +#endif // HTTP_CONNECTION_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server/connection_manager.cpp b/src/boost/libs/asio/example/cpp03/http/server/connection_manager.cpp new file mode 100644 index 000000000..57a509d26 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/connection_manager.cpp @@ -0,0 +1,38 @@ +// +// connection_manager.cpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "connection_manager.hpp" +#include <algorithm> +#include <boost/bind/bind.hpp> + +namespace http { +namespace server { + +void connection_manager::start(connection_ptr c) +{ + connections_.insert(c); + c->start(); +} + +void connection_manager::stop(connection_ptr c) +{ + connections_.erase(c); + c->stop(); +} + +void connection_manager::stop_all() +{ + std::for_each(connections_.begin(), connections_.end(), + boost::bind(&connection::stop, boost::placeholders::_1)); + connections_.clear(); +} + +} // namespace server +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server/connection_manager.hpp b/src/boost/libs/asio/example/cpp03/http/server/connection_manager.hpp new file mode 100644 index 000000000..48c90711f --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/connection_manager.hpp @@ -0,0 +1,44 @@ +// +// connection_manager.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_CONNECTION_MANAGER_HPP +#define HTTP_CONNECTION_MANAGER_HPP + +#include <set> +#include <boost/noncopyable.hpp> +#include "connection.hpp" + +namespace http { +namespace server { + +/// Manages open connections so that they may be cleanly stopped when the server +/// needs to shut down. +class connection_manager + : private boost::noncopyable +{ +public: + /// Add the specified connection to the manager and start it. + void start(connection_ptr c); + + /// Stop the specified connection. + void stop(connection_ptr c); + + /// Stop all connections. + void stop_all(); + +private: + /// The managed connections. + std::set<connection_ptr> connections_; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_CONNECTION_MANAGER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server/header.hpp b/src/boost/libs/asio/example/cpp03/http/server/header.hpp new file mode 100644 index 000000000..7e176c3b0 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/header.hpp @@ -0,0 +1,28 @@ +// +// header.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_HEADER_HPP +#define HTTP_HEADER_HPP + +#include <string> + +namespace http { +namespace server { + +struct header +{ + std::string name; + std::string value; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_HEADER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server/main.cpp b/src/boost/libs/asio/example/cpp03/http/server/main.cpp new file mode 100644 index 000000000..a073a7d4d --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/main.cpp @@ -0,0 +1,44 @@ +// +// main.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <string> +#include <boost/asio.hpp> +#include <boost/bind/bind.hpp> +#include "server.hpp" + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 4) + { + std::cerr << "Usage: http_server <address> <port> <doc_root>\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " receiver 0.0.0.0 80 .\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " receiver 0::0 80 .\n"; + return 1; + } + + // Initialise the server. + http::server::server s(argv[1], argv[2], argv[3]); + + // Run the server until stopped. + s.run(); + } + catch (std::exception& e) + { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/http/server/mime_types.cpp b/src/boost/libs/asio/example/cpp03/http/server/mime_types.cpp new file mode 100644 index 000000000..3964af74c --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/mime_types.cpp @@ -0,0 +1,46 @@ +// +// mime_types.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "mime_types.hpp" + +namespace http { +namespace server { +namespace mime_types { + +struct mapping +{ + const char* extension; + const char* mime_type; +} mappings[] = +{ + { "gif", "image/gif" }, + { "htm", "text/html" }, + { "html", "text/html" }, + { "jpg", "image/jpeg" }, + { "png", "image/png" }, + { 0, 0 } // Marks end of list. +}; + +std::string extension_to_type(const std::string& extension) +{ + for (mapping* m = mappings; m->extension; ++m) + { + if (m->extension == extension) + { + return m->mime_type; + } + } + + return "text/plain"; +} + +} // namespace mime_types +} // namespace server +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server/mime_types.hpp b/src/boost/libs/asio/example/cpp03/http/server/mime_types.hpp new file mode 100644 index 000000000..e9bb88268 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/mime_types.hpp @@ -0,0 +1,27 @@ +// +// mime_types.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_MIME_TYPES_HPP +#define HTTP_MIME_TYPES_HPP + +#include <string> + +namespace http { +namespace server { +namespace mime_types { + +/// Convert a file extension into a MIME type. +std::string extension_to_type(const std::string& extension); + +} // namespace mime_types +} // namespace server +} // namespace http + +#endif // HTTP_MIME_TYPES_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server/reply.cpp b/src/boost/libs/asio/example/cpp03/http/server/reply.cpp new file mode 100644 index 000000000..59eef1cb5 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/reply.cpp @@ -0,0 +1,256 @@ +// +// reply.cpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "reply.hpp" +#include <string> +#include <boost/lexical_cast.hpp> + +namespace http { +namespace server { + +namespace status_strings { + +const std::string ok = + "HTTP/1.0 200 OK\r\n"; +const std::string created = + "HTTP/1.0 201 Created\r\n"; +const std::string accepted = + "HTTP/1.0 202 Accepted\r\n"; +const std::string no_content = + "HTTP/1.0 204 No Content\r\n"; +const std::string multiple_choices = + "HTTP/1.0 300 Multiple Choices\r\n"; +const std::string moved_permanently = + "HTTP/1.0 301 Moved Permanently\r\n"; +const std::string moved_temporarily = + "HTTP/1.0 302 Moved Temporarily\r\n"; +const std::string not_modified = + "HTTP/1.0 304 Not Modified\r\n"; +const std::string bad_request = + "HTTP/1.0 400 Bad Request\r\n"; +const std::string unauthorized = + "HTTP/1.0 401 Unauthorized\r\n"; +const std::string forbidden = + "HTTP/1.0 403 Forbidden\r\n"; +const std::string not_found = + "HTTP/1.0 404 Not Found\r\n"; +const std::string internal_server_error = + "HTTP/1.0 500 Internal Server Error\r\n"; +const std::string not_implemented = + "HTTP/1.0 501 Not Implemented\r\n"; +const std::string bad_gateway = + "HTTP/1.0 502 Bad Gateway\r\n"; +const std::string service_unavailable = + "HTTP/1.0 503 Service Unavailable\r\n"; + +boost::asio::const_buffer to_buffer(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return boost::asio::buffer(ok); + case reply::created: + return boost::asio::buffer(created); + case reply::accepted: + return boost::asio::buffer(accepted); + case reply::no_content: + return boost::asio::buffer(no_content); + case reply::multiple_choices: + return boost::asio::buffer(multiple_choices); + case reply::moved_permanently: + return boost::asio::buffer(moved_permanently); + case reply::moved_temporarily: + return boost::asio::buffer(moved_temporarily); + case reply::not_modified: + return boost::asio::buffer(not_modified); + case reply::bad_request: + return boost::asio::buffer(bad_request); + case reply::unauthorized: + return boost::asio::buffer(unauthorized); + case reply::forbidden: + return boost::asio::buffer(forbidden); + case reply::not_found: + return boost::asio::buffer(not_found); + case reply::internal_server_error: + return boost::asio::buffer(internal_server_error); + case reply::not_implemented: + return boost::asio::buffer(not_implemented); + case reply::bad_gateway: + return boost::asio::buffer(bad_gateway); + case reply::service_unavailable: + return boost::asio::buffer(service_unavailable); + default: + return boost::asio::buffer(internal_server_error); + } +} + +} // namespace status_strings + +namespace misc_strings { + +const char name_value_separator[] = { ':', ' ' }; +const char crlf[] = { '\r', '\n' }; + +} // namespace misc_strings + +std::vector<boost::asio::const_buffer> reply::to_buffers() +{ + std::vector<boost::asio::const_buffer> buffers; + buffers.push_back(status_strings::to_buffer(status)); + for (std::size_t i = 0; i < headers.size(); ++i) + { + header& h = headers[i]; + buffers.push_back(boost::asio::buffer(h.name)); + buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator)); + buffers.push_back(boost::asio::buffer(h.value)); + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + } + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + buffers.push_back(boost::asio::buffer(content)); + return buffers; +} + +namespace stock_replies { + +const char ok[] = ""; +const char created[] = + "<html>" + "<head><title>Created</title></head>" + "<body><h1>201 Created</h1></body>" + "</html>"; +const char accepted[] = + "<html>" + "<head><title>Accepted</title></head>" + "<body><h1>202 Accepted</h1></body>" + "</html>"; +const char no_content[] = + "<html>" + "<head><title>No Content</title></head>" + "<body><h1>204 Content</h1></body>" + "</html>"; +const char multiple_choices[] = + "<html>" + "<head><title>Multiple Choices</title></head>" + "<body><h1>300 Multiple Choices</h1></body>" + "</html>"; +const char moved_permanently[] = + "<html>" + "<head><title>Moved Permanently</title></head>" + "<body><h1>301 Moved Permanently</h1></body>" + "</html>"; +const char moved_temporarily[] = + "<html>" + "<head><title>Moved Temporarily</title></head>" + "<body><h1>302 Moved Temporarily</h1></body>" + "</html>"; +const char not_modified[] = + "<html>" + "<head><title>Not Modified</title></head>" + "<body><h1>304 Not Modified</h1></body>" + "</html>"; +const char bad_request[] = + "<html>" + "<head><title>Bad Request</title></head>" + "<body><h1>400 Bad Request</h1></body>" + "</html>"; +const char unauthorized[] = + "<html>" + "<head><title>Unauthorized</title></head>" + "<body><h1>401 Unauthorized</h1></body>" + "</html>"; +const char forbidden[] = + "<html>" + "<head><title>Forbidden</title></head>" + "<body><h1>403 Forbidden</h1></body>" + "</html>"; +const char not_found[] = + "<html>" + "<head><title>Not Found</title></head>" + "<body><h1>404 Not Found</h1></body>" + "</html>"; +const char internal_server_error[] = + "<html>" + "<head><title>Internal Server Error</title></head>" + "<body><h1>500 Internal Server Error</h1></body>" + "</html>"; +const char not_implemented[] = + "<html>" + "<head><title>Not Implemented</title></head>" + "<body><h1>501 Not Implemented</h1></body>" + "</html>"; +const char bad_gateway[] = + "<html>" + "<head><title>Bad Gateway</title></head>" + "<body><h1>502 Bad Gateway</h1></body>" + "</html>"; +const char service_unavailable[] = + "<html>" + "<head><title>Service Unavailable</title></head>" + "<body><h1>503 Service Unavailable</h1></body>" + "</html>"; + +std::string to_string(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return ok; + case reply::created: + return created; + case reply::accepted: + return accepted; + case reply::no_content: + return no_content; + case reply::multiple_choices: + return multiple_choices; + case reply::moved_permanently: + return moved_permanently; + case reply::moved_temporarily: + return moved_temporarily; + case reply::not_modified: + return not_modified; + case reply::bad_request: + return bad_request; + case reply::unauthorized: + return unauthorized; + case reply::forbidden: + return forbidden; + case reply::not_found: + return not_found; + case reply::internal_server_error: + return internal_server_error; + case reply::not_implemented: + return not_implemented; + case reply::bad_gateway: + return bad_gateway; + case reply::service_unavailable: + return service_unavailable; + default: + return internal_server_error; + } +} + +} // namespace stock_replies + +reply reply::stock_reply(reply::status_type status) +{ + reply rep; + rep.status = status; + rep.content = stock_replies::to_string(status); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = "text/html"; + return rep; +} + +} // namespace server +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server/reply.hpp b/src/boost/libs/asio/example/cpp03/http/server/reply.hpp new file mode 100644 index 000000000..6a3240e2d --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/reply.hpp @@ -0,0 +1,64 @@ +// +// reply.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_REPLY_HPP +#define HTTP_REPLY_HPP + +#include <string> +#include <vector> +#include <boost/asio.hpp> +#include "header.hpp" + +namespace http { +namespace server { + +/// A reply to be sent to a client. +struct reply +{ + /// The status of the reply. + enum status_type + { + ok = 200, + created = 201, + accepted = 202, + no_content = 204, + multiple_choices = 300, + moved_permanently = 301, + moved_temporarily = 302, + not_modified = 304, + bad_request = 400, + unauthorized = 401, + forbidden = 403, + not_found = 404, + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503 + } status; + + /// The headers to be included in the reply. + std::vector<header> headers; + + /// The content to be sent in the reply. + std::string content; + + /// Convert the reply into a vector of buffers. The buffers do not own the + /// underlying memory blocks, therefore the reply object must remain valid and + /// not be changed until the write operation has completed. + std::vector<boost::asio::const_buffer> to_buffers(); + + /// Get a stock reply. + static reply stock_reply(status_type status); +}; + +} // namespace server +} // namespace http + +#endif // HTTP_REPLY_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server/request.hpp b/src/boost/libs/asio/example/cpp03/http/server/request.hpp new file mode 100644 index 000000000..358133512 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/request.hpp @@ -0,0 +1,34 @@ +// +// request.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_REQUEST_HPP +#define HTTP_REQUEST_HPP + +#include <string> +#include <vector> +#include "header.hpp" + +namespace http { +namespace server { + +/// A request received from a client. +struct request +{ + std::string method; + std::string uri; + int http_version_major; + int http_version_minor; + std::vector<header> headers; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_REQUEST_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server/request_handler.cpp b/src/boost/libs/asio/example/cpp03/http/server/request_handler.cpp new file mode 100644 index 000000000..2e38447b3 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/request_handler.cpp @@ -0,0 +1,122 @@ +// +// request_handler.cpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "request_handler.hpp" +#include <fstream> +#include <sstream> +#include <string> +#include <boost/lexical_cast.hpp> +#include "mime_types.hpp" +#include "reply.hpp" +#include "request.hpp" + +namespace http { +namespace server { + +request_handler::request_handler(const std::string& doc_root) + : doc_root_(doc_root) +{ +} + +void request_handler::handle_request(const request& req, reply& rep) +{ + // Decode url to path. + std::string request_path; + if (!url_decode(req.uri, request_path)) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // Request path must be absolute and not contain "..". + if (request_path.empty() || request_path[0] != '/' + || request_path.find("..") != std::string::npos) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // If path ends in slash (i.e. is a directory) then add "index.html". + if (request_path[request_path.size() - 1] == '/') + { + request_path += "index.html"; + } + + // Determine the file extension. + std::size_t last_slash_pos = request_path.find_last_of("/"); + std::size_t last_dot_pos = request_path.find_last_of("."); + std::string extension; + if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos) + { + extension = request_path.substr(last_dot_pos + 1); + } + + // Open the file to send back. + std::string full_path = doc_root_ + request_path; + std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary); + if (!is) + { + rep = reply::stock_reply(reply::not_found); + return; + } + + // Fill out the reply to be sent to the client. + rep.status = reply::ok; + char buf[512]; + while (is.read(buf, sizeof(buf)).gcount() > 0) + rep.content.append(buf, is.gcount()); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = mime_types::extension_to_type(extension); +} + +bool request_handler::url_decode(const std::string& in, std::string& out) +{ + out.clear(); + out.reserve(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) + { + if (in[i] == '%') + { + if (i + 3 <= in.size()) + { + int value = 0; + std::istringstream is(in.substr(i + 1, 2)); + if (is >> std::hex >> value) + { + out += static_cast<char>(value); + i += 2; + } + else + { + return false; + } + } + else + { + return false; + } + } + else if (in[i] == '+') + { + out += ' '; + } + else + { + out += in[i]; + } + } + return true; +} + +} // namespace server +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server/request_handler.hpp b/src/boost/libs/asio/example/cpp03/http/server/request_handler.hpp new file mode 100644 index 000000000..2f542a1e4 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/request_handler.hpp @@ -0,0 +1,46 @@ +// +// request_handler.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_REQUEST_HANDLER_HPP +#define HTTP_REQUEST_HANDLER_HPP + +#include <string> +#include <boost/noncopyable.hpp> + +namespace http { +namespace server { + +struct reply; +struct request; + +/// The common handler for all incoming requests. +class request_handler + : private boost::noncopyable +{ +public: + /// Construct with a directory containing files to be served. + explicit request_handler(const std::string& doc_root); + + /// Handle a request and produce a reply. + void handle_request(const request& req, reply& rep); + +private: + /// The directory containing the files to be served. + std::string doc_root_; + + /// Perform URL-decoding on a string. Returns false if the encoding was + /// invalid. + static bool url_decode(const std::string& in, std::string& out); +}; + +} // namespace server +} // namespace http + +#endif // HTTP_REQUEST_HANDLER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server/request_parser.cpp b/src/boost/libs/asio/example/cpp03/http/server/request_parser.cpp new file mode 100644 index 000000000..acfdf311b --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/request_parser.cpp @@ -0,0 +1,315 @@ +// +// request_parser.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "request_parser.hpp" +#include "request.hpp" + +namespace http { +namespace server { + +request_parser::request_parser() + : state_(method_start) +{ +} + +void request_parser::reset() +{ + state_ = method_start; +} + +boost::tribool request_parser::consume(request& req, char input) +{ + switch (state_) + { + case method_start: + if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + state_ = method; + req.method.push_back(input); + return boost::indeterminate; + } + case method: + if (input == ' ') + { + state_ = uri; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.method.push_back(input); + return boost::indeterminate; + } + case uri: + if (input == ' ') + { + state_ = http_version_h; + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + req.uri.push_back(input); + return boost::indeterminate; + } + case http_version_h: + if (input == 'H') + { + state_ = http_version_t_1; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_t_1: + if (input == 'T') + { + state_ = http_version_t_2; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_t_2: + if (input == 'T') + { + state_ = http_version_p; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_p: + if (input == 'P') + { + state_ = http_version_slash; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_slash: + if (input == '/') + { + req.http_version_major = 0; + req.http_version_minor = 0; + state_ = http_version_major_start; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_major_start: + if (is_digit(input)) + { + req.http_version_major = req.http_version_major * 10 + input - '0'; + state_ = http_version_major; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_major: + if (input == '.') + { + state_ = http_version_minor_start; + return boost::indeterminate; + } + else if (is_digit(input)) + { + req.http_version_major = req.http_version_major * 10 + input - '0'; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_minor_start: + if (is_digit(input)) + { + req.http_version_minor = req.http_version_minor * 10 + input - '0'; + state_ = http_version_minor; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_minor: + if (input == '\r') + { + state_ = expecting_newline_1; + return boost::indeterminate; + } + else if (is_digit(input)) + { + req.http_version_minor = req.http_version_minor * 10 + input - '0'; + return boost::indeterminate; + } + else + { + return false; + } + case expecting_newline_1: + if (input == '\n') + { + state_ = header_line_start; + return boost::indeterminate; + } + else + { + return false; + } + case header_line_start: + if (input == '\r') + { + state_ = expecting_newline_3; + return boost::indeterminate; + } + else if (!req.headers.empty() && (input == ' ' || input == '\t')) + { + state_ = header_lws; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.headers.push_back(header()); + req.headers.back().name.push_back(input); + state_ = header_name; + return boost::indeterminate; + } + case header_lws: + if (input == '\r') + { + state_ = expecting_newline_2; + return boost::indeterminate; + } + else if (input == ' ' || input == '\t') + { + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + state_ = header_value; + req.headers.back().value.push_back(input); + return boost::indeterminate; + } + case header_name: + if (input == ':') + { + state_ = space_before_header_value; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.headers.back().name.push_back(input); + return boost::indeterminate; + } + case space_before_header_value: + if (input == ' ') + { + state_ = header_value; + return boost::indeterminate; + } + else + { + return false; + } + case header_value: + if (input == '\r') + { + state_ = expecting_newline_2; + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + req.headers.back().value.push_back(input); + return boost::indeterminate; + } + case expecting_newline_2: + if (input == '\n') + { + state_ = header_line_start; + return boost::indeterminate; + } + else + { + return false; + } + case expecting_newline_3: + return (input == '\n'); + default: + return false; + } +} + +bool request_parser::is_char(int c) +{ + return c >= 0 && c <= 127; +} + +bool request_parser::is_ctl(int c) +{ + return (c >= 0 && c <= 31) || (c == 127); +} + +bool request_parser::is_tspecial(int c) +{ + switch (c) + { + case '(': case ')': case '<': case '>': case '@': + case ',': case ';': case ':': case '\\': case '"': + case '/': case '[': case ']': case '?': case '=': + case '{': case '}': case ' ': case '\t': + return true; + default: + return false; + } +} + +bool request_parser::is_digit(int c) +{ + return c >= '0' && c <= '9'; +} + +} // namespace server +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server/request_parser.hpp b/src/boost/libs/asio/example/cpp03/http/server/request_parser.hpp new file mode 100644 index 000000000..e13a13181 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/request_parser.hpp @@ -0,0 +1,95 @@ +// +// request_parser.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_REQUEST_PARSER_HPP +#define HTTP_REQUEST_PARSER_HPP + +#include <boost/logic/tribool.hpp> +#include <boost/tuple/tuple.hpp> + +namespace http { +namespace server { + +struct request; + +/// Parser for incoming requests. +class request_parser +{ +public: + /// Construct ready to parse the request method. + request_parser(); + + /// Reset to initial parser state. + void reset(); + + /// Parse some data. The tribool return value is true when a complete request + /// has been parsed, false if the data is invalid, indeterminate when more + /// data is required. The InputIterator return value indicates how much of the + /// input has been consumed. + template <typename InputIterator> + boost::tuple<boost::tribool, InputIterator> parse(request& req, + InputIterator begin, InputIterator end) + { + while (begin != end) + { + boost::tribool result = consume(req, *begin++); + if (result || !result) + return boost::make_tuple(result, begin); + } + boost::tribool result = boost::indeterminate; + return boost::make_tuple(result, begin); + } + +private: + /// Handle the next character of input. + boost::tribool consume(request& req, char input); + + /// Check if a byte is an HTTP character. + static bool is_char(int c); + + /// Check if a byte is an HTTP control character. + static bool is_ctl(int c); + + /// Check if a byte is defined as an HTTP tspecial character. + static bool is_tspecial(int c); + + /// Check if a byte is a digit. + static bool is_digit(int c); + + /// The current state of the parser. + enum state + { + method_start, + method, + uri, + http_version_h, + http_version_t_1, + http_version_t_2, + http_version_p, + http_version_slash, + http_version_major_start, + http_version_major, + http_version_minor_start, + http_version_minor, + expecting_newline_1, + header_line_start, + header_lws, + header_name, + space_before_header_value, + header_value, + expecting_newline_2, + expecting_newline_3 + } state_; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_REQUEST_PARSER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server/server.cpp b/src/boost/libs/asio/example/cpp03/http/server/server.cpp new file mode 100644 index 000000000..42253d878 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/server.cpp @@ -0,0 +1,94 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "server.hpp" +#include <boost/bind/bind.hpp> +#include <signal.h> + +namespace http { +namespace server { + +server::server(const std::string& address, const std::string& port, + const std::string& doc_root) + : io_context_(), + signals_(io_context_), + acceptor_(io_context_), + connection_manager_(), + new_connection_(), + request_handler_(doc_root) +{ + // Register to handle the signals that indicate when the server should exit. + // It is safe to register for the same signal multiple times in a program, + // provided all registration for the specified signal is made through Asio. + signals_.add(SIGINT); + signals_.add(SIGTERM); +#if defined(SIGQUIT) + signals_.add(SIGQUIT); +#endif // defined(SIGQUIT) + signals_.async_wait(boost::bind(&server::handle_stop, this)); + + // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). + boost::asio::ip::tcp::resolver resolver(io_context_); + boost::asio::ip::tcp::endpoint endpoint = + *resolver.resolve(address, port).begin(); + acceptor_.open(endpoint.protocol()); + acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + acceptor_.bind(endpoint); + acceptor_.listen(); + + start_accept(); +} + +void server::run() +{ + // The io_context::run() call will block until all asynchronous operations + // have finished. While the server is running, there is always at least one + // asynchronous operation outstanding: the asynchronous accept call waiting + // for new incoming connections. + io_context_.run(); +} + +void server::start_accept() +{ + new_connection_.reset(new connection(io_context_, + connection_manager_, request_handler_)); + acceptor_.async_accept(new_connection_->socket(), + boost::bind(&server::handle_accept, this, + boost::asio::placeholders::error)); +} + +void server::handle_accept(const boost::system::error_code& e) +{ + // Check whether the server was stopped by a signal before this completion + // handler had a chance to run. + if (!acceptor_.is_open()) + { + return; + } + + if (!e) + { + connection_manager_.start(new_connection_); + } + + start_accept(); +} + +void server::handle_stop() +{ + // The server is stopped by cancelling all outstanding asynchronous + // operations. Once all operations have finished the io_context::run() call + // will exit. + acceptor_.close(); + connection_manager_.stop_all(); +} + +} // namespace server +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server/server.hpp b/src/boost/libs/asio/example/cpp03/http/server/server.hpp new file mode 100644 index 000000000..e91b3d5fd --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server/server.hpp @@ -0,0 +1,69 @@ +// +// server.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER_HPP +#define HTTP_SERVER_HPP + +#include <boost/asio.hpp> +#include <string> +#include <boost/noncopyable.hpp> +#include "connection.hpp" +#include "connection_manager.hpp" +#include "request_handler.hpp" + +namespace http { +namespace server { + +/// The top-level class of the HTTP server. +class server + : private boost::noncopyable +{ +public: + /// Construct the server to listen on the specified TCP address and port, and + /// serve up files from the given directory. + explicit server(const std::string& address, const std::string& port, + const std::string& doc_root); + + /// Run the server's io_context loop. + void run(); + +private: + /// Initiate an asynchronous accept operation. + void start_accept(); + + /// Handle completion of an asynchronous accept operation. + void handle_accept(const boost::system::error_code& e); + + /// Handle a request to stop the server. + void handle_stop(); + + /// The io_context used to perform asynchronous operations. + boost::asio::io_context io_context_; + + /// The signal_set is used to register for process termination notifications. + boost::asio::signal_set signals_; + + /// Acceptor used to listen for incoming connections. + boost::asio::ip::tcp::acceptor acceptor_; + + /// The connection manager which owns all live connections. + connection_manager connection_manager_; + + /// The next connection to be accepted. + connection_ptr new_connection_; + + /// The handler for all incoming requests. + request_handler request_handler_; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_SERVER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server2/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/http/server2/Jamfile.v2 new file mode 100644 index 000000000..dc049c811 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/Jamfile.v2 @@ -0,0 +1,38 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe server + : connection.cpp + io_context_pool.cpp + main.cpp + mime_types.cpp + reply.cpp + request_handler.cpp + request_parser.cpp + server.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + /boost/thread//boost_thread + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/http/server2/connection.cpp b/src/boost/libs/asio/example/cpp03/http/server2/connection.cpp new file mode 100644 index 000000000..6f8b02e26 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/connection.cpp @@ -0,0 +1,93 @@ +// +// connection.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "connection.hpp" +#include <vector> +#include <boost/bind/bind.hpp> +#include "request_handler.hpp" + +namespace http { +namespace server2 { + +connection::connection(boost::asio::io_context& io_context, + request_handler& handler) + : socket_(io_context), + request_handler_(handler) +{ +} + +boost::asio::ip::tcp::socket& connection::socket() +{ + return socket_; +} + +void connection::start() +{ + socket_.async_read_some(boost::asio::buffer(buffer_), + boost::bind(&connection::handle_read, shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); +} + +void connection::handle_read(const boost::system::error_code& e, + std::size_t bytes_transferred) +{ + if (!e) + { + boost::tribool result; + boost::tie(result, boost::tuples::ignore) = request_parser_.parse( + request_, buffer_.data(), buffer_.data() + bytes_transferred); + + if (result) + { + request_handler_.handle_request(request_, reply_); + boost::asio::async_write(socket_, reply_.to_buffers(), + boost::bind(&connection::handle_write, shared_from_this(), + boost::asio::placeholders::error)); + } + else if (!result) + { + reply_ = reply::stock_reply(reply::bad_request); + boost::asio::async_write(socket_, reply_.to_buffers(), + boost::bind(&connection::handle_write, shared_from_this(), + boost::asio::placeholders::error)); + } + else + { + socket_.async_read_some(boost::asio::buffer(buffer_), + boost::bind(&connection::handle_read, shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + } + + // If an error occurs then no new asynchronous operations are started. This + // means that all shared_ptr references to the connection object will + // disappear and the object will be destroyed automatically after this + // handler returns. The connection class's destructor closes the socket. +} + +void connection::handle_write(const boost::system::error_code& e) +{ + if (!e) + { + // Initiate graceful connection closure. + boost::system::error_code ignored_ec; + socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); + } + + // No new asynchronous operations are started. This means that all shared_ptr + // references to the connection object will disappear and the object will be + // destroyed automatically after this handler returns. The connection class's + // destructor closes the socket. +} + +} // namespace server2 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server2/connection.hpp b/src/boost/libs/asio/example/cpp03/http/server2/connection.hpp new file mode 100644 index 000000000..c949c64c3 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/connection.hpp @@ -0,0 +1,75 @@ +// +// connection.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER2_CONNECTION_HPP +#define HTTP_SERVER2_CONNECTION_HPP + +#include <boost/asio.hpp> +#include <boost/array.hpp> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include "reply.hpp" +#include "request.hpp" +#include "request_handler.hpp" +#include "request_parser.hpp" + +namespace http { +namespace server2 { + +/// Represents a single connection from a client. +class connection + : public boost::enable_shared_from_this<connection>, + private boost::noncopyable +{ +public: + /// Construct a connection with the given io_context. + explicit connection(boost::asio::io_context& io_context, + request_handler& handler); + + /// Get the socket associated with the connection. + boost::asio::ip::tcp::socket& socket(); + + /// Start the first asynchronous operation for the connection. + void start(); + +private: + /// Handle completion of a read operation. + void handle_read(const boost::system::error_code& e, + std::size_t bytes_transferred); + + /// Handle completion of a write operation. + void handle_write(const boost::system::error_code& e); + + /// Socket for the connection. + boost::asio::ip::tcp::socket socket_; + + /// The handler used to process the incoming request. + request_handler& request_handler_; + + /// Buffer for incoming data. + boost::array<char, 8192> buffer_; + + /// The incoming request. + request request_; + + /// The parser for the incoming request. + request_parser request_parser_; + + /// The reply to be sent back to the client. + reply reply_; +}; + +typedef boost::shared_ptr<connection> connection_ptr; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_CONNECTION_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server2/header.hpp b/src/boost/libs/asio/example/cpp03/http/server2/header.hpp new file mode 100644 index 000000000..80efc6cc6 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/header.hpp @@ -0,0 +1,28 @@ +// +// header.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER2_HEADER_HPP +#define HTTP_SERVER2_HEADER_HPP + +#include <string> + +namespace http { +namespace server2 { + +struct header +{ + std::string name; + std::string value; +}; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_HEADER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server2/io_context_pool.cpp b/src/boost/libs/asio/example/cpp03/http/server2/io_context_pool.cpp new file mode 100644 index 000000000..427c22600 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/io_context_pool.cpp @@ -0,0 +1,70 @@ +// +// io_context_pool.cpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "server.hpp" +#include <stdexcept> +#include <boost/thread/thread.hpp> +#include <boost/bind/bind.hpp> +#include <boost/shared_ptr.hpp> + +namespace http { +namespace server2 { + +io_context_pool::io_context_pool(std::size_t pool_size) + : next_io_context_(0) +{ + if (pool_size == 0) + throw std::runtime_error("io_context_pool size is 0"); + + // Give all the io_contexts work to do so that their run() functions will not + // exit until they are explicitly stopped. + for (std::size_t i = 0; i < pool_size; ++i) + { + io_context_ptr io_context(new boost::asio::io_context); + io_contexts_.push_back(io_context); + work_.push_back(boost::asio::make_work_guard(*io_context)); + } +} + +void io_context_pool::run() +{ + // Create a pool of threads to run all of the io_contexts. + std::vector<boost::shared_ptr<boost::thread> > threads; + for (std::size_t i = 0; i < io_contexts_.size(); ++i) + { + boost::shared_ptr<boost::thread> thread(new boost::thread( + boost::bind(&boost::asio::io_context::run, io_contexts_[i]))); + threads.push_back(thread); + } + + // Wait for all threads in the pool to exit. + for (std::size_t i = 0; i < threads.size(); ++i) + threads[i]->join(); +} + +void io_context_pool::stop() +{ + // Explicitly stop all io_contexts. + for (std::size_t i = 0; i < io_contexts_.size(); ++i) + io_contexts_[i]->stop(); +} + +boost::asio::io_context& io_context_pool::get_io_context() +{ + // Use a round-robin scheme to choose the next io_context to use. + boost::asio::io_context& io_context = *io_contexts_[next_io_context_]; + ++next_io_context_; + if (next_io_context_ == io_contexts_.size()) + next_io_context_ = 0; + return io_context; +} + +} // namespace server2 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server2/io_context_pool.hpp b/src/boost/libs/asio/example/cpp03/http/server2/io_context_pool.hpp new file mode 100644 index 000000000..86bd5b45a --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/io_context_pool.hpp @@ -0,0 +1,58 @@ +// +// io_context_pool.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER2_IO_SERVICE_POOL_HPP +#define HTTP_SERVER2_IO_SERVICE_POOL_HPP + +#include <boost/asio.hpp> +#include <list> +#include <vector> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> + +namespace http { +namespace server2 { + +/// A pool of io_context objects. +class io_context_pool + : private boost::noncopyable +{ +public: + /// Construct the io_context pool. + explicit io_context_pool(std::size_t pool_size); + + /// Run all io_context objects in the pool. + void run(); + + /// Stop all io_context objects in the pool. + void stop(); + + /// Get an io_context to use. + boost::asio::io_context& get_io_context(); + +private: + typedef boost::shared_ptr<boost::asio::io_context> io_context_ptr; + typedef boost::asio::executor_work_guard< + boost::asio::io_context::executor_type> io_context_work; + + /// The pool of io_contexts. + std::vector<io_context_ptr> io_contexts_; + + /// The work that keeps the io_contexts running. + std::list<io_context_work> work_; + + /// The next io_context to use for a connection. + std::size_t next_io_context_; +}; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_IO_SERVICE_POOL_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server2/main.cpp b/src/boost/libs/asio/example/cpp03/http/server2/main.cpp new file mode 100644 index 000000000..18be4184e --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/main.cpp @@ -0,0 +1,46 @@ +// +// main.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <string> +#include <boost/asio.hpp> +#include <boost/bind/bind.hpp> +#include <boost/lexical_cast.hpp> +#include "server.hpp" + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 5) + { + std::cerr << "Usage: http_server <address> <port> <threads> <doc_root>\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " receiver 0.0.0.0 80 1 .\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " receiver 0::0 80 1 .\n"; + return 1; + } + + // Initialise the server. + std::size_t num_threads = boost::lexical_cast<std::size_t>(argv[3]); + http::server2::server s(argv[1], argv[2], argv[4], num_threads); + + // Run the server until stopped. + s.run(); + } + catch (std::exception& e) + { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/http/server2/mime_types.cpp b/src/boost/libs/asio/example/cpp03/http/server2/mime_types.cpp new file mode 100644 index 000000000..b2fc5e7aa --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/mime_types.cpp @@ -0,0 +1,46 @@ +// +// mime_types.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "mime_types.hpp" + +namespace http { +namespace server2 { +namespace mime_types { + +struct mapping +{ + const char* extension; + const char* mime_type; +} mappings[] = +{ + { "gif", "image/gif" }, + { "htm", "text/html" }, + { "html", "text/html" }, + { "jpg", "image/jpeg" }, + { "png", "image/png" }, + { 0, 0 } // Marks end of list. +}; + +std::string extension_to_type(const std::string& extension) +{ + for (mapping* m = mappings; m->extension; ++m) + { + if (m->extension == extension) + { + return m->mime_type; + } + } + + return "text/plain"; +} + +} // namespace mime_types +} // namespace server2 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server2/mime_types.hpp b/src/boost/libs/asio/example/cpp03/http/server2/mime_types.hpp new file mode 100644 index 000000000..56e3fd57b --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/mime_types.hpp @@ -0,0 +1,27 @@ +// +// mime_types.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER2_MIME_TYPES_HPP +#define HTTP_SERVER2_MIME_TYPES_HPP + +#include <string> + +namespace http { +namespace server2 { +namespace mime_types { + +/// Convert a file extension into a MIME type. +std::string extension_to_type(const std::string& extension); + +} // namespace mime_types +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_MIME_TYPES_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server2/reply.cpp b/src/boost/libs/asio/example/cpp03/http/server2/reply.cpp new file mode 100644 index 000000000..524cb65e9 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/reply.cpp @@ -0,0 +1,256 @@ +// +// reply.cpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "reply.hpp" +#include <string> +#include <boost/lexical_cast.hpp> + +namespace http { +namespace server2 { + +namespace status_strings { + +const std::string ok = + "HTTP/1.0 200 OK\r\n"; +const std::string created = + "HTTP/1.0 201 Created\r\n"; +const std::string accepted = + "HTTP/1.0 202 Accepted\r\n"; +const std::string no_content = + "HTTP/1.0 204 No Content\r\n"; +const std::string multiple_choices = + "HTTP/1.0 300 Multiple Choices\r\n"; +const std::string moved_permanently = + "HTTP/1.0 301 Moved Permanently\r\n"; +const std::string moved_temporarily = + "HTTP/1.0 302 Moved Temporarily\r\n"; +const std::string not_modified = + "HTTP/1.0 304 Not Modified\r\n"; +const std::string bad_request = + "HTTP/1.0 400 Bad Request\r\n"; +const std::string unauthorized = + "HTTP/1.0 401 Unauthorized\r\n"; +const std::string forbidden = + "HTTP/1.0 403 Forbidden\r\n"; +const std::string not_found = + "HTTP/1.0 404 Not Found\r\n"; +const std::string internal_server_error = + "HTTP/1.0 500 Internal Server Error\r\n"; +const std::string not_implemented = + "HTTP/1.0 501 Not Implemented\r\n"; +const std::string bad_gateway = + "HTTP/1.0 502 Bad Gateway\r\n"; +const std::string service_unavailable = + "HTTP/1.0 503 Service Unavailable\r\n"; + +boost::asio::const_buffer to_buffer(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return boost::asio::buffer(ok); + case reply::created: + return boost::asio::buffer(created); + case reply::accepted: + return boost::asio::buffer(accepted); + case reply::no_content: + return boost::asio::buffer(no_content); + case reply::multiple_choices: + return boost::asio::buffer(multiple_choices); + case reply::moved_permanently: + return boost::asio::buffer(moved_permanently); + case reply::moved_temporarily: + return boost::asio::buffer(moved_temporarily); + case reply::not_modified: + return boost::asio::buffer(not_modified); + case reply::bad_request: + return boost::asio::buffer(bad_request); + case reply::unauthorized: + return boost::asio::buffer(unauthorized); + case reply::forbidden: + return boost::asio::buffer(forbidden); + case reply::not_found: + return boost::asio::buffer(not_found); + case reply::internal_server_error: + return boost::asio::buffer(internal_server_error); + case reply::not_implemented: + return boost::asio::buffer(not_implemented); + case reply::bad_gateway: + return boost::asio::buffer(bad_gateway); + case reply::service_unavailable: + return boost::asio::buffer(service_unavailable); + default: + return boost::asio::buffer(internal_server_error); + } +} + +} // namespace status_strings + +namespace misc_strings { + +const char name_value_separator[] = { ':', ' ' }; +const char crlf[] = { '\r', '\n' }; + +} // namespace misc_strings + +std::vector<boost::asio::const_buffer> reply::to_buffers() +{ + std::vector<boost::asio::const_buffer> buffers; + buffers.push_back(status_strings::to_buffer(status)); + for (std::size_t i = 0; i < headers.size(); ++i) + { + header& h = headers[i]; + buffers.push_back(boost::asio::buffer(h.name)); + buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator)); + buffers.push_back(boost::asio::buffer(h.value)); + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + } + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + buffers.push_back(boost::asio::buffer(content)); + return buffers; +} + +namespace stock_replies { + +const char ok[] = ""; +const char created[] = + "<html>" + "<head><title>Created</title></head>" + "<body><h1>201 Created</h1></body>" + "</html>"; +const char accepted[] = + "<html>" + "<head><title>Accepted</title></head>" + "<body><h1>202 Accepted</h1></body>" + "</html>"; +const char no_content[] = + "<html>" + "<head><title>No Content</title></head>" + "<body><h1>204 Content</h1></body>" + "</html>"; +const char multiple_choices[] = + "<html>" + "<head><title>Multiple Choices</title></head>" + "<body><h1>300 Multiple Choices</h1></body>" + "</html>"; +const char moved_permanently[] = + "<html>" + "<head><title>Moved Permanently</title></head>" + "<body><h1>301 Moved Permanently</h1></body>" + "</html>"; +const char moved_temporarily[] = + "<html>" + "<head><title>Moved Temporarily</title></head>" + "<body><h1>302 Moved Temporarily</h1></body>" + "</html>"; +const char not_modified[] = + "<html>" + "<head><title>Not Modified</title></head>" + "<body><h1>304 Not Modified</h1></body>" + "</html>"; +const char bad_request[] = + "<html>" + "<head><title>Bad Request</title></head>" + "<body><h1>400 Bad Request</h1></body>" + "</html>"; +const char unauthorized[] = + "<html>" + "<head><title>Unauthorized</title></head>" + "<body><h1>401 Unauthorized</h1></body>" + "</html>"; +const char forbidden[] = + "<html>" + "<head><title>Forbidden</title></head>" + "<body><h1>403 Forbidden</h1></body>" + "</html>"; +const char not_found[] = + "<html>" + "<head><title>Not Found</title></head>" + "<body><h1>404 Not Found</h1></body>" + "</html>"; +const char internal_server_error[] = + "<html>" + "<head><title>Internal Server Error</title></head>" + "<body><h1>500 Internal Server Error</h1></body>" + "</html>"; +const char not_implemented[] = + "<html>" + "<head><title>Not Implemented</title></head>" + "<body><h1>501 Not Implemented</h1></body>" + "</html>"; +const char bad_gateway[] = + "<html>" + "<head><title>Bad Gateway</title></head>" + "<body><h1>502 Bad Gateway</h1></body>" + "</html>"; +const char service_unavailable[] = + "<html>" + "<head><title>Service Unavailable</title></head>" + "<body><h1>503 Service Unavailable</h1></body>" + "</html>"; + +std::string to_string(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return ok; + case reply::created: + return created; + case reply::accepted: + return accepted; + case reply::no_content: + return no_content; + case reply::multiple_choices: + return multiple_choices; + case reply::moved_permanently: + return moved_permanently; + case reply::moved_temporarily: + return moved_temporarily; + case reply::not_modified: + return not_modified; + case reply::bad_request: + return bad_request; + case reply::unauthorized: + return unauthorized; + case reply::forbidden: + return forbidden; + case reply::not_found: + return not_found; + case reply::internal_server_error: + return internal_server_error; + case reply::not_implemented: + return not_implemented; + case reply::bad_gateway: + return bad_gateway; + case reply::service_unavailable: + return service_unavailable; + default: + return internal_server_error; + } +} + +} // namespace stock_replies + +reply reply::stock_reply(reply::status_type status) +{ + reply rep; + rep.status = status; + rep.content = stock_replies::to_string(status); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = "text/html"; + return rep; +} + +} // namespace server2 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server2/reply.hpp b/src/boost/libs/asio/example/cpp03/http/server2/reply.hpp new file mode 100644 index 000000000..bff15b0db --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/reply.hpp @@ -0,0 +1,64 @@ +// +// reply.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER2_REPLY_HPP +#define HTTP_SERVER2_REPLY_HPP + +#include <string> +#include <vector> +#include <boost/asio.hpp> +#include "header.hpp" + +namespace http { +namespace server2 { + +/// A reply to be sent to a client. +struct reply +{ + /// The status of the reply. + enum status_type + { + ok = 200, + created = 201, + accepted = 202, + no_content = 204, + multiple_choices = 300, + moved_permanently = 301, + moved_temporarily = 302, + not_modified = 304, + bad_request = 400, + unauthorized = 401, + forbidden = 403, + not_found = 404, + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503 + } status; + + /// The headers to be included in the reply. + std::vector<header> headers; + + /// The content to be sent in the reply. + std::string content; + + /// Convert the reply into a vector of buffers. The buffers do not own the + /// underlying memory blocks, therefore the reply object must remain valid and + /// not be changed until the write operation has completed. + std::vector<boost::asio::const_buffer> to_buffers(); + + /// Get a stock reply. + static reply stock_reply(status_type status); +}; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_REPLY_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server2/request.hpp b/src/boost/libs/asio/example/cpp03/http/server2/request.hpp new file mode 100644 index 000000000..197afe34a --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/request.hpp @@ -0,0 +1,34 @@ +// +// request.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER2_REQUEST_HPP +#define HTTP_SERVER2_REQUEST_HPP + +#include <string> +#include <vector> +#include "header.hpp" + +namespace http { +namespace server2 { + +/// A request received from a client. +struct request +{ + std::string method; + std::string uri; + int http_version_major; + int http_version_minor; + std::vector<header> headers; +}; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_REQUEST_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server2/request_handler.cpp b/src/boost/libs/asio/example/cpp03/http/server2/request_handler.cpp new file mode 100644 index 000000000..ca3728b58 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/request_handler.cpp @@ -0,0 +1,122 @@ +// +// request_handler.cpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "request_handler.hpp" +#include <fstream> +#include <sstream> +#include <string> +#include <boost/lexical_cast.hpp> +#include "mime_types.hpp" +#include "reply.hpp" +#include "request.hpp" + +namespace http { +namespace server2 { + +request_handler::request_handler(const std::string& doc_root) + : doc_root_(doc_root) +{ +} + +void request_handler::handle_request(const request& req, reply& rep) +{ + // Decode url to path. + std::string request_path; + if (!url_decode(req.uri, request_path)) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // Request path must be absolute and not contain "..". + if (request_path.empty() || request_path[0] != '/' + || request_path.find("..") != std::string::npos) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // If path ends in slash (i.e. is a directory) then add "index.html". + if (request_path[request_path.size() - 1] == '/') + { + request_path += "index.html"; + } + + // Determine the file extension. + std::size_t last_slash_pos = request_path.find_last_of("/"); + std::size_t last_dot_pos = request_path.find_last_of("."); + std::string extension; + if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos) + { + extension = request_path.substr(last_dot_pos + 1); + } + + // Open the file to send back. + std::string full_path = doc_root_ + request_path; + std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary); + if (!is) + { + rep = reply::stock_reply(reply::not_found); + return; + } + + // Fill out the reply to be sent to the client. + rep.status = reply::ok; + char buf[512]; + while (is.read(buf, sizeof(buf)).gcount() > 0) + rep.content.append(buf, is.gcount()); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = mime_types::extension_to_type(extension); +} + +bool request_handler::url_decode(const std::string& in, std::string& out) +{ + out.clear(); + out.reserve(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) + { + if (in[i] == '%') + { + if (i + 3 <= in.size()) + { + int value = 0; + std::istringstream is(in.substr(i + 1, 2)); + if (is >> std::hex >> value) + { + out += static_cast<char>(value); + i += 2; + } + else + { + return false; + } + } + else + { + return false; + } + } + else if (in[i] == '+') + { + out += ' '; + } + else + { + out += in[i]; + } + } + return true; +} + +} // namespace server2 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server2/request_handler.hpp b/src/boost/libs/asio/example/cpp03/http/server2/request_handler.hpp new file mode 100644 index 000000000..dc327fdb4 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/request_handler.hpp @@ -0,0 +1,46 @@ +// +// request_handler.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER2_REQUEST_HANDLER_HPP +#define HTTP_SERVER2_REQUEST_HANDLER_HPP + +#include <string> +#include <boost/noncopyable.hpp> + +namespace http { +namespace server2 { + +struct reply; +struct request; + +/// The common handler for all incoming requests. +class request_handler + : private boost::noncopyable +{ +public: + /// Construct with a directory containing files to be served. + explicit request_handler(const std::string& doc_root); + + /// Handle a request and produce a reply. + void handle_request(const request& req, reply& rep); + +private: + /// The directory containing the files to be served. + std::string doc_root_; + + /// Perform URL-decoding on a string. Returns false if the encoding was + /// invalid. + static bool url_decode(const std::string& in, std::string& out); +}; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_REQUEST_HANDLER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server2/request_parser.cpp b/src/boost/libs/asio/example/cpp03/http/server2/request_parser.cpp new file mode 100644 index 000000000..8533e8873 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/request_parser.cpp @@ -0,0 +1,315 @@ +// +// request_parser.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "request_parser.hpp" +#include "request.hpp" + +namespace http { +namespace server2 { + +request_parser::request_parser() + : state_(method_start) +{ +} + +void request_parser::reset() +{ + state_ = method_start; +} + +boost::tribool request_parser::consume(request& req, char input) +{ + switch (state_) + { + case method_start: + if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + state_ = method; + req.method.push_back(input); + return boost::indeterminate; + } + case method: + if (input == ' ') + { + state_ = uri; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.method.push_back(input); + return boost::indeterminate; + } + case uri: + if (input == ' ') + { + state_ = http_version_h; + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + req.uri.push_back(input); + return boost::indeterminate; + } + case http_version_h: + if (input == 'H') + { + state_ = http_version_t_1; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_t_1: + if (input == 'T') + { + state_ = http_version_t_2; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_t_2: + if (input == 'T') + { + state_ = http_version_p; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_p: + if (input == 'P') + { + state_ = http_version_slash; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_slash: + if (input == '/') + { + req.http_version_major = 0; + req.http_version_minor = 0; + state_ = http_version_major_start; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_major_start: + if (is_digit(input)) + { + req.http_version_major = req.http_version_major * 10 + input - '0'; + state_ = http_version_major; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_major: + if (input == '.') + { + state_ = http_version_minor_start; + return boost::indeterminate; + } + else if (is_digit(input)) + { + req.http_version_major = req.http_version_major * 10 + input - '0'; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_minor_start: + if (is_digit(input)) + { + req.http_version_minor = req.http_version_minor * 10 + input - '0'; + state_ = http_version_minor; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_minor: + if (input == '\r') + { + state_ = expecting_newline_1; + return boost::indeterminate; + } + else if (is_digit(input)) + { + req.http_version_minor = req.http_version_minor * 10 + input - '0'; + return boost::indeterminate; + } + else + { + return false; + } + case expecting_newline_1: + if (input == '\n') + { + state_ = header_line_start; + return boost::indeterminate; + } + else + { + return false; + } + case header_line_start: + if (input == '\r') + { + state_ = expecting_newline_3; + return boost::indeterminate; + } + else if (!req.headers.empty() && (input == ' ' || input == '\t')) + { + state_ = header_lws; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.headers.push_back(header()); + req.headers.back().name.push_back(input); + state_ = header_name; + return boost::indeterminate; + } + case header_lws: + if (input == '\r') + { + state_ = expecting_newline_2; + return boost::indeterminate; + } + else if (input == ' ' || input == '\t') + { + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + state_ = header_value; + req.headers.back().value.push_back(input); + return boost::indeterminate; + } + case header_name: + if (input == ':') + { + state_ = space_before_header_value; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.headers.back().name.push_back(input); + return boost::indeterminate; + } + case space_before_header_value: + if (input == ' ') + { + state_ = header_value; + return boost::indeterminate; + } + else + { + return false; + } + case header_value: + if (input == '\r') + { + state_ = expecting_newline_2; + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + req.headers.back().value.push_back(input); + return boost::indeterminate; + } + case expecting_newline_2: + if (input == '\n') + { + state_ = header_line_start; + return boost::indeterminate; + } + else + { + return false; + } + case expecting_newline_3: + return (input == '\n'); + default: + return false; + } +} + +bool request_parser::is_char(int c) +{ + return c >= 0 && c <= 127; +} + +bool request_parser::is_ctl(int c) +{ + return (c >= 0 && c <= 31) || (c == 127); +} + +bool request_parser::is_tspecial(int c) +{ + switch (c) + { + case '(': case ')': case '<': case '>': case '@': + case ',': case ';': case ':': case '\\': case '"': + case '/': case '[': case ']': case '?': case '=': + case '{': case '}': case ' ': case '\t': + return true; + default: + return false; + } +} + +bool request_parser::is_digit(int c) +{ + return c >= '0' && c <= '9'; +} + +} // namespace server2 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server2/request_parser.hpp b/src/boost/libs/asio/example/cpp03/http/server2/request_parser.hpp new file mode 100644 index 000000000..3d9aae7a9 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/request_parser.hpp @@ -0,0 +1,95 @@ +// +// request_parser.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER2_REQUEST_PARSER_HPP +#define HTTP_SERVER2_REQUEST_PARSER_HPP + +#include <boost/logic/tribool.hpp> +#include <boost/tuple/tuple.hpp> + +namespace http { +namespace server2 { + +struct request; + +/// Parser for incoming requests. +class request_parser +{ +public: + /// Construct ready to parse the request method. + request_parser(); + + /// Reset to initial parser state. + void reset(); + + /// Parse some data. The tribool return value is true when a complete request + /// has been parsed, false if the data is invalid, indeterminate when more + /// data is required. The InputIterator return value indicates how much of the + /// input has been consumed. + template <typename InputIterator> + boost::tuple<boost::tribool, InputIterator> parse(request& req, + InputIterator begin, InputIterator end) + { + while (begin != end) + { + boost::tribool result = consume(req, *begin++); + if (result || !result) + return boost::make_tuple(result, begin); + } + boost::tribool result = boost::indeterminate; + return boost::make_tuple(result, begin); + } + +private: + /// Handle the next character of input. + boost::tribool consume(request& req, char input); + + /// Check if a byte is an HTTP character. + static bool is_char(int c); + + /// Check if a byte is an HTTP control character. + static bool is_ctl(int c); + + /// Check if a byte is defined as an HTTP tspecial character. + static bool is_tspecial(int c); + + /// Check if a byte is a digit. + static bool is_digit(int c); + + /// The current state of the parser. + enum state + { + method_start, + method, + uri, + http_version_h, + http_version_t_1, + http_version_t_2, + http_version_p, + http_version_slash, + http_version_major_start, + http_version_major, + http_version_minor_start, + http_version_minor, + expecting_newline_1, + header_line_start, + header_lws, + header_name, + space_before_header_value, + header_value, + expecting_newline_2, + expecting_newline_3 + } state_; +}; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_REQUEST_PARSER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server2/server.cpp b/src/boost/libs/asio/example/cpp03/http/server2/server.cpp new file mode 100644 index 000000000..c6fa33ca1 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/server.cpp @@ -0,0 +1,77 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "server.hpp" +#include <boost/bind/bind.hpp> + +namespace http { +namespace server2 { + +server::server(const std::string& address, const std::string& port, + const std::string& doc_root, std::size_t io_context_pool_size) + : io_context_pool_(io_context_pool_size), + signals_(io_context_pool_.get_io_context()), + acceptor_(io_context_pool_.get_io_context()), + new_connection_(), + request_handler_(doc_root) +{ + // Register to handle the signals that indicate when the server should exit. + // It is safe to register for the same signal multiple times in a program, + // provided all registration for the specified signal is made through Asio. + signals_.add(SIGINT); + signals_.add(SIGTERM); +#if defined(SIGQUIT) + signals_.add(SIGQUIT); +#endif // defined(SIGQUIT) + signals_.async_wait(boost::bind(&server::handle_stop, this)); + + // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). + boost::asio::ip::tcp::resolver resolver(acceptor_.get_executor()); + boost::asio::ip::tcp::endpoint endpoint = + *resolver.resolve(address, port).begin(); + acceptor_.open(endpoint.protocol()); + acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + acceptor_.bind(endpoint); + acceptor_.listen(); + + start_accept(); +} + +void server::run() +{ + io_context_pool_.run(); +} + +void server::start_accept() +{ + new_connection_.reset(new connection( + io_context_pool_.get_io_context(), request_handler_)); + acceptor_.async_accept(new_connection_->socket(), + boost::bind(&server::handle_accept, this, + boost::asio::placeholders::error)); +} + +void server::handle_accept(const boost::system::error_code& e) +{ + if (!e) + { + new_connection_->start(); + } + + start_accept(); +} + +void server::handle_stop() +{ + io_context_pool_.stop(); +} + +} // namespace server2 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server2/server.hpp b/src/boost/libs/asio/example/cpp03/http/server2/server.hpp new file mode 100644 index 000000000..90b921f4e --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server2/server.hpp @@ -0,0 +1,68 @@ +// +// server.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER2_SERVER_HPP +#define HTTP_SERVER2_SERVER_HPP + +#include <boost/asio.hpp> +#include <string> +#include <vector> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include "connection.hpp" +#include "io_context_pool.hpp" +#include "request_handler.hpp" + +namespace http { +namespace server2 { + +/// The top-level class of the HTTP server. +class server + : private boost::noncopyable +{ +public: + /// Construct the server to listen on the specified TCP address and port, and + /// serve up files from the given directory. + explicit server(const std::string& address, const std::string& port, + const std::string& doc_root, std::size_t io_context_pool_size); + + /// Run the server's io_context loop. + void run(); + +private: + /// Initiate an asynchronous accept operation. + void start_accept(); + + /// Handle completion of an asynchronous accept operation. + void handle_accept(const boost::system::error_code& e); + + /// Handle a request to stop the server. + void handle_stop(); + + /// The pool of io_context objects used to perform asynchronous operations. + io_context_pool io_context_pool_; + + /// The signal_set is used to register for process termination notifications. + boost::asio::signal_set signals_; + + /// Acceptor used to listen for incoming connections. + boost::asio::ip::tcp::acceptor acceptor_; + + /// The next connection to be accepted. + connection_ptr new_connection_; + + /// The handler for all incoming requests. + request_handler request_handler_; +}; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_SERVER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server3/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/http/server3/Jamfile.v2 new file mode 100644 index 000000000..e49656ec5 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/Jamfile.v2 @@ -0,0 +1,37 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe server + : connection.cpp + main.cpp + mime_types.cpp + reply.cpp + request_handler.cpp + request_parser.cpp + server.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + /boost/thread//boost_thread + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/http/server3/connection.cpp b/src/boost/libs/asio/example/cpp03/http/server3/connection.cpp new file mode 100644 index 000000000..fa6f828a9 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/connection.cpp @@ -0,0 +1,94 @@ +// +// connection.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "connection.hpp" +#include <vector> +#include <boost/bind/bind.hpp> +#include "request_handler.hpp" + +namespace http { +namespace server3 { + +connection::connection(boost::asio::io_context& io_context, + request_handler& handler) + : strand_(boost::asio::make_strand(io_context)), + socket_(strand_), + request_handler_(handler) +{ +} + +boost::asio::ip::tcp::socket& connection::socket() +{ + return socket_; +} + +void connection::start() +{ + socket_.async_read_some(boost::asio::buffer(buffer_), + boost::bind(&connection::handle_read, shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); +} + +void connection::handle_read(const boost::system::error_code& e, + std::size_t bytes_transferred) +{ + if (!e) + { + boost::tribool result; + boost::tie(result, boost::tuples::ignore) = request_parser_.parse( + request_, buffer_.data(), buffer_.data() + bytes_transferred); + + if (result) + { + request_handler_.handle_request(request_, reply_); + boost::asio::async_write(socket_, reply_.to_buffers(), + boost::bind(&connection::handle_write, shared_from_this(), + boost::asio::placeholders::error)); + } + else if (!result) + { + reply_ = reply::stock_reply(reply::bad_request); + boost::asio::async_write(socket_, reply_.to_buffers(), + boost::bind(&connection::handle_write, shared_from_this(), + boost::asio::placeholders::error)); + } + else + { + socket_.async_read_some(boost::asio::buffer(buffer_), + boost::bind(&connection::handle_read, shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + } + + // If an error occurs then no new asynchronous operations are started. This + // means that all shared_ptr references to the connection object will + // disappear and the object will be destroyed automatically after this + // handler returns. The connection class's destructor closes the socket. +} + +void connection::handle_write(const boost::system::error_code& e) +{ + if (!e) + { + // Initiate graceful connection closure. + boost::system::error_code ignored_ec; + socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); + } + + // No new asynchronous operations are started. This means that all shared_ptr + // references to the connection object will disappear and the object will be + // destroyed automatically after this handler returns. The connection class's + // destructor closes the socket. +} + +} // namespace server3 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server3/connection.hpp b/src/boost/libs/asio/example/cpp03/http/server3/connection.hpp new file mode 100644 index 000000000..f5583d02f --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/connection.hpp @@ -0,0 +1,78 @@ +// +// connection.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER3_CONNECTION_HPP +#define HTTP_SERVER3_CONNECTION_HPP + +#include <boost/asio.hpp> +#include <boost/array.hpp> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include "reply.hpp" +#include "request.hpp" +#include "request_handler.hpp" +#include "request_parser.hpp" + +namespace http { +namespace server3 { + +/// Represents a single connection from a client. +class connection + : public boost::enable_shared_from_this<connection>, + private boost::noncopyable +{ +public: + /// Construct a connection with the given io_context. + explicit connection(boost::asio::io_context& io_context, + request_handler& handler); + + /// Get the socket associated with the connection. + boost::asio::ip::tcp::socket& socket(); + + /// Start the first asynchronous operation for the connection. + void start(); + +private: + /// Handle completion of a read operation. + void handle_read(const boost::system::error_code& e, + std::size_t bytes_transferred); + + /// Handle completion of a write operation. + void handle_write(const boost::system::error_code& e); + + /// Strand to ensure the connection's handlers are not called concurrently. + boost::asio::strand<boost::asio::io_context::executor_type> strand_; + + /// Socket for the connection. + boost::asio::ip::tcp::socket socket_; + + /// The handler used to process the incoming request. + request_handler& request_handler_; + + /// Buffer for incoming data. + boost::array<char, 8192> buffer_; + + /// The incoming request. + request request_; + + /// The parser for the incoming request. + request_parser request_parser_; + + /// The reply to be sent back to the client. + reply reply_; +}; + +typedef boost::shared_ptr<connection> connection_ptr; + +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_CONNECTION_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server3/header.hpp b/src/boost/libs/asio/example/cpp03/http/server3/header.hpp new file mode 100644 index 000000000..45a98a4bb --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/header.hpp @@ -0,0 +1,28 @@ +// +// header.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER3_HEADER_HPP +#define HTTP_SERVER3_HEADER_HPP + +#include <string> + +namespace http { +namespace server3 { + +struct header +{ + std::string name; + std::string value; +}; + +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_HEADER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server3/main.cpp b/src/boost/libs/asio/example/cpp03/http/server3/main.cpp new file mode 100644 index 000000000..4f181c573 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/main.cpp @@ -0,0 +1,46 @@ +// +// main.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <string> +#include <boost/asio.hpp> +#include <boost/bind/bind.hpp> +#include <boost/lexical_cast.hpp> +#include "server.hpp" + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 5) + { + std::cerr << "Usage: http_server <address> <port> <threads> <doc_root>\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " receiver 0.0.0.0 80 1 .\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " receiver 0::0 80 1 .\n"; + return 1; + } + + // Initialise the server. + std::size_t num_threads = boost::lexical_cast<std::size_t>(argv[3]); + http::server3::server s(argv[1], argv[2], argv[4], num_threads); + + // Run the server until stopped. + s.run(); + } + catch (std::exception& e) + { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/http/server3/mime_types.cpp b/src/boost/libs/asio/example/cpp03/http/server3/mime_types.cpp new file mode 100644 index 000000000..028970c10 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/mime_types.cpp @@ -0,0 +1,46 @@ +// +// mime_types.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "mime_types.hpp" + +namespace http { +namespace server3 { +namespace mime_types { + +struct mapping +{ + const char* extension; + const char* mime_type; +} mappings[] = +{ + { "gif", "image/gif" }, + { "htm", "text/html" }, + { "html", "text/html" }, + { "jpg", "image/jpeg" }, + { "png", "image/png" }, + { 0, 0 } // Marks end of list. +}; + +std::string extension_to_type(const std::string& extension) +{ + for (mapping* m = mappings; m->extension; ++m) + { + if (m->extension == extension) + { + return m->mime_type; + } + } + + return "text/plain"; +} + +} // namespace mime_types +} // namespace server3 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server3/mime_types.hpp b/src/boost/libs/asio/example/cpp03/http/server3/mime_types.hpp new file mode 100644 index 000000000..9558f5e83 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/mime_types.hpp @@ -0,0 +1,27 @@ +// +// mime_types.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER3_MIME_TYPES_HPP +#define HTTP_SERVER3_MIME_TYPES_HPP + +#include <string> + +namespace http { +namespace server3 { +namespace mime_types { + +/// Convert a file extension into a MIME type. +std::string extension_to_type(const std::string& extension); + +} // namespace mime_types +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_MIME_TYPES_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server3/reply.cpp b/src/boost/libs/asio/example/cpp03/http/server3/reply.cpp new file mode 100644 index 000000000..0d4639200 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/reply.cpp @@ -0,0 +1,256 @@ +// +// reply.cpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "reply.hpp" +#include <string> +#include <boost/lexical_cast.hpp> + +namespace http { +namespace server3 { + +namespace status_strings { + +const std::string ok = + "HTTP/1.0 200 OK\r\n"; +const std::string created = + "HTTP/1.0 201 Created\r\n"; +const std::string accepted = + "HTTP/1.0 202 Accepted\r\n"; +const std::string no_content = + "HTTP/1.0 204 No Content\r\n"; +const std::string multiple_choices = + "HTTP/1.0 300 Multiple Choices\r\n"; +const std::string moved_permanently = + "HTTP/1.0 301 Moved Permanently\r\n"; +const std::string moved_temporarily = + "HTTP/1.0 302 Moved Temporarily\r\n"; +const std::string not_modified = + "HTTP/1.0 304 Not Modified\r\n"; +const std::string bad_request = + "HTTP/1.0 400 Bad Request\r\n"; +const std::string unauthorized = + "HTTP/1.0 401 Unauthorized\r\n"; +const std::string forbidden = + "HTTP/1.0 403 Forbidden\r\n"; +const std::string not_found = + "HTTP/1.0 404 Not Found\r\n"; +const std::string internal_server_error = + "HTTP/1.0 500 Internal Server Error\r\n"; +const std::string not_implemented = + "HTTP/1.0 501 Not Implemented\r\n"; +const std::string bad_gateway = + "HTTP/1.0 502 Bad Gateway\r\n"; +const std::string service_unavailable = + "HTTP/1.0 503 Service Unavailable\r\n"; + +boost::asio::const_buffer to_buffer(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return boost::asio::buffer(ok); + case reply::created: + return boost::asio::buffer(created); + case reply::accepted: + return boost::asio::buffer(accepted); + case reply::no_content: + return boost::asio::buffer(no_content); + case reply::multiple_choices: + return boost::asio::buffer(multiple_choices); + case reply::moved_permanently: + return boost::asio::buffer(moved_permanently); + case reply::moved_temporarily: + return boost::asio::buffer(moved_temporarily); + case reply::not_modified: + return boost::asio::buffer(not_modified); + case reply::bad_request: + return boost::asio::buffer(bad_request); + case reply::unauthorized: + return boost::asio::buffer(unauthorized); + case reply::forbidden: + return boost::asio::buffer(forbidden); + case reply::not_found: + return boost::asio::buffer(not_found); + case reply::internal_server_error: + return boost::asio::buffer(internal_server_error); + case reply::not_implemented: + return boost::asio::buffer(not_implemented); + case reply::bad_gateway: + return boost::asio::buffer(bad_gateway); + case reply::service_unavailable: + return boost::asio::buffer(service_unavailable); + default: + return boost::asio::buffer(internal_server_error); + } +} + +} // namespace status_strings + +namespace misc_strings { + +const char name_value_separator[] = { ':', ' ' }; +const char crlf[] = { '\r', '\n' }; + +} // namespace misc_strings + +std::vector<boost::asio::const_buffer> reply::to_buffers() +{ + std::vector<boost::asio::const_buffer> buffers; + buffers.push_back(status_strings::to_buffer(status)); + for (std::size_t i = 0; i < headers.size(); ++i) + { + header& h = headers[i]; + buffers.push_back(boost::asio::buffer(h.name)); + buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator)); + buffers.push_back(boost::asio::buffer(h.value)); + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + } + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + buffers.push_back(boost::asio::buffer(content)); + return buffers; +} + +namespace stock_replies { + +const char ok[] = ""; +const char created[] = + "<html>" + "<head><title>Created</title></head>" + "<body><h1>201 Created</h1></body>" + "</html>"; +const char accepted[] = + "<html>" + "<head><title>Accepted</title></head>" + "<body><h1>202 Accepted</h1></body>" + "</html>"; +const char no_content[] = + "<html>" + "<head><title>No Content</title></head>" + "<body><h1>204 Content</h1></body>" + "</html>"; +const char multiple_choices[] = + "<html>" + "<head><title>Multiple Choices</title></head>" + "<body><h1>300 Multiple Choices</h1></body>" + "</html>"; +const char moved_permanently[] = + "<html>" + "<head><title>Moved Permanently</title></head>" + "<body><h1>301 Moved Permanently</h1></body>" + "</html>"; +const char moved_temporarily[] = + "<html>" + "<head><title>Moved Temporarily</title></head>" + "<body><h1>302 Moved Temporarily</h1></body>" + "</html>"; +const char not_modified[] = + "<html>" + "<head><title>Not Modified</title></head>" + "<body><h1>304 Not Modified</h1></body>" + "</html>"; +const char bad_request[] = + "<html>" + "<head><title>Bad Request</title></head>" + "<body><h1>400 Bad Request</h1></body>" + "</html>"; +const char unauthorized[] = + "<html>" + "<head><title>Unauthorized</title></head>" + "<body><h1>401 Unauthorized</h1></body>" + "</html>"; +const char forbidden[] = + "<html>" + "<head><title>Forbidden</title></head>" + "<body><h1>403 Forbidden</h1></body>" + "</html>"; +const char not_found[] = + "<html>" + "<head><title>Not Found</title></head>" + "<body><h1>404 Not Found</h1></body>" + "</html>"; +const char internal_server_error[] = + "<html>" + "<head><title>Internal Server Error</title></head>" + "<body><h1>500 Internal Server Error</h1></body>" + "</html>"; +const char not_implemented[] = + "<html>" + "<head><title>Not Implemented</title></head>" + "<body><h1>501 Not Implemented</h1></body>" + "</html>"; +const char bad_gateway[] = + "<html>" + "<head><title>Bad Gateway</title></head>" + "<body><h1>502 Bad Gateway</h1></body>" + "</html>"; +const char service_unavailable[] = + "<html>" + "<head><title>Service Unavailable</title></head>" + "<body><h1>503 Service Unavailable</h1></body>" + "</html>"; + +std::string to_string(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return ok; + case reply::created: + return created; + case reply::accepted: + return accepted; + case reply::no_content: + return no_content; + case reply::multiple_choices: + return multiple_choices; + case reply::moved_permanently: + return moved_permanently; + case reply::moved_temporarily: + return moved_temporarily; + case reply::not_modified: + return not_modified; + case reply::bad_request: + return bad_request; + case reply::unauthorized: + return unauthorized; + case reply::forbidden: + return forbidden; + case reply::not_found: + return not_found; + case reply::internal_server_error: + return internal_server_error; + case reply::not_implemented: + return not_implemented; + case reply::bad_gateway: + return bad_gateway; + case reply::service_unavailable: + return service_unavailable; + default: + return internal_server_error; + } +} + +} // namespace stock_replies + +reply reply::stock_reply(reply::status_type status) +{ + reply rep; + rep.status = status; + rep.content = stock_replies::to_string(status); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = "text/html"; + return rep; +} + +} // namespace server3 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server3/reply.hpp b/src/boost/libs/asio/example/cpp03/http/server3/reply.hpp new file mode 100644 index 000000000..201578576 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/reply.hpp @@ -0,0 +1,64 @@ +// +// reply.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER3_REPLY_HPP +#define HTTP_SERVER3_REPLY_HPP + +#include <string> +#include <vector> +#include <boost/asio.hpp> +#include "header.hpp" + +namespace http { +namespace server3 { + +/// A reply to be sent to a client. +struct reply +{ + /// The status of the reply. + enum status_type + { + ok = 200, + created = 201, + accepted = 202, + no_content = 204, + multiple_choices = 300, + moved_permanently = 301, + moved_temporarily = 302, + not_modified = 304, + bad_request = 400, + unauthorized = 401, + forbidden = 403, + not_found = 404, + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503 + } status; + + /// The headers to be included in the reply. + std::vector<header> headers; + + /// The content to be sent in the reply. + std::string content; + + /// Convert the reply into a vector of buffers. The buffers do not own the + /// underlying memory blocks, therefore the reply object must remain valid and + /// not be changed until the write operation has completed. + std::vector<boost::asio::const_buffer> to_buffers(); + + /// Get a stock reply. + static reply stock_reply(status_type status); +}; + +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_REPLY_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server3/request.hpp b/src/boost/libs/asio/example/cpp03/http/server3/request.hpp new file mode 100644 index 000000000..300d379cc --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/request.hpp @@ -0,0 +1,34 @@ +// +// request.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER3_REQUEST_HPP +#define HTTP_SERVER3_REQUEST_HPP + +#include <string> +#include <vector> +#include "header.hpp" + +namespace http { +namespace server3 { + +/// A request received from a client. +struct request +{ + std::string method; + std::string uri; + int http_version_major; + int http_version_minor; + std::vector<header> headers; +}; + +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_REQUEST_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server3/request_handler.cpp b/src/boost/libs/asio/example/cpp03/http/server3/request_handler.cpp new file mode 100644 index 000000000..3046989f4 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/request_handler.cpp @@ -0,0 +1,122 @@ +// +// request_handler.cpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "request_handler.hpp" +#include <fstream> +#include <sstream> +#include <string> +#include <boost/lexical_cast.hpp> +#include "mime_types.hpp" +#include "reply.hpp" +#include "request.hpp" + +namespace http { +namespace server3 { + +request_handler::request_handler(const std::string& doc_root) + : doc_root_(doc_root) +{ +} + +void request_handler::handle_request(const request& req, reply& rep) +{ + // Decode url to path. + std::string request_path; + if (!url_decode(req.uri, request_path)) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // Request path must be absolute and not contain "..". + if (request_path.empty() || request_path[0] != '/' + || request_path.find("..") != std::string::npos) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // If path ends in slash (i.e. is a directory) then add "index.html". + if (request_path[request_path.size() - 1] == '/') + { + request_path += "index.html"; + } + + // Determine the file extension. + std::size_t last_slash_pos = request_path.find_last_of("/"); + std::size_t last_dot_pos = request_path.find_last_of("."); + std::string extension; + if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos) + { + extension = request_path.substr(last_dot_pos + 1); + } + + // Open the file to send back. + std::string full_path = doc_root_ + request_path; + std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary); + if (!is) + { + rep = reply::stock_reply(reply::not_found); + return; + } + + // Fill out the reply to be sent to the client. + rep.status = reply::ok; + char buf[512]; + while (is.read(buf, sizeof(buf)).gcount() > 0) + rep.content.append(buf, is.gcount()); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = mime_types::extension_to_type(extension); +} + +bool request_handler::url_decode(const std::string& in, std::string& out) +{ + out.clear(); + out.reserve(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) + { + if (in[i] == '%') + { + if (i + 3 <= in.size()) + { + int value = 0; + std::istringstream is(in.substr(i + 1, 2)); + if (is >> std::hex >> value) + { + out += static_cast<char>(value); + i += 2; + } + else + { + return false; + } + } + else + { + return false; + } + } + else if (in[i] == '+') + { + out += ' '; + } + else + { + out += in[i]; + } + } + return true; +} + +} // namespace server3 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server3/request_handler.hpp b/src/boost/libs/asio/example/cpp03/http/server3/request_handler.hpp new file mode 100644 index 000000000..75d415716 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/request_handler.hpp @@ -0,0 +1,46 @@ +// +// request_handler.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER3_REQUEST_HANDLER_HPP +#define HTTP_SERVER3_REQUEST_HANDLER_HPP + +#include <string> +#include <boost/noncopyable.hpp> + +namespace http { +namespace server3 { + +struct reply; +struct request; + +/// The common handler for all incoming requests. +class request_handler + : private boost::noncopyable +{ +public: + /// Construct with a directory containing files to be served. + explicit request_handler(const std::string& doc_root); + + /// Handle a request and produce a reply. + void handle_request(const request& req, reply& rep); + +private: + /// The directory containing the files to be served. + std::string doc_root_; + + /// Perform URL-decoding on a string. Returns false if the encoding was + /// invalid. + static bool url_decode(const std::string& in, std::string& out); +}; + +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_REQUEST_HANDLER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server3/request_parser.cpp b/src/boost/libs/asio/example/cpp03/http/server3/request_parser.cpp new file mode 100644 index 000000000..d70645512 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/request_parser.cpp @@ -0,0 +1,315 @@ +// +// request_parser.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "request_parser.hpp" +#include "request.hpp" + +namespace http { +namespace server3 { + +request_parser::request_parser() + : state_(method_start) +{ +} + +void request_parser::reset() +{ + state_ = method_start; +} + +boost::tribool request_parser::consume(request& req, char input) +{ + switch (state_) + { + case method_start: + if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + state_ = method; + req.method.push_back(input); + return boost::indeterminate; + } + case method: + if (input == ' ') + { + state_ = uri; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.method.push_back(input); + return boost::indeterminate; + } + case uri: + if (input == ' ') + { + state_ = http_version_h; + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + req.uri.push_back(input); + return boost::indeterminate; + } + case http_version_h: + if (input == 'H') + { + state_ = http_version_t_1; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_t_1: + if (input == 'T') + { + state_ = http_version_t_2; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_t_2: + if (input == 'T') + { + state_ = http_version_p; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_p: + if (input == 'P') + { + state_ = http_version_slash; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_slash: + if (input == '/') + { + req.http_version_major = 0; + req.http_version_minor = 0; + state_ = http_version_major_start; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_major_start: + if (is_digit(input)) + { + req.http_version_major = req.http_version_major * 10 + input - '0'; + state_ = http_version_major; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_major: + if (input == '.') + { + state_ = http_version_minor_start; + return boost::indeterminate; + } + else if (is_digit(input)) + { + req.http_version_major = req.http_version_major * 10 + input - '0'; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_minor_start: + if (is_digit(input)) + { + req.http_version_minor = req.http_version_minor * 10 + input - '0'; + state_ = http_version_minor; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_minor: + if (input == '\r') + { + state_ = expecting_newline_1; + return boost::indeterminate; + } + else if (is_digit(input)) + { + req.http_version_minor = req.http_version_minor * 10 + input - '0'; + return boost::indeterminate; + } + else + { + return false; + } + case expecting_newline_1: + if (input == '\n') + { + state_ = header_line_start; + return boost::indeterminate; + } + else + { + return false; + } + case header_line_start: + if (input == '\r') + { + state_ = expecting_newline_3; + return boost::indeterminate; + } + else if (!req.headers.empty() && (input == ' ' || input == '\t')) + { + state_ = header_lws; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.headers.push_back(header()); + req.headers.back().name.push_back(input); + state_ = header_name; + return boost::indeterminate; + } + case header_lws: + if (input == '\r') + { + state_ = expecting_newline_2; + return boost::indeterminate; + } + else if (input == ' ' || input == '\t') + { + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + state_ = header_value; + req.headers.back().value.push_back(input); + return boost::indeterminate; + } + case header_name: + if (input == ':') + { + state_ = space_before_header_value; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.headers.back().name.push_back(input); + return boost::indeterminate; + } + case space_before_header_value: + if (input == ' ') + { + state_ = header_value; + return boost::indeterminate; + } + else + { + return false; + } + case header_value: + if (input == '\r') + { + state_ = expecting_newline_2; + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + req.headers.back().value.push_back(input); + return boost::indeterminate; + } + case expecting_newline_2: + if (input == '\n') + { + state_ = header_line_start; + return boost::indeterminate; + } + else + { + return false; + } + case expecting_newline_3: + return (input == '\n'); + default: + return false; + } +} + +bool request_parser::is_char(int c) +{ + return c >= 0 && c <= 127; +} + +bool request_parser::is_ctl(int c) +{ + return (c >= 0 && c <= 31) || (c == 127); +} + +bool request_parser::is_tspecial(int c) +{ + switch (c) + { + case '(': case ')': case '<': case '>': case '@': + case ',': case ';': case ':': case '\\': case '"': + case '/': case '[': case ']': case '?': case '=': + case '{': case '}': case ' ': case '\t': + return true; + default: + return false; + } +} + +bool request_parser::is_digit(int c) +{ + return c >= '0' && c <= '9'; +} + +} // namespace server3 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server3/request_parser.hpp b/src/boost/libs/asio/example/cpp03/http/server3/request_parser.hpp new file mode 100644 index 000000000..f3ae654f4 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/request_parser.hpp @@ -0,0 +1,95 @@ +// +// request_parser.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER3_REQUEST_PARSER_HPP +#define HTTP_SERVER3_REQUEST_PARSER_HPP + +#include <boost/logic/tribool.hpp> +#include <boost/tuple/tuple.hpp> + +namespace http { +namespace server3 { + +struct request; + +/// Parser for incoming requests. +class request_parser +{ +public: + /// Construct ready to parse the request method. + request_parser(); + + /// Reset to initial parser state. + void reset(); + + /// Parse some data. The tribool return value is true when a complete request + /// has been parsed, false if the data is invalid, indeterminate when more + /// data is required. The InputIterator return value indicates how much of the + /// input has been consumed. + template <typename InputIterator> + boost::tuple<boost::tribool, InputIterator> parse(request& req, + InputIterator begin, InputIterator end) + { + while (begin != end) + { + boost::tribool result = consume(req, *begin++); + if (result || !result) + return boost::make_tuple(result, begin); + } + boost::tribool result = boost::indeterminate; + return boost::make_tuple(result, begin); + } + +private: + /// Handle the next character of input. + boost::tribool consume(request& req, char input); + + /// Check if a byte is an HTTP character. + static bool is_char(int c); + + /// Check if a byte is an HTTP control character. + static bool is_ctl(int c); + + /// Check if a byte is defined as an HTTP tspecial character. + static bool is_tspecial(int c); + + /// Check if a byte is a digit. + static bool is_digit(int c); + + /// The current state of the parser. + enum state + { + method_start, + method, + uri, + http_version_h, + http_version_t_1, + http_version_t_2, + http_version_p, + http_version_slash, + http_version_major_start, + http_version_major, + http_version_minor_start, + http_version_minor, + expecting_newline_1, + header_line_start, + header_lws, + header_name, + space_before_header_value, + header_value, + expecting_newline_2, + expecting_newline_3 + } state_; +}; + +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_REQUEST_PARSER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server3/server.cpp b/src/boost/libs/asio/example/cpp03/http/server3/server.cpp new file mode 100644 index 000000000..8035d3167 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/server.cpp @@ -0,0 +1,90 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "server.hpp" +#include <boost/thread/thread.hpp> +#include <boost/bind/bind.hpp> +#include <boost/shared_ptr.hpp> +#include <vector> + +namespace http { +namespace server3 { + +server::server(const std::string& address, const std::string& port, + const std::string& doc_root, std::size_t thread_pool_size) + : thread_pool_size_(thread_pool_size), + signals_(io_context_), + acceptor_(io_context_), + new_connection_(), + request_handler_(doc_root) +{ + // Register to handle the signals that indicate when the server should exit. + // It is safe to register for the same signal multiple times in a program, + // provided all registration for the specified signal is made through Asio. + signals_.add(SIGINT); + signals_.add(SIGTERM); +#if defined(SIGQUIT) + signals_.add(SIGQUIT); +#endif // defined(SIGQUIT) + signals_.async_wait(boost::bind(&server::handle_stop, this)); + + // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). + boost::asio::ip::tcp::resolver resolver(io_context_); + boost::asio::ip::tcp::endpoint endpoint = + *resolver.resolve(address, port).begin(); + acceptor_.open(endpoint.protocol()); + acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + acceptor_.bind(endpoint); + acceptor_.listen(); + + start_accept(); +} + +void server::run() +{ + // Create a pool of threads to run all of the io_contexts. + std::vector<boost::shared_ptr<boost::thread> > threads; + for (std::size_t i = 0; i < thread_pool_size_; ++i) + { + boost::shared_ptr<boost::thread> thread(new boost::thread( + boost::bind(&boost::asio::io_context::run, &io_context_))); + threads.push_back(thread); + } + + // Wait for all threads in the pool to exit. + for (std::size_t i = 0; i < threads.size(); ++i) + threads[i]->join(); +} + +void server::start_accept() +{ + new_connection_.reset(new connection(io_context_, request_handler_)); + acceptor_.async_accept(new_connection_->socket(), + boost::bind(&server::handle_accept, this, + boost::asio::placeholders::error)); +} + +void server::handle_accept(const boost::system::error_code& e) +{ + if (!e) + { + new_connection_->start(); + } + + start_accept(); +} + +void server::handle_stop() +{ + io_context_.stop(); +} + +} // namespace server3 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server3/server.hpp b/src/boost/libs/asio/example/cpp03/http/server3/server.hpp new file mode 100644 index 000000000..fc7c21d37 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server3/server.hpp @@ -0,0 +1,70 @@ +// +// server.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER3_SERVER_HPP +#define HTTP_SERVER3_SERVER_HPP + +#include <boost/asio.hpp> +#include <string> +#include <vector> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include "connection.hpp" +#include "request_handler.hpp" + +namespace http { +namespace server3 { + +/// The top-level class of the HTTP server. +class server + : private boost::noncopyable +{ +public: + /// Construct the server to listen on the specified TCP address and port, and + /// serve up files from the given directory. + explicit server(const std::string& address, const std::string& port, + const std::string& doc_root, std::size_t thread_pool_size); + + /// Run the server's io_context loop. + void run(); + +private: + /// Initiate an asynchronous accept operation. + void start_accept(); + + /// Handle completion of an asynchronous accept operation. + void handle_accept(const boost::system::error_code& e); + + /// Handle a request to stop the server. + void handle_stop(); + + /// The number of threads that will call io_context::run(). + std::size_t thread_pool_size_; + + /// The io_context used to perform asynchronous operations. + boost::asio::io_context io_context_; + + /// The signal_set is used to register for process termination notifications. + boost::asio::signal_set signals_; + + /// Acceptor used to listen for incoming connections. + boost::asio::ip::tcp::acceptor acceptor_; + + /// The next connection to be accepted. + connection_ptr new_connection_; + + /// The handler for all incoming requests. + request_handler request_handler_; +}; + +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_SERVER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server4/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/http/server4/Jamfile.v2 new file mode 100644 index 000000000..c103f1217 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server4/Jamfile.v2 @@ -0,0 +1,36 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe server + : file_handler.cpp + main.cpp + mime_types.cpp + reply.cpp + request_parser.cpp + server.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + /boost/thread//boost_thread + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/http/server4/file_handler.cpp b/src/boost/libs/asio/example/cpp03/http/server4/file_handler.cpp new file mode 100644 index 000000000..49537f9b6 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server4/file_handler.cpp @@ -0,0 +1,122 @@ +// +// file_handler.cpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "file_handler.hpp" +#include <fstream> +#include <sstream> +#include <string> +#include <boost/lexical_cast.hpp> +#include "mime_types.hpp" +#include "reply.hpp" +#include "request.hpp" + +namespace http { +namespace server4 { + +file_handler::file_handler(const std::string& doc_root) + : doc_root_(doc_root) +{ +} + +void file_handler::operator()(const request& req, reply& rep) +{ + // Decode url to path. + std::string request_path; + if (!url_decode(req.uri, request_path)) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // Request path must be absolute and not contain "..". + if (request_path.empty() || request_path[0] != '/' + || request_path.find("..") != std::string::npos) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // If path ends in slash (i.e. is a directory) then add "index.html". + if (request_path[request_path.size() - 1] == '/') + { + request_path += "index.html"; + } + + // Determine the file extension. + std::size_t last_slash_pos = request_path.find_last_of("/"); + std::size_t last_dot_pos = request_path.find_last_of("."); + std::string extension; + if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos) + { + extension = request_path.substr(last_dot_pos + 1); + } + + // Open the file to send back. + std::string full_path = doc_root_ + request_path; + std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary); + if (!is) + { + rep = reply::stock_reply(reply::not_found); + return; + } + + // Fill out the reply to be sent to the client. + rep.status = reply::ok; + char buf[512]; + while (is.read(buf, sizeof(buf)).gcount() > 0) + rep.content.append(buf, is.gcount()); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = mime_types::extension_to_type(extension); +} + +bool file_handler::url_decode(const std::string& in, std::string& out) +{ + out.clear(); + out.reserve(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) + { + if (in[i] == '%') + { + if (i + 3 <= in.size()) + { + int value = 0; + std::istringstream is(in.substr(i + 1, 2)); + if (is >> std::hex >> value) + { + out += static_cast<char>(value); + i += 2; + } + else + { + return false; + } + } + else + { + return false; + } + } + else if (in[i] == '+') + { + out += ' '; + } + else + { + out += in[i]; + } + } + return true; +} + +} // namespace server4 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server4/file_handler.hpp b/src/boost/libs/asio/example/cpp03/http/server4/file_handler.hpp new file mode 100644 index 000000000..ff9f0f887 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server4/file_handler.hpp @@ -0,0 +1,44 @@ +// +// file_handler.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER4_FILE_HANDLER_HPP +#define HTTP_SERVER4_FILE_HANDLER_HPP + +#include <string> + +namespace http { +namespace server4 { + +struct reply; +struct request; + +/// The common handler for all incoming requests. +class file_handler +{ +public: + /// Construct with a directory containing files to be served. + explicit file_handler(const std::string& doc_root); + + /// Handle a request and produce a reply. + void operator()(const request& req, reply& rep); + +private: + /// The directory containing the files to be served. + std::string doc_root_; + + /// Perform URL-decoding on a string. Returns false if the encoding was + /// invalid. + static bool url_decode(const std::string& in, std::string& out); +}; + +} // namespace server4 +} // namespace http + +#endif // HTTP_SERVER4_FILE_HANDLER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server4/header.hpp b/src/boost/libs/asio/example/cpp03/http/server4/header.hpp new file mode 100644 index 000000000..5a1373e21 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server4/header.hpp @@ -0,0 +1,28 @@ +// +// header.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER4_HEADER_HPP +#define HTTP_SERVER4_HEADER_HPP + +#include <string> + +namespace http { +namespace server4 { + +struct header +{ + std::string name; + std::string value; +}; + +} // namespace server4 +} // namespace http + +#endif // HTTP_SERVER4_HEADER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server4/main.cpp b/src/boost/libs/asio/example/cpp03/http/server4/main.cpp new file mode 100644 index 000000000..50efacd22 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server4/main.cpp @@ -0,0 +1,58 @@ +// +// main.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <boost/asio.hpp> +#include <boost/bind/bind.hpp> +#include <signal.h> +#include "server.hpp" +#include "file_handler.hpp" + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 4) + { + std::cerr << "Usage: http_server <address> <port> <doc_root>\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " receiver 0.0.0.0 80 .\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " receiver 0::0 80 .\n"; + return 1; + } + + boost::asio::io_context io_context; + + // Launch the initial server coroutine. + http::server4::server(io_context, argv[1], argv[2], + http::server4::file_handler(argv[3]))(); + + // Wait for signals indicating time to shut down. + boost::asio::signal_set signals(io_context); + signals.add(SIGINT); + signals.add(SIGTERM); +#if defined(SIGQUIT) + signals.add(SIGQUIT); +#endif // defined(SIGQUIT) + signals.async_wait(boost::bind( + &boost::asio::io_context::stop, &io_context)); + + // Run the server. + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/http/server4/mime_types.cpp b/src/boost/libs/asio/example/cpp03/http/server4/mime_types.cpp new file mode 100644 index 000000000..0d8ea5d5a --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server4/mime_types.cpp @@ -0,0 +1,46 @@ +// +// mime_types.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "mime_types.hpp" + +namespace http { +namespace server4 { +namespace mime_types { + +struct mapping +{ + const char* extension; + const char* mime_type; +} mappings[] = +{ + { "gif", "image/gif" }, + { "htm", "text/html" }, + { "html", "text/html" }, + { "jpg", "image/jpeg" }, + { "png", "image/png" }, + { 0, 0 } // Marks end of list. +}; + +std::string extension_to_type(const std::string& extension) +{ + for (mapping* m = mappings; m->extension; ++m) + { + if (m->extension == extension) + { + return m->mime_type; + } + } + + return "text/plain"; +} + +} // namespace mime_types +} // namespace server4 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server4/mime_types.hpp b/src/boost/libs/asio/example/cpp03/http/server4/mime_types.hpp new file mode 100644 index 000000000..d92ba1283 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server4/mime_types.hpp @@ -0,0 +1,27 @@ +// +// mime_types.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER4_MIME_TYPES_HPP +#define HTTP_SERVER4_MIME_TYPES_HPP + +#include <string> + +namespace http { +namespace server4 { +namespace mime_types { + +/// Convert a file extension into a MIME type. +std::string extension_to_type(const std::string& extension); + +} // namespace mime_types +} // namespace server4 +} // namespace http + +#endif // HTTP_SERVER4_MIME_TYPES_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server4/reply.cpp b/src/boost/libs/asio/example/cpp03/http/server4/reply.cpp new file mode 100644 index 000000000..be9481aeb --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server4/reply.cpp @@ -0,0 +1,256 @@ +// +// reply.cpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "reply.hpp" +#include <string> +#include <boost/lexical_cast.hpp> + +namespace http { +namespace server4 { + +namespace status_strings { + +const std::string ok = + "HTTP/1.0 200 OK\r\n"; +const std::string created = + "HTTP/1.0 201 Created\r\n"; +const std::string accepted = + "HTTP/1.0 202 Accepted\r\n"; +const std::string no_content = + "HTTP/1.0 204 No Content\r\n"; +const std::string multiple_choices = + "HTTP/1.0 300 Multiple Choices\r\n"; +const std::string moved_permanently = + "HTTP/1.0 301 Moved Permanently\r\n"; +const std::string moved_temporarily = + "HTTP/1.0 302 Moved Temporarily\r\n"; +const std::string not_modified = + "HTTP/1.0 304 Not Modified\r\n"; +const std::string bad_request = + "HTTP/1.0 400 Bad Request\r\n"; +const std::string unauthorized = + "HTTP/1.0 401 Unauthorized\r\n"; +const std::string forbidden = + "HTTP/1.0 403 Forbidden\r\n"; +const std::string not_found = + "HTTP/1.0 404 Not Found\r\n"; +const std::string internal_server_error = + "HTTP/1.0 500 Internal Server Error\r\n"; +const std::string not_implemented = + "HTTP/1.0 501 Not Implemented\r\n"; +const std::string bad_gateway = + "HTTP/1.0 502 Bad Gateway\r\n"; +const std::string service_unavailable = + "HTTP/1.0 503 Service Unavailable\r\n"; + +boost::asio::const_buffer to_buffer(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return boost::asio::buffer(ok); + case reply::created: + return boost::asio::buffer(created); + case reply::accepted: + return boost::asio::buffer(accepted); + case reply::no_content: + return boost::asio::buffer(no_content); + case reply::multiple_choices: + return boost::asio::buffer(multiple_choices); + case reply::moved_permanently: + return boost::asio::buffer(moved_permanently); + case reply::moved_temporarily: + return boost::asio::buffer(moved_temporarily); + case reply::not_modified: + return boost::asio::buffer(not_modified); + case reply::bad_request: + return boost::asio::buffer(bad_request); + case reply::unauthorized: + return boost::asio::buffer(unauthorized); + case reply::forbidden: + return boost::asio::buffer(forbidden); + case reply::not_found: + return boost::asio::buffer(not_found); + case reply::internal_server_error: + return boost::asio::buffer(internal_server_error); + case reply::not_implemented: + return boost::asio::buffer(not_implemented); + case reply::bad_gateway: + return boost::asio::buffer(bad_gateway); + case reply::service_unavailable: + return boost::asio::buffer(service_unavailable); + default: + return boost::asio::buffer(internal_server_error); + } +} + +} // namespace status_strings + +namespace misc_strings { + +const char name_value_separator[] = { ':', ' ' }; +const char crlf[] = { '\r', '\n' }; + +} // namespace misc_strings + +std::vector<boost::asio::const_buffer> reply::to_buffers() +{ + std::vector<boost::asio::const_buffer> buffers; + buffers.push_back(status_strings::to_buffer(status)); + for (std::size_t i = 0; i < headers.size(); ++i) + { + header& h = headers[i]; + buffers.push_back(boost::asio::buffer(h.name)); + buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator)); + buffers.push_back(boost::asio::buffer(h.value)); + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + } + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + buffers.push_back(boost::asio::buffer(content)); + return buffers; +} + +namespace stock_replies { + +const char ok[] = ""; +const char created[] = + "<html>" + "<head><title>Created</title></head>" + "<body><h1>201 Created</h1></body>" + "</html>"; +const char accepted[] = + "<html>" + "<head><title>Accepted</title></head>" + "<body><h1>202 Accepted</h1></body>" + "</html>"; +const char no_content[] = + "<html>" + "<head><title>No Content</title></head>" + "<body><h1>204 Content</h1></body>" + "</html>"; +const char multiple_choices[] = + "<html>" + "<head><title>Multiple Choices</title></head>" + "<body><h1>300 Multiple Choices</h1></body>" + "</html>"; +const char moved_permanently[] = + "<html>" + "<head><title>Moved Permanently</title></head>" + "<body><h1>301 Moved Permanently</h1></body>" + "</html>"; +const char moved_temporarily[] = + "<html>" + "<head><title>Moved Temporarily</title></head>" + "<body><h1>302 Moved Temporarily</h1></body>" + "</html>"; +const char not_modified[] = + "<html>" + "<head><title>Not Modified</title></head>" + "<body><h1>304 Not Modified</h1></body>" + "</html>"; +const char bad_request[] = + "<html>" + "<head><title>Bad Request</title></head>" + "<body><h1>400 Bad Request</h1></body>" + "</html>"; +const char unauthorized[] = + "<html>" + "<head><title>Unauthorized</title></head>" + "<body><h1>401 Unauthorized</h1></body>" + "</html>"; +const char forbidden[] = + "<html>" + "<head><title>Forbidden</title></head>" + "<body><h1>403 Forbidden</h1></body>" + "</html>"; +const char not_found[] = + "<html>" + "<head><title>Not Found</title></head>" + "<body><h1>404 Not Found</h1></body>" + "</html>"; +const char internal_server_error[] = + "<html>" + "<head><title>Internal Server Error</title></head>" + "<body><h1>500 Internal Server Error</h1></body>" + "</html>"; +const char not_implemented[] = + "<html>" + "<head><title>Not Implemented</title></head>" + "<body><h1>501 Not Implemented</h1></body>" + "</html>"; +const char bad_gateway[] = + "<html>" + "<head><title>Bad Gateway</title></head>" + "<body><h1>502 Bad Gateway</h1></body>" + "</html>"; +const char service_unavailable[] = + "<html>" + "<head><title>Service Unavailable</title></head>" + "<body><h1>503 Service Unavailable</h1></body>" + "</html>"; + +std::string to_string(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return ok; + case reply::created: + return created; + case reply::accepted: + return accepted; + case reply::no_content: + return no_content; + case reply::multiple_choices: + return multiple_choices; + case reply::moved_permanently: + return moved_permanently; + case reply::moved_temporarily: + return moved_temporarily; + case reply::not_modified: + return not_modified; + case reply::bad_request: + return bad_request; + case reply::unauthorized: + return unauthorized; + case reply::forbidden: + return forbidden; + case reply::not_found: + return not_found; + case reply::internal_server_error: + return internal_server_error; + case reply::not_implemented: + return not_implemented; + case reply::bad_gateway: + return bad_gateway; + case reply::service_unavailable: + return service_unavailable; + default: + return internal_server_error; + } +} + +} // namespace stock_replies + +reply reply::stock_reply(reply::status_type status) +{ + reply rep; + rep.status = status; + rep.content = stock_replies::to_string(status); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = "text/html"; + return rep; +} + +} // namespace server4 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server4/reply.hpp b/src/boost/libs/asio/example/cpp03/http/server4/reply.hpp new file mode 100644 index 000000000..6ca1d635d --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server4/reply.hpp @@ -0,0 +1,64 @@ +// +// reply.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER4_REPLY_HPP +#define HTTP_SERVER4_REPLY_HPP + +#include <string> +#include <vector> +#include <boost/asio.hpp> +#include "header.hpp" + +namespace http { +namespace server4 { + +/// A reply to be sent to a client. +struct reply +{ + /// The status of the reply. + enum status_type + { + ok = 200, + created = 201, + accepted = 202, + no_content = 204, + multiple_choices = 300, + moved_permanently = 301, + moved_temporarily = 302, + not_modified = 304, + bad_request = 400, + unauthorized = 401, + forbidden = 403, + not_found = 404, + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503 + } status; + + /// The headers to be included in the reply. + std::vector<header> headers; + + /// The content to be sent in the reply. + std::string content; + + /// Convert the reply into a vector of buffers. The buffers do not own the + /// underlying memory blocks, therefore the reply object must remain valid and + /// not be changed until the write operation has completed. + std::vector<boost::asio::const_buffer> to_buffers(); + + /// Get a stock reply. + static reply stock_reply(status_type status); +}; + +} // namespace server4 +} // namespace http + +#endif // HTTP_SERVER4_REPLY_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server4/request.hpp b/src/boost/libs/asio/example/cpp03/http/server4/request.hpp new file mode 100644 index 000000000..a50a21fd2 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server4/request.hpp @@ -0,0 +1,46 @@ +// +// request.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER4_REQUEST_HPP +#define HTTP_SERVER4_REQUEST_HPP + +#include <string> +#include <vector> +#include "header.hpp" + +namespace http { +namespace server4 { + +/// A request received from a client. +struct request +{ + /// The request method, e.g. "GET", "POST". + std::string method; + + /// The requested URI, such as a path to a file. + std::string uri; + + /// Major version number, usually 1. + int http_version_major; + + /// Minor version number, usually 0 or 1. + int http_version_minor; + + /// The headers included with the request. + std::vector<header> headers; + + /// The optional content sent with the request. + std::string content; +}; + +} // namespace server4 +} // namespace http + +#endif // HTTP_SERVER4_REQUEST_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server4/request_parser.cpp b/src/boost/libs/asio/example/cpp03/http/server4/request_parser.cpp new file mode 100644 index 000000000..17f0abb1c --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server4/request_parser.cpp @@ -0,0 +1,226 @@ +// +// request_parser.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "request_parser.hpp" +#include <algorithm> +#include <cctype> +#include <boost/lexical_cast.hpp> +#include "request.hpp" + +namespace http { +namespace server4 { + +// Enable the pseudo-keywords reenter, yield and fork. +#include <boost/asio/yield.hpp> + +std::string request_parser::content_length_name_ = "Content-Length"; + +boost::tribool request_parser::consume(request& req, char c) +{ + reenter (this) + { + req.method.clear(); + req.uri.clear(); + req.http_version_major = 0; + req.http_version_minor = 0; + req.headers.clear(); + req.content.clear(); + content_length_ = 0; + + // Request method. + while (is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != ' ') + { + req.method.push_back(c); + yield return boost::indeterminate; + } + if (req.method.empty()) + return false; + + // Space. + if (c != ' ') return false; + yield return boost::indeterminate; + + // URI. + while (!is_ctl(c) && c != ' ') + { + req.uri.push_back(c); + yield return boost::indeterminate; + } + if (req.uri.empty()) return false; + + // Space. + if (c != ' ') return false; + yield return boost::indeterminate; + + // HTTP protocol identifier. + if (c != 'H') return false; + yield return boost::indeterminate; + if (c != 'T') return false; + yield return boost::indeterminate; + if (c != 'T') return false; + yield return boost::indeterminate; + if (c != 'P') return false; + yield return boost::indeterminate; + + // Slash. + if (c != '/') return false; + yield return boost::indeterminate; + + // Major version number. + if (!is_digit(c)) return false; + while (is_digit(c)) + { + req.http_version_major = req.http_version_major * 10 + c - '0'; + yield return boost::indeterminate; + } + + // Dot. + if (c != '.') return false; + yield return boost::indeterminate; + + // Minor version number. + if (!is_digit(c)) return false; + while (is_digit(c)) + { + req.http_version_minor = req.http_version_minor * 10 + c - '0'; + yield return boost::indeterminate; + } + + // CRLF. + if (c != '\r') return false; + yield return boost::indeterminate; + if (c != '\n') return false; + yield return boost::indeterminate; + + // Headers. + while ((is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != '\r') + || (c == ' ' || c == '\t')) + { + if (c == ' ' || c == '\t') + { + // Leading whitespace. Must be continuation of previous header's value. + if (req.headers.empty()) return false; + while (c == ' ' || c == '\t') + yield return boost::indeterminate; + } + else + { + // Start the next header. + req.headers.push_back(header()); + + // Header name. + while (is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != ':') + { + req.headers.back().name.push_back(c); + yield return boost::indeterminate; + } + + // Colon and space separates the header name from the header value. + if (c != ':') return false; + yield return boost::indeterminate; + if (c != ' ') return false; + yield return boost::indeterminate; + } + + // Header value. + while (is_char(c) && !is_ctl(c) && c != '\r') + { + req.headers.back().value.push_back(c); + yield return boost::indeterminate; + } + + // CRLF. + if (c != '\r') return false; + yield return boost::indeterminate; + if (c != '\n') return false; + yield return boost::indeterminate; + } + + // CRLF. + if (c != '\r') return false; + yield return boost::indeterminate; + if (c != '\n') return false; + + // Check for optional Content-Length header. + for (std::size_t i = 0; i < req.headers.size(); ++i) + { + if (headers_equal(req.headers[i].name, content_length_name_)) + { + try + { + content_length_ = + boost::lexical_cast<std::size_t>(req.headers[i].value); + } + catch (boost::bad_lexical_cast&) + { + return false; + } + } + } + + // Content. + while (req.content.size() < content_length_) + { + yield return boost::indeterminate; + req.content.push_back(c); + } + } + + return true; +} + +// Disable the pseudo-keywords reenter, yield and fork. +#include <boost/asio/unyield.hpp> + +bool request_parser::is_char(int c) +{ + return c >= 0 && c <= 127; +} + +bool request_parser::is_ctl(int c) +{ + return (c >= 0 && c <= 31) || (c == 127); +} + +bool request_parser::is_tspecial(int c) +{ + switch (c) + { + case '(': case ')': case '<': case '>': case '@': + case ',': case ';': case ':': case '\\': case '"': + case '/': case '[': case ']': case '?': case '=': + case '{': case '}': case ' ': case '\t': + return true; + default: + return false; + } +} + +bool request_parser::is_digit(int c) +{ + return c >= '0' && c <= '9'; +} + +bool request_parser::tolower_compare(char a, char b) +{ + return std::tolower(a) == std::tolower(b); +} + +bool request_parser::headers_equal(const std::string& a, const std::string& b) +{ + if (a.length() != b.length()) + return false; + + return std::equal(a.begin(), a.end(), b.begin(), + &request_parser::tolower_compare); +} + +} // namespace server4 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server4/request_parser.hpp b/src/boost/libs/asio/example/cpp03/http/server4/request_parser.hpp new file mode 100644 index 000000000..f7beaebd4 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server4/request_parser.hpp @@ -0,0 +1,78 @@ +// +// request_parser.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER4_REQUEST_PARSER_HPP +#define HTTP_SERVER4_REQUEST_PARSER_HPP + +#include <string> +#include <boost/logic/tribool.hpp> +#include <boost/tuple/tuple.hpp> +#include <boost/asio/coroutine.hpp> + +namespace http { +namespace server4 { + +struct request; + +/// Parser for incoming requests. +class request_parser : boost::asio::coroutine +{ +public: + /// Parse some data. The tribool return value is true when a complete request + /// has been parsed, false if the data is invalid, indeterminate when more + /// data is required. The InputIterator return value indicates how much of the + /// input has been consumed. + template <typename InputIterator> + boost::tuple<boost::tribool, InputIterator> parse(request& req, + InputIterator begin, InputIterator end) + { + while (begin != end) + { + boost::tribool result = consume(req, *begin++); + if (result || !result) + return boost::make_tuple(result, begin); + } + boost::tribool result = boost::indeterminate; + return boost::make_tuple(result, begin); + } + +private: + /// The name of the content length header. + static std::string content_length_name_; + + /// Content length as decoded from headers. Defaults to 0. + std::size_t content_length_; + + /// Handle the next character of input. + boost::tribool consume(request& req, char input); + + /// Check if a byte is an HTTP character. + static bool is_char(int c); + + /// Check if a byte is an HTTP control character. + static bool is_ctl(int c); + + /// Check if a byte is defined as an HTTP tspecial character. + static bool is_tspecial(int c); + + /// Check if a byte is a digit. + static bool is_digit(int c); + + /// Check if two characters are equal, without regard to case. + static bool tolower_compare(char a, char b); + + /// Check whether the two request header names match. + bool headers_equal(const std::string& a, const std::string& b); +}; + +} // namespace server4 +} // namespace http + +#endif // HTTP_SERVER4_REQUEST_PARSER_HPP diff --git a/src/boost/libs/asio/example/cpp03/http/server4/server.cpp b/src/boost/libs/asio/example/cpp03/http/server4/server.cpp new file mode 100644 index 000000000..b6d109cad --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server4/server.cpp @@ -0,0 +1,122 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "server.hpp" +#include "request.hpp" +#include "reply.hpp" + +namespace http { +namespace server4 { + +server::server(boost::asio::io_context& io_context, + const std::string& address, const std::string& port, + boost::function<void(const request&, reply&)> request_handler) + : request_handler_(request_handler) +{ + tcp::resolver resolver(io_context); + boost::asio::ip::tcp::endpoint endpoint = + *resolver.resolve(address, port).begin(); + acceptor_.reset(new tcp::acceptor(io_context, endpoint)); +} + +// Enable the pseudo-keywords reenter, yield and fork. +#include <boost/asio/yield.hpp> + +void server::operator()(boost::system::error_code ec, std::size_t length) +{ + // In this example we keep the error handling code in one place by + // hoisting it outside the coroutine. An alternative approach would be to + // check the value of ec after each yield for an asynchronous operation. + if (!ec) + { + // On reentering a coroutine, control jumps to the location of the last + // yield or fork. The argument to the "reenter" pseudo-keyword can be a + // pointer or reference to an object of type coroutine. + reenter (this) + { + // Loop to accept incoming connections. + do + { + // Create a new socket for the next incoming connection. + socket_.reset(new tcp::socket(acceptor_->get_executor())); + + // Accept a new connection. The "yield" pseudo-keyword saves the current + // line number and exits the coroutine's "reenter" block. We use the + // server coroutine as the completion handler for the async_accept + // operation. When the asynchronous operation completes, the io_context + // invokes the function call operator, we "reenter" the coroutine, and + // then control resumes at the following line. + yield acceptor_->async_accept(*socket_, *this); + + // We "fork" by cloning a new server coroutine to handle the connection. + // After forking we have a parent coroutine and a child coroutine. Both + // parent and child continue execution at the following line. They can + // be distinguished using the functions coroutine::is_parent() and + // coroutine::is_child(). + fork server(*this)(); + + // The parent continues looping to accept the next incoming connection. + // The child exits the loop and processes the connection. + } while (is_parent()); + + // Create the objects needed to receive a request on the connection. + buffer_.reset(new boost::array<char, 8192>); + request_.reset(new request); + + // Loop until a complete request (or an invalid one) has been received. + do + { + // Receive some more data. When control resumes at the following line, + // the ec and length parameters reflect the result of the asynchronous + // operation. + yield socket_->async_read_some(boost::asio::buffer(*buffer_), *this); + + // Parse the data we just received. + boost::tie(valid_request_, boost::tuples::ignore) + = request_parser_.parse(*request_, + buffer_->data(), buffer_->data() + length); + + // An indeterminate result means we need more data, so keep looping. + } while (boost::indeterminate(valid_request_)); + + // Create the reply object that will be sent back to the client. + reply_.reset(new reply); + + if (valid_request_) + { + // A valid request was received. Call the user-supplied function object + // to process the request and compose a reply. + request_handler_(*request_, *reply_); + } + else + { + // The request was invalid. + *reply_ = reply::stock_reply(reply::bad_request); + } + + // Send the reply back to the client. + yield boost::asio::async_write(*socket_, reply_->to_buffers(), *this); + + // Initiate graceful connection closure. + socket_->shutdown(tcp::socket::shutdown_both, ec); + } + } + + // If an error occurs then the coroutine is not reentered. Consequently, no + // new asynchronous operations are started. This means that all shared_ptr + // references will disappear and the resources associated with the coroutine + // will be destroyed automatically after this function call returns. +} + +// Disable the pseudo-keywords reenter, yield and fork. +#include <boost/asio/unyield.hpp> + +} // namespace server4 +} // namespace http diff --git a/src/boost/libs/asio/example/cpp03/http/server4/server.hpp b/src/boost/libs/asio/example/cpp03/http/server4/server.hpp new file mode 100644 index 000000000..5be1f250a --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/http/server4/server.hpp @@ -0,0 +1,73 @@ +// +// server.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER4_SERVER_HPP +#define HTTP_SERVER4_SERVER_HPP + +#include <boost/asio.hpp> +#include <string> +#include <boost/array.hpp> +#include <boost/function.hpp> +#include <boost/shared_ptr.hpp> +#include "request_parser.hpp" + +namespace http { +namespace server4 { + +struct request; +struct reply; + +/// The top-level coroutine of the HTTP server. +class server : boost::asio::coroutine +{ +public: + /// Construct the server to listen on the specified TCP address and port, and + /// serve up files from the given directory. + explicit server(boost::asio::io_context& io_context, + const std::string& address, const std::string& port, + boost::function<void(const request&, reply&)> request_handler); + + /// Perform work associated with the server. + void operator()( + boost::system::error_code ec = boost::system::error_code(), + std::size_t length = 0); + +private: + typedef boost::asio::ip::tcp tcp; + + /// The user-supplied handler for all incoming requests. + boost::function<void(const request&, reply&)> request_handler_; + + /// Acceptor used to listen for incoming connections. + boost::shared_ptr<tcp::acceptor> acceptor_; + + /// The current connection from a client. + boost::shared_ptr<tcp::socket> socket_; + + /// Buffer for incoming data. + boost::shared_ptr<boost::array<char, 8192> > buffer_; + + /// The incoming request. + boost::shared_ptr<request> request_; + + /// Whether the request is valid or not. + boost::tribool valid_request_; + + /// The parser for the incoming request. + request_parser request_parser_; + + /// The reply to be sent back to the client. + boost::shared_ptr<reply> reply_; +}; + +} // namespace server4 +} // namespace http + +#endif // HTTP_SERVER4_SERVER_HPP diff --git a/src/boost/libs/asio/example/cpp03/icmp/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/icmp/Jamfile.v2 new file mode 100644 index 000000000..a7a2ac60d --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/icmp/Jamfile.v2 @@ -0,0 +1,30 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe ping + : ping.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/icmp/icmp_header.hpp b/src/boost/libs/asio/example/cpp03/icmp/icmp_header.hpp new file mode 100644 index 000000000..ddb992e62 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/icmp/icmp_header.hpp @@ -0,0 +1,94 @@ +// +// icmp_header.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef ICMP_HEADER_HPP +#define ICMP_HEADER_HPP + +#include <istream> +#include <ostream> +#include <algorithm> + +// ICMP header for both IPv4 and IPv6. +// +// The wire format of an ICMP header is: +// +// 0 8 16 31 +// +---------------+---------------+------------------------------+ --- +// | | | | ^ +// | type | code | checksum | | +// | | | | | +// +---------------+---------------+------------------------------+ 8 bytes +// | | | | +// | identifier | sequence number | | +// | | | v +// +-------------------------------+------------------------------+ --- + +class icmp_header +{ +public: + enum { echo_reply = 0, destination_unreachable = 3, source_quench = 4, + redirect = 5, echo_request = 8, time_exceeded = 11, parameter_problem = 12, + timestamp_request = 13, timestamp_reply = 14, info_request = 15, + info_reply = 16, address_request = 17, address_reply = 18 }; + + icmp_header() { std::fill(rep_, rep_ + sizeof(rep_), 0); } + + unsigned char type() const { return rep_[0]; } + unsigned char code() const { return rep_[1]; } + unsigned short checksum() const { return decode(2, 3); } + unsigned short identifier() const { return decode(4, 5); } + unsigned short sequence_number() const { return decode(6, 7); } + + void type(unsigned char n) { rep_[0] = n; } + void code(unsigned char n) { rep_[1] = n; } + void checksum(unsigned short n) { encode(2, 3, n); } + void identifier(unsigned short n) { encode(4, 5, n); } + void sequence_number(unsigned short n) { encode(6, 7, n); } + + friend std::istream& operator>>(std::istream& is, icmp_header& header) + { return is.read(reinterpret_cast<char*>(header.rep_), 8); } + + friend std::ostream& operator<<(std::ostream& os, const icmp_header& header) + { return os.write(reinterpret_cast<const char*>(header.rep_), 8); } + +private: + unsigned short decode(int a, int b) const + { return (rep_[a] << 8) + rep_[b]; } + + void encode(int a, int b, unsigned short n) + { + rep_[a] = static_cast<unsigned char>(n >> 8); + rep_[b] = static_cast<unsigned char>(n & 0xFF); + } + + unsigned char rep_[8]; +}; + +template <typename Iterator> +void compute_checksum(icmp_header& header, + Iterator body_begin, Iterator body_end) +{ + unsigned int sum = (header.type() << 8) + header.code() + + header.identifier() + header.sequence_number(); + + Iterator body_iter = body_begin; + while (body_iter != body_end) + { + sum += (static_cast<unsigned char>(*body_iter++) << 8); + if (body_iter != body_end) + sum += static_cast<unsigned char>(*body_iter++); + } + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + header.checksum(static_cast<unsigned short>(~sum)); +} + +#endif // ICMP_HEADER_HPP diff --git a/src/boost/libs/asio/example/cpp03/icmp/ipv4_header.hpp b/src/boost/libs/asio/example/cpp03/icmp/ipv4_header.hpp new file mode 100644 index 000000000..56adc2494 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/icmp/ipv4_header.hpp @@ -0,0 +1,102 @@ +// +// ipv4_header.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef IPV4_HEADER_HPP +#define IPV4_HEADER_HPP + +#include <algorithm> +#include <boost/asio/ip/address_v4.hpp> + +// Packet header for IPv4. +// +// The wire format of an IPv4 header is: +// +// 0 8 16 31 +// +-------+-------+---------------+------------------------------+ --- +// | | | | | ^ +// |version|header | type of | total length in bytes | | +// | (4) | length| service | | | +// +-------+-------+---------------+-+-+-+------------------------+ | +// | | | | | | | +// | identification |0|D|M| fragment offset | | +// | | |F|F| | | +// +---------------+---------------+-+-+-+------------------------+ | +// | | | | | +// | time to live | protocol | header checksum | 20 bytes +// | | | | | +// +---------------+---------------+------------------------------+ | +// | | | +// | source IPv4 address | | +// | | | +// +--------------------------------------------------------------+ | +// | | | +// | destination IPv4 address | | +// | | v +// +--------------------------------------------------------------+ --- +// | | ^ +// | | | +// / options (if any) / 0 - 40 +// / / bytes +// | | | +// | | v +// +--------------------------------------------------------------+ --- + +class ipv4_header +{ +public: + ipv4_header() { std::fill(rep_, rep_ + sizeof(rep_), 0); } + + unsigned char version() const { return (rep_[0] >> 4) & 0xF; } + unsigned short header_length() const { return (rep_[0] & 0xF) * 4; } + unsigned char type_of_service() const { return rep_[1]; } + unsigned short total_length() const { return decode(2, 3); } + unsigned short identification() const { return decode(4, 5); } + bool dont_fragment() const { return (rep_[6] & 0x40) != 0; } + bool more_fragments() const { return (rep_[6] & 0x20) != 0; } + unsigned short fragment_offset() const { return decode(6, 7) & 0x1FFF; } + unsigned int time_to_live() const { return rep_[8]; } + unsigned char protocol() const { return rep_[9]; } + unsigned short header_checksum() const { return decode(10, 11); } + + boost::asio::ip::address_v4 source_address() const + { + boost::asio::ip::address_v4::bytes_type bytes + = { { rep_[12], rep_[13], rep_[14], rep_[15] } }; + return boost::asio::ip::address_v4(bytes); + } + + boost::asio::ip::address_v4 destination_address() const + { + boost::asio::ip::address_v4::bytes_type bytes + = { { rep_[16], rep_[17], rep_[18], rep_[19] } }; + return boost::asio::ip::address_v4(bytes); + } + + friend std::istream& operator>>(std::istream& is, ipv4_header& header) + { + is.read(reinterpret_cast<char*>(header.rep_), 20); + if (header.version() != 4) + is.setstate(std::ios::failbit); + std::streamsize options_length = header.header_length() - 20; + if (options_length < 0 || options_length > 40) + is.setstate(std::ios::failbit); + else + is.read(reinterpret_cast<char*>(header.rep_) + 20, options_length); + return is; + } + +private: + unsigned short decode(int a, int b) const + { return (rep_[a] << 8) + rep_[b]; } + + unsigned char rep_[60]; +}; + +#endif // IPV4_HEADER_HPP diff --git a/src/boost/libs/asio/example/cpp03/icmp/ping.cpp b/src/boost/libs/asio/example/cpp03/icmp/ping.cpp new file mode 100644 index 000000000..7a3bfea21 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/icmp/ping.cpp @@ -0,0 +1,163 @@ +// +// ping.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio.hpp> +#include <boost/bind/bind.hpp> +#include <istream> +#include <iostream> +#include <ostream> + +#include "icmp_header.hpp" +#include "ipv4_header.hpp" + +using boost::asio::ip::icmp; +using boost::asio::steady_timer; +namespace chrono = boost::asio::chrono; + +class pinger +{ +public: + pinger(boost::asio::io_context& io_context, const char* destination) + : resolver_(io_context), socket_(io_context, icmp::v4()), + timer_(io_context), sequence_number_(0), num_replies_(0) + { + destination_ = *resolver_.resolve(icmp::v4(), destination, "").begin(); + + start_send(); + start_receive(); + } + +private: + void start_send() + { + std::string body("\"Hello!\" from Asio ping."); + + // Create an ICMP header for an echo request. + icmp_header echo_request; + echo_request.type(icmp_header::echo_request); + echo_request.code(0); + echo_request.identifier(get_identifier()); + echo_request.sequence_number(++sequence_number_); + compute_checksum(echo_request, body.begin(), body.end()); + + // Encode the request packet. + boost::asio::streambuf request_buffer; + std::ostream os(&request_buffer); + os << echo_request << body; + + // Send the request. + time_sent_ = steady_timer::clock_type::now(); + socket_.send_to(request_buffer.data(), destination_); + + // Wait up to five seconds for a reply. + num_replies_ = 0; + timer_.expires_at(time_sent_ + chrono::seconds(5)); + timer_.async_wait(boost::bind(&pinger::handle_timeout, this)); + } + + void handle_timeout() + { + if (num_replies_ == 0) + std::cout << "Request timed out" << std::endl; + + // Requests must be sent no less than one second apart. + timer_.expires_at(time_sent_ + chrono::seconds(1)); + timer_.async_wait(boost::bind(&pinger::start_send, this)); + } + + void start_receive() + { + // Discard any data already in the buffer. + reply_buffer_.consume(reply_buffer_.size()); + + // Wait for a reply. We prepare the buffer to receive up to 64KB. + socket_.async_receive(reply_buffer_.prepare(65536), + boost::bind(&pinger::handle_receive, this, boost::placeholders::_2)); + } + + void handle_receive(std::size_t length) + { + // The actual number of bytes received is committed to the buffer so that we + // can extract it using a std::istream object. + reply_buffer_.commit(length); + + // Decode the reply packet. + std::istream is(&reply_buffer_); + ipv4_header ipv4_hdr; + icmp_header icmp_hdr; + is >> ipv4_hdr >> icmp_hdr; + + // We can receive all ICMP packets received by the host, so we need to + // filter out only the echo replies that match the our identifier and + // expected sequence number. + if (is && icmp_hdr.type() == icmp_header::echo_reply + && icmp_hdr.identifier() == get_identifier() + && icmp_hdr.sequence_number() == sequence_number_) + { + // If this is the first reply, interrupt the five second timeout. + if (num_replies_++ == 0) + timer_.cancel(); + + // Print out some information about the reply packet. + chrono::steady_clock::time_point now = chrono::steady_clock::now(); + chrono::steady_clock::duration elapsed = now - time_sent_; + std::cout << length - ipv4_hdr.header_length() + << " bytes from " << ipv4_hdr.source_address() + << ": icmp_seq=" << icmp_hdr.sequence_number() + << ", ttl=" << ipv4_hdr.time_to_live() + << ", time=" + << chrono::duration_cast<chrono::milliseconds>(elapsed).count() + << std::endl; + } + + start_receive(); + } + + static unsigned short get_identifier() + { +#if defined(BOOST_ASIO_WINDOWS) + return static_cast<unsigned short>(::GetCurrentProcessId()); +#else + return static_cast<unsigned short>(::getpid()); +#endif + } + + icmp::resolver resolver_; + icmp::endpoint destination_; + icmp::socket socket_; + steady_timer timer_; + unsigned short sequence_number_; + chrono::steady_clock::time_point time_sent_; + boost::asio::streambuf reply_buffer_; + std::size_t num_replies_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: ping <host>" << std::endl; +#if !defined(BOOST_ASIO_WINDOWS) + std::cerr << "(You may need to run this program as root.)" << std::endl; +#endif + return 1; + } + + boost::asio::io_context io_context; + pinger p(io_context, argv[1]); + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + } +} diff --git a/src/boost/libs/asio/example/cpp03/invocation/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/invocation/Jamfile.v2 new file mode 100644 index 000000000..56beb37f0 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/invocation/Jamfile.v2 @@ -0,0 +1,30 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe prioritised_handlers + : prioritised_handlers.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/invocation/prioritised_handlers.cpp b/src/boost/libs/asio/example/cpp03/invocation/prioritised_handlers.cpp new file mode 100644 index 000000000..f0d201d83 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/invocation/prioritised_handlers.cpp @@ -0,0 +1,171 @@ +// +// prioritised_handlers.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio.hpp> +#include <boost/function.hpp> +#include <iostream> +#include <queue> + +using boost::asio::ip::tcp; + +class handler_priority_queue : public boost::asio::execution_context +{ +public: + void add(int priority, boost::function<void()> function) + { + handlers_.push(queued_handler(priority, function)); + } + + void execute_all() + { + while (!handlers_.empty()) + { + queued_handler handler = handlers_.top(); + handler.execute(); + handlers_.pop(); + } + } + + class executor + { + public: + executor(handler_priority_queue& q, int p) + : context_(q), priority_(p) + { + } + + handler_priority_queue& context() const + { + return context_; + } + + template <typename Function, typename Allocator> + void dispatch(const Function& f, const Allocator&) const + { + context_.add(priority_, f); + } + + template <typename Function, typename Allocator> + void post(const Function& f, const Allocator&) const + { + context_.add(priority_, f); + } + + template <typename Function, typename Allocator> + void defer(const Function& f, const Allocator&) const + { + context_.add(priority_, f); + } + + void on_work_started() const {} + void on_work_finished() const {} + + bool operator==(const executor& other) const + { + return &context_ == &other.context_ && priority_ == other.priority_; + } + + bool operator!=(const executor& other) const + { + return !operator==(other); + } + + private: + handler_priority_queue& context_; + int priority_; + }; + + template <typename Handler> + boost::asio::executor_binder<Handler, executor> + wrap(int priority, Handler handler) + { + return boost::asio::bind_executor(executor(*this, priority), handler); + } + +private: + class queued_handler + { + public: + queued_handler(int p, boost::function<void()> f) + : priority_(p), function_(f) + { + } + + void execute() + { + function_(); + } + + friend bool operator<(const queued_handler& a, + const queued_handler& b) + { + return a.priority_ < b.priority_; + } + + private: + int priority_; + boost::function<void()> function_; + }; + + std::priority_queue<queued_handler> handlers_; +}; + +//---------------------------------------------------------------------- + +void high_priority_handler(const boost::system::error_code& /*ec*/) +{ + std::cout << "High priority handler\n"; +} + +void middle_priority_handler(const boost::system::error_code& /*ec*/) +{ + std::cout << "Middle priority handler\n"; +} + +void low_priority_handler() +{ + std::cout << "Low priority handler\n"; +} + +int main() +{ + boost::asio::io_context io_context; + + handler_priority_queue pri_queue; + + // Post a completion handler to be run immediately. + boost::asio::post(io_context, pri_queue.wrap(0, low_priority_handler)); + + // Start an asynchronous accept that will complete immediately. + tcp::endpoint endpoint(boost::asio::ip::address_v4::loopback(), 0); + tcp::acceptor acceptor(io_context, endpoint); + tcp::socket server_socket(io_context); + acceptor.async_accept(server_socket, + pri_queue.wrap(100, high_priority_handler)); + tcp::socket client_socket(io_context); + client_socket.connect(acceptor.local_endpoint()); + + // Set a deadline timer to expire immediately. + boost::asio::steady_timer timer(io_context); + timer.expires_at(boost::asio::steady_timer::time_point::min()); + timer.async_wait(pri_queue.wrap(42, middle_priority_handler)); + + while (io_context.run_one()) + { + // The custom invocation hook adds the handlers to the priority queue + // rather than executing them from within the poll_one() call. + while (io_context.poll_one()) + ; + + pri_queue.execute_all(); + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/iostreams/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/iostreams/Jamfile.v2 new file mode 100644 index 000000000..ece413e23 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/iostreams/Jamfile.v2 @@ -0,0 +1,34 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/chrono//boost_chrono + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe daytime_client : daytime_client.cpp ; +exe daytime_server : daytime_server.cpp ; +exe http_client : http_client.cpp ; diff --git a/src/boost/libs/asio/example/cpp03/iostreams/daytime_client.cpp b/src/boost/libs/asio/example/cpp03/iostreams/daytime_client.cpp new file mode 100644 index 000000000..7abd1115f --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/iostreams/daytime_client.cpp @@ -0,0 +1,44 @@ +// +// daytime_client.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <string> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: daytime_client <host>" << std::endl; + return 1; + } + + tcp::iostream s(argv[1], "daytime"); + if (!s) + { + std::cout << "Unable to connect: " << s.error().message() << std::endl; + return 1; + } + + std::string line; + std::getline(s, line); + std::cout << line << std::endl; + } + catch (std::exception& e) + { + std::cout << "Exception: " << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/iostreams/daytime_server.cpp b/src/boost/libs/asio/example/cpp03/iostreams/daytime_server.cpp new file mode 100644 index 000000000..bcc0b1605 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/iostreams/daytime_server.cpp @@ -0,0 +1,51 @@ +// +// daytime_server.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <ctime> +#include <iostream> +#include <string> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +std::string make_daytime_string() +{ + using namespace std; // For time_t, time and ctime; + time_t now = time(0); + return ctime(&now); +} + +int main() +{ + try + { + boost::asio::io_context io_context; + + tcp::endpoint endpoint(tcp::v4(), 13); + tcp::acceptor acceptor(io_context, endpoint); + + for (;;) + { + tcp::iostream stream; + boost::system::error_code ec; + acceptor.accept(stream.socket(), ec); + if (!ec) + { + stream << make_daytime_string(); + } + } + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/iostreams/http_client.cpp b/src/boost/libs/asio/example/cpp03/iostreams/http_client.cpp new file mode 100644 index 000000000..bc5613a03 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/iostreams/http_client.cpp @@ -0,0 +1,91 @@ +// +// http_client.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <istream> +#include <ostream> +#include <string> +#include <boost/asio/ip/tcp.hpp> + +using boost::asio::ip::tcp; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cout << "Usage: http_client <server> <path>\n"; + std::cout << "Example:\n"; + std::cout << " http_client www.boost.org /LICENSE_1_0.txt\n"; + return 1; + } + + boost::asio::ip::tcp::iostream s; + + // The entire sequence of I/O operations must complete within 60 seconds. + // If an expiry occurs, the socket is automatically closed and the stream + // becomes bad. + s.expires_after(boost::asio::chrono::seconds(60)); + + // Establish a connection to the server. + s.connect(argv[1], "http"); + if (!s) + { + std::cout << "Unable to connect: " << s.error().message() << "\n"; + return 1; + } + + // Send the request. We specify the "Connection: close" header so that the + // server will close the socket after transmitting the response. This will + // allow us to treat all data up until the EOF as the content. + s << "GET " << argv[2] << " HTTP/1.0\r\n"; + s << "Host: " << argv[1] << "\r\n"; + s << "Accept: */*\r\n"; + s << "Connection: close\r\n\r\n"; + + // By default, the stream is tied with itself. This means that the stream + // automatically flush the buffered output before attempting a read. It is + // not necessary not explicitly flush the stream at this point. + + // Check that response is OK. + std::string http_version; + s >> http_version; + unsigned int status_code; + s >> status_code; + std::string status_message; + std::getline(s, status_message); + if (!s || http_version.substr(0, 5) != "HTTP/") + { + std::cout << "Invalid response\n"; + return 1; + } + if (status_code != 200) + { + std::cout << "Response returned with status code " << status_code << "\n"; + return 1; + } + + // Process the response headers, which are terminated by a blank line. + std::string header; + while (std::getline(s, header) && header != "\r") + std::cout << header << "\n"; + std::cout << "\n"; + + // Write the remaining data to output. + std::cout << s.rdbuf(); + } + catch (std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/local/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/local/Jamfile.v2 new file mode 100644 index 000000000..05ef40b90 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/local/Jamfile.v2 @@ -0,0 +1,82 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe connect_pair + : connect_pair.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + /boost/thread//boost_thread + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe iostream_client + : iostream_client.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe stream_client + : stream_client.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe stream_server + : stream_server.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/local/connect_pair.cpp b/src/boost/libs/asio/example/cpp03/local/connect_pair.cpp new file mode 100644 index 000000000..7901783b6 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/local/connect_pair.cpp @@ -0,0 +1,142 @@ +// +// connect_pair.cpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <string> +#include <cctype> +#include <boost/asio.hpp> +#include <boost/thread/thread.hpp> +#include <boost/array.hpp> +#include <boost/bind/bind.hpp> + +#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) + +using boost::asio::local::stream_protocol; + +class uppercase_filter +{ +public: + uppercase_filter(boost::asio::io_context& io_context) + : socket_(io_context) + { + } + + stream_protocol::socket& socket() + { + return socket_; + } + + void start() + { + // Wait for request. + socket_.async_read_some(boost::asio::buffer(data_), + boost::bind(&uppercase_filter::handle_read, + this, boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + +private: + void handle_read(const boost::system::error_code& ec, std::size_t size) + { + if (!ec) + { + // Compute result. + for (std::size_t i = 0; i < size; ++i) + data_[i] = std::toupper(data_[i]); + + // Send result. + boost::asio::async_write(socket_, boost::asio::buffer(data_, size), + boost::bind(&uppercase_filter::handle_write, + this, boost::asio::placeholders::error)); + } + else + { + throw boost::system::system_error(ec); + } + } + + void handle_write(const boost::system::error_code& ec) + { + if (!ec) + { + // Wait for request. + socket_.async_read_some(boost::asio::buffer(data_), + boost::bind(&uppercase_filter::handle_read, + this, boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + else + { + throw boost::system::system_error(ec); + } + } + + stream_protocol::socket socket_; + boost::array<char, 512> data_; +}; + +void run(boost::asio::io_context* io_context) +{ + try + { + io_context->run(); + } + catch (std::exception& e) + { + std::cerr << "Exception in thread: " << e.what() << "\n"; + std::exit(1); + } +} + +int main() +{ + try + { + boost::asio::io_context io_context; + + // Create filter and establish a connection to it. + uppercase_filter filter(io_context); + stream_protocol::socket socket(io_context); + boost::asio::local::connect_pair(socket, filter.socket()); + filter.start(); + + // The io_context runs in a background thread to perform filtering. + boost::thread thread(boost::bind(run, &io_context)); + + for (;;) + { + // Collect request from user. + std::cout << "Enter a string: "; + std::string request; + std::getline(std::cin, request); + + // Send request to filter. + boost::asio::write(socket, boost::asio::buffer(request)); + + // Wait for reply from filter. + std::vector<char> reply(request.size()); + boost::asio::read(socket, boost::asio::buffer(reply)); + + // Show reply to user. + std::cout << "Result: "; + std::cout.write(&reply[0], request.size()); + std::cout << std::endl; + } + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + std::exit(1); + } +} + +#else // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) +# error Local sockets not available on this platform. +#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) diff --git a/src/boost/libs/asio/example/cpp03/local/iostream_client.cpp b/src/boost/libs/asio/example/cpp03/local/iostream_client.cpp new file mode 100644 index 000000000..4e61abb47 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/local/iostream_client.cpp @@ -0,0 +1,62 @@ +// +// stream_client.cpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstring> +#include <iostream> +#include <boost/asio.hpp> + +#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) + +using boost::asio::local::stream_protocol; + +enum { max_length = 1024 }; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: iostream_client <file>\n"; + return 1; + } + + stream_protocol::endpoint ep(argv[1]); + stream_protocol::iostream s(ep); + if (!s) + { + std::cerr << "Unable to connect: " << s.error().message() << std::endl; + return 1; + } + + using namespace std; // For strlen. + std::cout << "Enter message: "; + char request[max_length]; + std::cin.getline(request, max_length); + size_t length = strlen(request); + s << request; + + char reply[max_length]; + s.read(reply, length); + std::cout << "Reply is: "; + std::cout.write(reply, length); + std::cout << "\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} + +#else // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) +# error Local sockets not available on this platform. +#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) diff --git a/src/boost/libs/asio/example/cpp03/local/stream_client.cpp b/src/boost/libs/asio/example/cpp03/local/stream_client.cpp new file mode 100644 index 000000000..2a62bdee2 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/local/stream_client.cpp @@ -0,0 +1,61 @@ +// +// stream_client.cpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <boost/asio.hpp> + +#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) + +using boost::asio::local::stream_protocol; + +enum { max_length = 1024 }; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: stream_client <file>\n"; + return 1; + } + + boost::asio::io_context io_context; + + stream_protocol::socket s(io_context); + s.connect(stream_protocol::endpoint(argv[1])); + + using namespace std; // For strlen. + std::cout << "Enter message: "; + char request[max_length]; + std::cin.getline(request, max_length); + size_t request_length = strlen(request); + boost::asio::write(s, boost::asio::buffer(request, request_length)); + + char reply[max_length]; + size_t reply_length = boost::asio::read(s, + boost::asio::buffer(reply, request_length)); + std::cout << "Reply is: "; + std::cout.write(reply, reply_length); + std::cout << "\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} + +#else // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) +# error Local sockets not available on this platform. +#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) diff --git a/src/boost/libs/asio/example/cpp03/local/stream_server.cpp b/src/boost/libs/asio/example/cpp03/local/stream_server.cpp new file mode 100644 index 000000000..7d309ef2a --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/local/stream_server.cpp @@ -0,0 +1,141 @@ +// +// stream_server.cpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdio> +#include <iostream> +#include <boost/array.hpp> +#include <boost/bind/bind.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/asio.hpp> + +#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) + +using boost::asio::local::stream_protocol; + +class session + : public boost::enable_shared_from_this<session> +{ +public: + session(boost::asio::io_context& io_context) + : socket_(io_context) + { + } + + stream_protocol::socket& socket() + { + return socket_; + } + + void start() + { + socket_.async_read_some(boost::asio::buffer(data_), + boost::bind(&session::handle_read, + shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + + void handle_read(const boost::system::error_code& error, + size_t bytes_transferred) + { + if (!error) + { + boost::asio::async_write(socket_, + boost::asio::buffer(data_, bytes_transferred), + boost::bind(&session::handle_write, + shared_from_this(), + boost::asio::placeholders::error)); + } + } + + void handle_write(const boost::system::error_code& error) + { + if (!error) + { + socket_.async_read_some(boost::asio::buffer(data_), + boost::bind(&session::handle_read, + shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + } + +private: + // The socket used to communicate with the client. + stream_protocol::socket socket_; + + // Buffer used to store data received from the client. + boost::array<char, 1024> data_; +}; + +typedef boost::shared_ptr<session> session_ptr; + +class server +{ +public: + server(boost::asio::io_context& io_context, const std::string& file) + : io_context_(io_context), + acceptor_(io_context, stream_protocol::endpoint(file)) + { + session_ptr new_session(new session(io_context_)); + acceptor_.async_accept(new_session->socket(), + boost::bind(&server::handle_accept, this, new_session, + boost::asio::placeholders::error)); + } + + void handle_accept(session_ptr new_session, + const boost::system::error_code& error) + { + if (!error) + { + new_session->start(); + } + + new_session.reset(new session(io_context_)); + acceptor_.async_accept(new_session->socket(), + boost::bind(&server::handle_accept, this, new_session, + boost::asio::placeholders::error)); + } + +private: + boost::asio::io_context& io_context_; + stream_protocol::acceptor acceptor_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: stream_server <file>\n"; + std::cerr << "*** WARNING: existing file is removed ***\n"; + return 1; + } + + boost::asio::io_context io_context; + + std::remove(argv[1]); + server s(io_context, argv[1]); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} + +#else // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) +# error Local sockets not available on this platform. +#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) diff --git a/src/boost/libs/asio/example/cpp03/multicast/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/multicast/Jamfile.v2 new file mode 100644 index 000000000..909903a79 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/multicast/Jamfile.v2 @@ -0,0 +1,33 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/chrono//boost_chrono + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe receiver : receiver.cpp ; +exe sender : sender.cpp ; diff --git a/src/boost/libs/asio/example/cpp03/multicast/receiver.cpp b/src/boost/libs/asio/example/cpp03/multicast/receiver.cpp new file mode 100644 index 000000000..f6da0138d --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/multicast/receiver.cpp @@ -0,0 +1,93 @@ +// +// receiver.cpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <string> +#include <boost/asio.hpp> +#include "boost/bind/bind.hpp" + +const short multicast_port = 30001; + +class receiver +{ +public: + receiver(boost::asio::io_context& io_context, + const boost::asio::ip::address& listen_address, + const boost::asio::ip::address& multicast_address) + : socket_(io_context) + { + // Create the socket so that multiple may be bound to the same address. + boost::asio::ip::udp::endpoint listen_endpoint( + listen_address, multicast_port); + socket_.open(listen_endpoint.protocol()); + socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true)); + socket_.bind(listen_endpoint); + + // Join the multicast group. + socket_.set_option( + boost::asio::ip::multicast::join_group(multicast_address)); + + socket_.async_receive_from( + boost::asio::buffer(data_, max_length), sender_endpoint_, + boost::bind(&receiver::handle_receive_from, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + + void handle_receive_from(const boost::system::error_code& error, + size_t bytes_recvd) + { + if (!error) + { + std::cout.write(data_, bytes_recvd); + std::cout << std::endl; + + socket_.async_receive_from( + boost::asio::buffer(data_, max_length), sender_endpoint_, + boost::bind(&receiver::handle_receive_from, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + } + +private: + boost::asio::ip::udp::socket socket_; + boost::asio::ip::udp::endpoint sender_endpoint_; + enum { max_length = 1024 }; + char data_[max_length]; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: receiver <listen_address> <multicast_address>\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " receiver 0.0.0.0 239.255.0.1\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " receiver 0::0 ff31::8000:1234\n"; + return 1; + } + + boost::asio::io_context io_context; + receiver r(io_context, + boost::asio::ip::make_address(argv[1]), + boost::asio::ip::make_address(argv[2])); + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/multicast/sender.cpp b/src/boost/libs/asio/example/cpp03/multicast/sender.cpp new file mode 100644 index 000000000..256eb482f --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/multicast/sender.cpp @@ -0,0 +1,98 @@ +// +// sender.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <sstream> +#include <string> +#include <boost/asio.hpp> +#include "boost/bind/bind.hpp" + +const short multicast_port = 30001; +const int max_message_count = 10; + +class sender +{ +public: + sender(boost::asio::io_context& io_context, + const boost::asio::ip::address& multicast_address) + : endpoint_(multicast_address, multicast_port), + socket_(io_context, endpoint_.protocol()), + timer_(io_context), + message_count_(0) + { + std::ostringstream os; + os << "Message " << message_count_++; + message_ = os.str(); + + socket_.async_send_to( + boost::asio::buffer(message_), endpoint_, + boost::bind(&sender::handle_send_to, this, + boost::asio::placeholders::error)); + } + + void handle_send_to(const boost::system::error_code& error) + { + if (!error && message_count_ < max_message_count) + { + timer_.expires_after(boost::asio::chrono::seconds(1)); + timer_.async_wait( + boost::bind(&sender::handle_timeout, this, + boost::asio::placeholders::error)); + } + } + + void handle_timeout(const boost::system::error_code& error) + { + if (!error) + { + std::ostringstream os; + os << "Message " << message_count_++; + message_ = os.str(); + + socket_.async_send_to( + boost::asio::buffer(message_), endpoint_, + boost::bind(&sender::handle_send_to, this, + boost::asio::placeholders::error)); + } + } + +private: + boost::asio::ip::udp::endpoint endpoint_; + boost::asio::ip::udp::socket socket_; + boost::asio::steady_timer timer_; + int message_count_; + std::string message_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: sender <multicast_address>\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " sender 239.255.0.1\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " sender ff31::8000:1234\n"; + return 1; + } + + boost::asio::io_context io_context; + sender s(io_context, boost::asio::ip::make_address(argv[1])); + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/nonblocking/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/nonblocking/Jamfile.v2 new file mode 100644 index 000000000..0c4c30bae --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/nonblocking/Jamfile.v2 @@ -0,0 +1,30 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe third_party_lib + : third_party_lib.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/nonblocking/third_party_lib.cpp b/src/boost/libs/asio/example/cpp03/nonblocking/third_party_lib.cpp new file mode 100644 index 000000000..86b11c6dd --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/nonblocking/third_party_lib.cpp @@ -0,0 +1,240 @@ +// +// third_party_lib.cpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio.hpp> +#include <boost/array.hpp> +#include <boost/bind/bind.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <iostream> + +using boost::asio::ip::tcp; + +namespace third_party_lib { + +// Simulation of a third party library that wants to perform read and write +// operations directly on a socket. It needs to be polled to determine whether +// it requires a read or write operation, and notified when the socket is ready +// for reading or writing. +class session +{ +public: + session(tcp::socket& socket) + : socket_(socket), + state_(reading) + { + } + + // Returns true if the third party library wants to be notified when the + // socket is ready for reading. + bool want_read() const + { + return state_ == reading; + } + + // Notify that third party library that it should perform its read operation. + void do_read(boost::system::error_code& ec) + { + if (std::size_t len = socket_.read_some(boost::asio::buffer(data_), ec)) + { + write_buffer_ = boost::asio::buffer(data_, len); + state_ = writing; + } + } + + // Returns true if the third party library wants to be notified when the + // socket is ready for writing. + bool want_write() const + { + return state_ == writing; + } + + // Notify that third party library that it should perform its write operation. + void do_write(boost::system::error_code& ec) + { + if (std::size_t len = socket_.write_some( + boost::asio::buffer(write_buffer_), ec)) + { + write_buffer_ = write_buffer_ + len; + state_ = boost::asio::buffer_size(write_buffer_) > 0 ? writing : reading; + } + } + +private: + tcp::socket& socket_; + enum { reading, writing } state_; + boost::array<char, 128> data_; + boost::asio::const_buffer write_buffer_; +}; + +} // namespace third_party_lib + +// The glue between asio's sockets and the third party library. +class connection + : public boost::enable_shared_from_this<connection> +{ +public: + typedef boost::shared_ptr<connection> pointer; + + static pointer create(const boost::asio::executor& ex) + { + return pointer(new connection(ex)); + } + + tcp::socket& socket() + { + return socket_; + } + + void start() + { + // Put the socket into non-blocking mode. + socket_.non_blocking(true); + + start_operations(); + } + +private: + connection(const boost::asio::executor& ex) + : socket_(ex), + session_impl_(socket_), + read_in_progress_(false), + write_in_progress_(false) + { + } + + void start_operations() + { + // Start a read operation if the third party library wants one. + if (session_impl_.want_read() && !read_in_progress_) + { + read_in_progress_ = true; + socket_.async_wait(tcp::socket::wait_read, + boost::bind(&connection::handle_read, + shared_from_this(), + boost::asio::placeholders::error)); + } + + // Start a write operation if the third party library wants one. + if (session_impl_.want_write() && !write_in_progress_) + { + write_in_progress_ = true; + socket_.async_wait(tcp::socket::wait_write, + boost::bind(&connection::handle_write, + shared_from_this(), + boost::asio::placeholders::error)); + } + } + + void handle_read(boost::system::error_code ec) + { + read_in_progress_ = false; + + // Notify third party library that it can perform a read. + if (!ec) + session_impl_.do_read(ec); + + // The third party library successfully performed a read on the socket. + // Start new read or write operations based on what it now wants. + if (!ec || ec == boost::asio::error::would_block) + start_operations(); + + // Otherwise, an error occurred. Closing the socket cancels any outstanding + // asynchronous read or write operations. The connection object will be + // destroyed automatically once those outstanding operations complete. + else + socket_.close(); + } + + void handle_write(boost::system::error_code ec) + { + write_in_progress_ = false; + + // Notify third party library that it can perform a write. + if (!ec) + session_impl_.do_write(ec); + + // The third party library successfully performed a write on the socket. + // Start new read or write operations based on what it now wants. + if (!ec || ec == boost::asio::error::would_block) + start_operations(); + + // Otherwise, an error occurred. Closing the socket cancels any outstanding + // asynchronous read or write operations. The connection object will be + // destroyed automatically once those outstanding operations complete. + else + socket_.close(); + } + +private: + tcp::socket socket_; + third_party_lib::session session_impl_; + bool read_in_progress_; + bool write_in_progress_; +}; + +class server +{ +public: + server(boost::asio::io_context& io_context, unsigned short port) + : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) + { + start_accept(); + } + +private: + void start_accept() + { + connection::pointer new_connection = + connection::create(acceptor_.get_executor()); + + acceptor_.async_accept(new_connection->socket(), + boost::bind(&server::handle_accept, this, new_connection, + boost::asio::placeholders::error)); + } + + void handle_accept(connection::pointer new_connection, + const boost::system::error_code& error) + { + if (!error) + { + new_connection->start(); + } + + start_accept(); + } + + tcp::acceptor acceptor_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: third_party_lib <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + using namespace std; // For atoi. + server s(io_context, atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/porthopper/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/porthopper/Jamfile.v2 new file mode 100644 index 000000000..64d070c28 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/porthopper/Jamfile.v2 @@ -0,0 +1,33 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/chrono//boost_chrono + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe client : client.cpp ; +exe server : server.cpp ; diff --git a/src/boost/libs/asio/example/cpp03/porthopper/client.cpp b/src/boost/libs/asio/example/cpp03/porthopper/client.cpp new file mode 100644 index 000000000..fc258515d --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/porthopper/client.cpp @@ -0,0 +1,192 @@ +// +// client.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio.hpp> +#include <boost/lambda/lambda.hpp> +#include <boost/lambda/bind.hpp> +#include <boost/lambda/if.hpp> +#include <boost/shared_ptr.hpp> +#include <algorithm> +#include <cstdlib> +#include <exception> +#include <iostream> +#include <string> +#include "protocol.hpp" + +using namespace boost; +using boost::asio::ip::tcp; +using boost::asio::ip::udp; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: client <host> <port>\n"; + return 1; + } + using namespace std; // For atoi. + std::string host_name = argv[1]; + std::string port = argv[2]; + + boost::asio::io_context io_context; + + // Determine the location of the server. + tcp::resolver resolver(io_context); + tcp::endpoint remote_endpoint = *resolver.resolve(host_name, port).begin(); + + // Establish the control connection to the server. + tcp::socket control_socket(io_context); + control_socket.connect(remote_endpoint); + + // Create a datagram socket to receive data from the server. + boost::shared_ptr<udp::socket> data_socket( + new udp::socket(io_context, udp::endpoint(udp::v4(), 0))); + + // Determine what port we will receive data on. + udp::endpoint data_endpoint = data_socket->local_endpoint(); + + // Ask the server to start sending us data. + control_request start = control_request::start(data_endpoint.port()); + boost::asio::write(control_socket, start.to_buffers()); + + unsigned long last_frame_number = 0; + for (;;) + { + // Receive 50 messages on the current data socket. + for (int i = 0; i < 50; ++i) + { + // Receive a frame from the server. + frame f; + data_socket->receive(f.to_buffers(), 0); + if (f.number() > last_frame_number) + { + last_frame_number = f.number(); + std::cout << "\n" << f.payload(); + } + } + + // Time to switch to a new socket. To ensure seamless handover we will + // continue to receive packets using the old socket until data arrives on + // the new one. + std::cout << " Starting renegotiation"; + + // Create the new data socket. + boost::shared_ptr<udp::socket> new_data_socket( + new udp::socket(io_context, udp::endpoint(udp::v4(), 0))); + + // Determine the new port we will use to receive data. + udp::endpoint new_data_endpoint = new_data_socket->local_endpoint(); + + // Ask the server to switch over to the new port. + control_request change = control_request::change( + data_endpoint.port(), new_data_endpoint.port()); + boost::system::error_code control_result; + boost::asio::async_write(control_socket, change.to_buffers(), + ( + lambda::var(control_result) = lambda::_1 + )); + + // Try to receive a frame from the server on the new data socket. If we + // successfully receive a frame on this new data socket we can consider + // the renegotation complete. In that case we will close the old data + // socket, which will cause any outstanding receive operation on it to be + // cancelled. + frame f1; + boost::system::error_code new_data_socket_result; + new_data_socket->async_receive(f1.to_buffers(), + ( + // Note: lambda::_1 is the first argument to the callback handler, + // which in this case is the error code for the operation. + lambda::var(new_data_socket_result) = lambda::_1, + lambda::if_(!lambda::_1) + [ + // We have successfully received a frame on the new data socket, + // so we can close the old data socket. This will cancel any + // outstanding receive operation on the old data socket. + lambda::var(data_socket) = boost::shared_ptr<udp::socket>() + ] + )); + + // This loop will continue until we have successfully completed the + // renegotiation (i.e. received a frame on the new data socket), or some + // unrecoverable error occurs. + bool done = false; + while (!done) + { + // Even though we're performing a renegotation, we want to continue + // receiving data as smoothly as possible. Therefore we will continue to + // try to receive a frame from the server on the old data socket. If we + // receive a frame on this socket we will interrupt the io_context, + // print the frame, and resume waiting for the other operations to + // complete. + frame f2; + done = true; // Let's be optimistic. + if (data_socket) // Might have been closed by new_data_socket's handler. + { + data_socket->async_receive(f2.to_buffers(), 0, + ( + lambda::if_(!lambda::_1) + [ + // We have successfully received a frame on the old data + // socket. Stop the io_context so that we can print it. + lambda::bind(&boost::asio::io_context::stop, &io_context), + lambda::var(done) = false + ] + )); + } + + // Run the operations in parallel. This will block until all operations + // have finished, or until the io_context is interrupted. (No threads!) + io_context.restart(); + io_context.run(); + + // If the io_context.run() was interrupted then we have received a frame + // on the old data socket. We need to keep waiting for the renegotation + // operations to complete. + if (!done) + { + if (f2.number() > last_frame_number) + { + last_frame_number = f2.number(); + std::cout << "\n" << f2.payload(); + } + } + } + + // Since the loop has finished, we have either successfully completed + // the renegotation, or an error has occurred. First we'll check for + // errors. + if (control_result) + throw boost::system::system_error(control_result); + if (new_data_socket_result) + throw boost::system::system_error(new_data_socket_result); + + // If we get here it means we have successfully started receiving data on + // the new data socket. This new data socket will be used from now on + // (until the next time we renegotiate). + std::cout << " Renegotiation complete"; + data_socket = new_data_socket; + data_endpoint = new_data_endpoint; + if (f1.number() > last_frame_number) + { + last_frame_number = f1.number(); + std::cout << "\n" << f1.payload(); + } + } + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/porthopper/protocol.hpp b/src/boost/libs/asio/example/cpp03/porthopper/protocol.hpp new file mode 100644 index 000000000..38e2ee896 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/porthopper/protocol.hpp @@ -0,0 +1,156 @@ +// +// protocol.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef PORTHOPPER_PROTOCOL_HPP +#define PORTHOPPER_PROTOCOL_HPP + +#include <boost/array.hpp> +#include <boost/asio.hpp> +#include <cstring> +#include <iomanip> +#include <string> +#include <strstream> + +// This request is sent by the client to the server over a TCP connection. +// The client uses it to perform three functions: +// - To request that data start being sent to a given port. +// - To request that data is no longer sent to a given port. +// - To change the target port to another. +class control_request +{ +public: + // Construct an empty request. Used when receiving. + control_request() + { + } + + // Create a request to start sending data to a given port. + static const control_request start(unsigned short port) + { + return control_request(0, port); + } + + // Create a request to stop sending data to a given port. + static const control_request stop(unsigned short port) + { + return control_request(port, 0); + } + + // Create a request to change the port that data is sent to. + static const control_request change( + unsigned short old_port, unsigned short new_port) + { + return control_request(old_port, new_port); + } + + // Get the old port. Returns 0 for start requests. + unsigned short old_port() const + { + std::istrstream is(data_, encoded_port_size); + unsigned short port = 0; + is >> std::setw(encoded_port_size) >> std::hex >> port; + return port; + } + + // Get the new port. Returns 0 for stop requests. + unsigned short new_port() const + { + std::istrstream is(data_ + encoded_port_size, encoded_port_size); + unsigned short port = 0; + is >> std::setw(encoded_port_size) >> std::hex >> port; + return port; + } + + // Obtain buffers for reading from or writing to a socket. + boost::array<boost::asio::mutable_buffer, 1> to_buffers() + { + boost::array<boost::asio::mutable_buffer, 1> buffers + = { { boost::asio::buffer(data_) } }; + return buffers; + } + +private: + // Construct with specified old and new ports. + control_request(unsigned short old_port_number, + unsigned short new_port_number) + { + std::ostrstream os(data_, control_request_size); + os << std::setw(encoded_port_size) << std::hex << old_port_number; + os << std::setw(encoded_port_size) << std::hex << new_port_number; + } + + // The length in bytes of a control_request and its components. + enum + { + encoded_port_size = 4, // 16-bit port in hex. + control_request_size = encoded_port_size * 2 + }; + + // The encoded request data. + char data_[control_request_size]; +}; + +// This frame is sent from the server to subscribed clients over UDP. +class frame +{ +public: + // The maximum allowable length of the payload. + enum { payload_size = 32 }; + + // Construct an empty frame. Used when receiving. + frame() + { + } + + // Construct a frame with specified frame number and payload. + frame(unsigned long frame_number, const std::string& payload_data) + { + std::ostrstream os(data_, frame_size); + os << std::setw(encoded_number_size) << std::hex << frame_number; + os << std::setw(payload_size) + << std::setfill(' ') << payload_data.substr(0, payload_size); + } + + // Get the frame number. + unsigned long number() const + { + std::istrstream is(data_, encoded_number_size); + unsigned long frame_number = 0; + is >> std::setw(encoded_number_size) >> std::hex >> frame_number; + return frame_number; + } + + // Get the payload data. + const std::string payload() const + { + return std::string(data_ + encoded_number_size, payload_size); + } + + // Obtain buffers for reading from or writing to a socket. + boost::array<boost::asio::mutable_buffer, 1> to_buffers() + { + boost::array<boost::asio::mutable_buffer, 1> buffers + = { { boost::asio::buffer(data_) } }; + return buffers; + } + +private: + // The length in bytes of a frame and its components. + enum + { + encoded_number_size = 8, // Frame number in hex. + frame_size = encoded_number_size + payload_size + }; + + // The encoded frame data. + char data_[frame_size]; +}; + +#endif // PORTHOPPER_PROTOCOL_HPP diff --git a/src/boost/libs/asio/example/cpp03/porthopper/server.cpp b/src/boost/libs/asio/example/cpp03/porthopper/server.cpp new file mode 100644 index 000000000..191f62088 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/porthopper/server.cpp @@ -0,0 +1,187 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio.hpp> +#include <boost/bind/bind.hpp> +#include <boost/shared_ptr.hpp> +#include <cmath> +#include <cstdlib> +#include <exception> +#include <iostream> +#include <set> +#include "protocol.hpp" + +using boost::asio::ip::tcp; +using boost::asio::ip::udp; + +typedef boost::shared_ptr<tcp::socket> tcp_socket_ptr; +typedef boost::shared_ptr<boost::asio::steady_timer> timer_ptr; +typedef boost::shared_ptr<control_request> control_request_ptr; + +class server +{ +public: + // Construct the server to wait for incoming control connections. + server(boost::asio::io_context& io_context, unsigned short port) + : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)), + timer_(io_context), + udp_socket_(io_context, udp::endpoint(udp::v4(), 0)), + next_frame_number_(1) + { + // Start waiting for a new control connection. + tcp_socket_ptr new_socket(new tcp::socket(acceptor_.get_executor())); + acceptor_.async_accept(*new_socket, + boost::bind(&server::handle_accept, this, + boost::asio::placeholders::error, new_socket)); + + // Start the timer used to generate outgoing frames. + timer_.expires_after(boost::asio::chrono::milliseconds(100)); + timer_.async_wait(boost::bind(&server::handle_timer, this)); + } + + // Handle a new control connection. + void handle_accept(const boost::system::error_code& ec, tcp_socket_ptr socket) + { + if (!ec) + { + // Start receiving control requests on the connection. + control_request_ptr request(new control_request); + boost::asio::async_read(*socket, request->to_buffers(), + boost::bind(&server::handle_control_request, this, + boost::asio::placeholders::error, socket, request)); + } + + // Start waiting for a new control connection. + tcp_socket_ptr new_socket(new tcp::socket(acceptor_.get_executor())); + acceptor_.async_accept(*new_socket, + boost::bind(&server::handle_accept, this, + boost::asio::placeholders::error, new_socket)); + } + + // Handle a new control request. + void handle_control_request(const boost::system::error_code& ec, + tcp_socket_ptr socket, control_request_ptr request) + { + if (!ec) + { + // Delay handling of the control request to simulate network latency. + timer_ptr delay_timer( + new boost::asio::steady_timer(acceptor_.get_executor())); + delay_timer->expires_after(boost::asio::chrono::seconds(2)); + delay_timer->async_wait( + boost::bind(&server::handle_control_request_timer, this, + socket, request, delay_timer)); + } + } + + void handle_control_request_timer(tcp_socket_ptr socket, + control_request_ptr request, timer_ptr /*delay_timer*/) + { + // Determine what address this client is connected from, since + // subscriptions must be stored on the server as a complete endpoint, not + // just a port. We use the non-throwing overload of remote_endpoint() since + // it may fail if the socket is no longer connected. + boost::system::error_code ec; + tcp::endpoint remote_endpoint = socket->remote_endpoint(ec); + if (!ec) + { + // Remove old port subscription, if any. + if (unsigned short old_port = request->old_port()) + { + udp::endpoint old_endpoint(remote_endpoint.address(), old_port); + subscribers_.erase(old_endpoint); + std::cout << "Removing subscription " << old_endpoint << std::endl; + } + + // Add new port subscription, if any. + if (unsigned short new_port = request->new_port()) + { + udp::endpoint new_endpoint(remote_endpoint.address(), new_port); + subscribers_.insert(new_endpoint); + std::cout << "Adding subscription " << new_endpoint << std::endl; + } + } + + // Wait for next control request on this connection. + boost::asio::async_read(*socket, request->to_buffers(), + boost::bind(&server::handle_control_request, this, + boost::asio::placeholders::error, socket, request)); + } + + // Every time the timer fires we will generate a new frame and send it to all + // subscribers. + void handle_timer() + { + // Generate payload. + double x = next_frame_number_ * 0.2; + double y = std::sin(x); + int char_index = static_cast<int>((y + 1.0) * (frame::payload_size / 2)); + std::string payload; + for (int i = 0; i < frame::payload_size; ++i) + payload += (i == char_index ? '*' : '.'); + + // Create the frame to be sent to all subscribers. + frame f(next_frame_number_++, payload); + + // Send frame to all subscribers. We can use synchronous calls here since + // UDP send operations typically do not block. + std::set<udp::endpoint>::iterator j; + for (j = subscribers_.begin(); j != subscribers_.end(); ++j) + { + boost::system::error_code ec; + udp_socket_.send_to(f.to_buffers(), *j, 0, ec); + } + + // Wait for next timeout. + timer_.expires_after(boost::asio::chrono::milliseconds(100)); + timer_.async_wait(boost::bind(&server::handle_timer, this)); + } + +private: + // The acceptor used to accept incoming control connections. + tcp::acceptor acceptor_; + + // The timer used for generating data. + boost::asio::steady_timer timer_; + + // The socket used to send data to subscribers. + udp::socket udp_socket_; + + // The next frame number. + unsigned long next_frame_number_; + + // The set of endpoints that are subscribed. + std::set<udp::endpoint> subscribers_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + using namespace std; // For atoi. + server s(io_context, atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/serialization/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/serialization/Jamfile.v2 new file mode 100644 index 000000000..c0231b8b7 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/serialization/Jamfile.v2 @@ -0,0 +1,34 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/serialization//boost_serialization + <library>/boost/system//boost_system + <library>/boost/chrono//boost_chrono + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe client : client.cpp ; +exe server : server.cpp ; diff --git a/src/boost/libs/asio/example/cpp03/serialization/client.cpp b/src/boost/libs/asio/example/cpp03/serialization/client.cpp new file mode 100644 index 000000000..f4b9b8efd --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/serialization/client.cpp @@ -0,0 +1,125 @@ +// +// client.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio.hpp> +#include <boost/bind/bind.hpp> +#include <iostream> +#include <vector> +#include "connection.hpp" // Must come before boost/serialization headers. +#include <boost/serialization/vector.hpp> +#include "stock.hpp" + +namespace s11n_example { + +/// Downloads stock quote information from a server. +class client +{ +public: + /// Constructor starts the asynchronous connect operation. + client(boost::asio::io_context& io_context, + const std::string& host, const std::string& service) + : connection_(io_context.get_executor()) + { + // Resolve the host name into an IP address. + boost::asio::ip::tcp::resolver resolver(io_context); + boost::asio::ip::tcp::resolver::query query(host, service); + boost::asio::ip::tcp::resolver::iterator endpoint_iterator = + resolver.resolve(query); + + // Start an asynchronous connect operation. + boost::asio::async_connect(connection_.socket(), endpoint_iterator, + boost::bind(&client::handle_connect, this, + boost::asio::placeholders::error)); + } + + /// Handle completion of a connect operation. + void handle_connect(const boost::system::error_code& e) + { + if (!e) + { + // Successfully established connection. Start operation to read the list + // of stocks. The connection::async_read() function will automatically + // decode the data that is read from the underlying socket. + connection_.async_read(stocks_, + boost::bind(&client::handle_read, this, + boost::asio::placeholders::error)); + } + else + { + // An error occurred. Log it and return. Since we are not starting a new + // operation the io_context will run out of work to do and the client will + // exit. + std::cerr << e.message() << std::endl; + } + } + + /// Handle completion of a read operation. + void handle_read(const boost::system::error_code& e) + { + if (!e) + { + // Print out the data that was received. + for (std::size_t i = 0; i < stocks_.size(); ++i) + { + std::cout << "Stock number " << i << "\n"; + std::cout << " code: " << stocks_[i].code << "\n"; + std::cout << " name: " << stocks_[i].name << "\n"; + std::cout << " open_price: " << stocks_[i].open_price << "\n"; + std::cout << " high_price: " << stocks_[i].high_price << "\n"; + std::cout << " low_price: " << stocks_[i].low_price << "\n"; + std::cout << " last_price: " << stocks_[i].last_price << "\n"; + std::cout << " buy_price: " << stocks_[i].buy_price << "\n"; + std::cout << " buy_quantity: " << stocks_[i].buy_quantity << "\n"; + std::cout << " sell_price: " << stocks_[i].sell_price << "\n"; + std::cout << " sell_quantity: " << stocks_[i].sell_quantity << "\n"; + } + } + else + { + // An error occurred. + std::cerr << e.message() << std::endl; + } + + // Since we are not starting a new operation the io_context will run out of + // work to do and the client will exit. + } + +private: + /// The connection to the server. + connection connection_; + + /// The data received from the server. + std::vector<stock> stocks_; +}; + +} // namespace s11n_example + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 3) + { + std::cerr << "Usage: client <host> <port>" << std::endl; + return 1; + } + + boost::asio::io_context io_context; + s11n_example::client client(io_context, argv[1], argv[2]); + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/serialization/connection.hpp b/src/boost/libs/asio/example/cpp03/serialization/connection.hpp new file mode 100644 index 000000000..e6801614d --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/serialization/connection.hpp @@ -0,0 +1,188 @@ +// +// connection.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef SERIALIZATION_CONNECTION_HPP +#define SERIALIZATION_CONNECTION_HPP + +#include <boost/asio.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/bind/bind.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/tuple/tuple.hpp> +#include <iomanip> +#include <string> +#include <sstream> +#include <vector> + +namespace s11n_example { + +/// The connection class provides serialization primitives on top of a socket. +/** + * Each message sent using this class consists of: + * @li An 8-byte header containing the length of the serialized data in + * hexadecimal. + * @li The serialized data. + */ +class connection +{ +public: + /// Constructor. + connection(const boost::asio::executor& ex) + : socket_(ex) + { + } + + /// Get the underlying socket. Used for making a connection or for accepting + /// an incoming connection. + boost::asio::ip::tcp::socket& socket() + { + return socket_; + } + + /// Asynchronously write a data structure to the socket. + template <typename T, typename Handler> + void async_write(const T& t, Handler handler) + { + // Serialize the data first so we know how large it is. + std::ostringstream archive_stream; + boost::archive::text_oarchive archive(archive_stream); + archive << t; + outbound_data_ = archive_stream.str(); + + // Format the header. + std::ostringstream header_stream; + header_stream << std::setw(header_length) + << std::hex << outbound_data_.size(); + if (!header_stream || header_stream.str().size() != header_length) + { + // Something went wrong, inform the caller. + boost::system::error_code error(boost::asio::error::invalid_argument); + boost::asio::post(socket_.get_executor(), boost::bind(handler, error)); + return; + } + outbound_header_ = header_stream.str(); + + // Write the serialized data to the socket. We use "gather-write" to send + // both the header and the data in a single write operation. + std::vector<boost::asio::const_buffer> buffers; + buffers.push_back(boost::asio::buffer(outbound_header_)); + buffers.push_back(boost::asio::buffer(outbound_data_)); + boost::asio::async_write(socket_, buffers, handler); + } + + /// Asynchronously read a data structure from the socket. + template <typename T, typename Handler> + void async_read(T& t, Handler handler) + { + // Issue a read operation to read exactly the number of bytes in a header. + void (connection::*f)( + const boost::system::error_code&, + T&, boost::tuple<Handler>) + = &connection::handle_read_header<T, Handler>; + boost::asio::async_read(socket_, boost::asio::buffer(inbound_header_), + boost::bind(f, + this, boost::asio::placeholders::error, boost::ref(t), + boost::make_tuple(handler))); + } + + /// Handle a completed read of a message header. The handler is passed using + /// a tuple since boost::bind seems to have trouble binding a function object + /// created using boost::bind as a parameter. + template <typename T, typename Handler> + void handle_read_header(const boost::system::error_code& e, + T& t, boost::tuple<Handler> handler) + { + if (e) + { + boost::get<0>(handler)(e); + } + else + { + // Determine the length of the serialized data. + std::istringstream is(std::string(inbound_header_, header_length)); + std::size_t inbound_data_size = 0; + if (!(is >> std::hex >> inbound_data_size)) + { + // Header doesn't seem to be valid. Inform the caller. + boost::system::error_code error(boost::asio::error::invalid_argument); + boost::get<0>(handler)(error); + return; + } + + // Start an asynchronous call to receive the data. + inbound_data_.resize(inbound_data_size); + void (connection::*f)( + const boost::system::error_code&, + T&, boost::tuple<Handler>) + = &connection::handle_read_data<T, Handler>; + boost::asio::async_read(socket_, boost::asio::buffer(inbound_data_), + boost::bind(f, this, + boost::asio::placeholders::error, boost::ref(t), handler)); + } + } + + /// Handle a completed read of message data. + template <typename T, typename Handler> + void handle_read_data(const boost::system::error_code& e, + T& t, boost::tuple<Handler> handler) + { + if (e) + { + boost::get<0>(handler)(e); + } + else + { + // Extract the data structure from the data just received. + try + { + std::string archive_data(&inbound_data_[0], inbound_data_.size()); + std::istringstream archive_stream(archive_data); + boost::archive::text_iarchive archive(archive_stream); + archive >> t; + } + catch (std::exception& e) + { + // Unable to decode data. + boost::system::error_code error(boost::asio::error::invalid_argument); + boost::get<0>(handler)(error); + return; + } + + // Inform caller that data has been received ok. + boost::get<0>(handler)(e); + } + } + +private: + /// The underlying socket. + boost::asio::ip::tcp::socket socket_; + + /// The size of a fixed length header. + enum { header_length = 8 }; + + /// Holds an outbound header. + std::string outbound_header_; + + /// Holds the outbound data. + std::string outbound_data_; + + /// Holds an inbound header. + char inbound_header_[header_length]; + + /// Holds the inbound data. + std::vector<char> inbound_data_; +}; + +typedef boost::shared_ptr<connection> connection_ptr; + +} // namespace s11n_example + +#endif // SERIALIZATION_CONNECTION_HPP diff --git a/src/boost/libs/asio/example/cpp03/serialization/server.cpp b/src/boost/libs/asio/example/cpp03/serialization/server.cpp new file mode 100644 index 000000000..4e527cf27 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/serialization/server.cpp @@ -0,0 +1,123 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio.hpp> +#include <boost/bind/bind.hpp> +#include <boost/lexical_cast.hpp> +#include <iostream> +#include <vector> +#include "connection.hpp" // Must come before boost/serialization headers. +#include <boost/serialization/vector.hpp> +#include "stock.hpp" + +namespace s11n_example { + +/// Serves stock quote information to any client that connects to it. +class server +{ +public: + /// Constructor opens the acceptor and starts waiting for the first incoming + /// connection. + server(boost::asio::io_context& io_context, unsigned short port) + : acceptor_(io_context, + boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)) + { + // Create the data to be sent to each client. + stock s; + s.code = "ABC"; + s.name = "A Big Company"; + s.open_price = 4.56; + s.high_price = 5.12; + s.low_price = 4.33; + s.last_price = 4.98; + s.buy_price = 4.96; + s.buy_quantity = 1000; + s.sell_price = 4.99; + s.sell_quantity = 2000; + stocks_.push_back(s); + s.code = "DEF"; + s.name = "Developer Entertainment Firm"; + s.open_price = 20.24; + s.high_price = 22.88; + s.low_price = 19.50; + s.last_price = 19.76; + s.buy_price = 19.72; + s.buy_quantity = 34000; + s.sell_price = 19.85; + s.sell_quantity = 45000; + stocks_.push_back(s); + + // Start an accept operation for a new connection. + connection_ptr new_conn(new connection(acceptor_.get_executor())); + acceptor_.async_accept(new_conn->socket(), + boost::bind(&server::handle_accept, this, + boost::asio::placeholders::error, new_conn)); + } + + /// Handle completion of a accept operation. + void handle_accept(const boost::system::error_code& e, connection_ptr conn) + { + if (!e) + { + // Successfully accepted a new connection. Send the list of stocks to the + // client. The connection::async_write() function will automatically + // serialize the data structure for us. + conn->async_write(stocks_, + boost::bind(&server::handle_write, this, + boost::asio::placeholders::error, conn)); + } + + // Start an accept operation for a new connection. + connection_ptr new_conn(new connection(acceptor_.get_executor())); + acceptor_.async_accept(new_conn->socket(), + boost::bind(&server::handle_accept, this, + boost::asio::placeholders::error, new_conn)); + } + + /// Handle completion of a write operation. + void handle_write(const boost::system::error_code& e, connection_ptr conn) + { + // Nothing to do. The socket will be closed automatically when the last + // reference to the connection object goes away. + } + +private: + /// The acceptor object used to accept incoming socket connections. + boost::asio::ip::tcp::acceptor acceptor_; + + /// The data to be sent to each client. + std::vector<stock> stocks_; +}; + +} // namespace s11n_example + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 2) + { + std::cerr << "Usage: server <port>" << std::endl; + return 1; + } + unsigned short port = boost::lexical_cast<unsigned short>(argv[1]); + + boost::asio::io_context io_context; + s11n_example::server server(io_context, port); + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/serialization/stock.hpp b/src/boost/libs/asio/example/cpp03/serialization/stock.hpp new file mode 100644 index 000000000..ca5a1c957 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/serialization/stock.hpp @@ -0,0 +1,50 @@ +// +// stock.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef SERIALIZATION_STOCK_HPP +#define SERIALIZATION_STOCK_HPP + +#include <string> + +namespace s11n_example { + +/// Structure to hold information about a single stock. +struct stock +{ + std::string code; + std::string name; + double open_price; + double high_price; + double low_price; + double last_price; + double buy_price; + int buy_quantity; + double sell_price; + int sell_quantity; + + template <typename Archive> + void serialize(Archive& ar, const unsigned int version) + { + ar & code; + ar & name; + ar & open_price; + ar & high_price; + ar & low_price; + ar & last_price; + ar & buy_price; + ar & buy_quantity; + ar & sell_price; + ar & sell_quantity; + } +}; + +} // namespace s11n_example + +#endif // SERIALIZATION_STOCK_HPP diff --git a/src/boost/libs/asio/example/cpp03/services/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/services/Jamfile.v2 new file mode 100644 index 000000000..3a2634d74 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/services/Jamfile.v2 @@ -0,0 +1,32 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe daytime_client + : daytime_client.cpp + logger_service.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + /boost/thread//boost_thread + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/services/basic_logger.hpp b/src/boost/libs/asio/example/cpp03/services/basic_logger.hpp new file mode 100644 index 000000000..d7cc42579 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/services/basic_logger.hpp @@ -0,0 +1,83 @@ +// +// basic_logger.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef SERVICES_BASIC_LOGGER_HPP +#define SERVICES_BASIC_LOGGER_HPP + +#include <boost/asio.hpp> +#include <boost/noncopyable.hpp> +#include <string> + +namespace services { + +/// Class to provide simple logging functionality. Use the services::logger +/// typedef. +template <typename Service> +class basic_logger + : private boost::noncopyable +{ +public: + /// The type of the service that will be used to provide timer operations. + typedef Service service_type; + + /// The native implementation type of the timer. + typedef typename service_type::impl_type impl_type; + + /// Constructor. + /** + * This constructor creates a logger. + * + * @param context The execution context used to locate the logger service. + * + * @param identifier An identifier for this logger. + */ + explicit basic_logger(boost::asio::execution_context& context, + const std::string& identifier) + : service_(boost::asio::use_service<Service>(context)), + impl_(service_.null()) + { + service_.create(impl_, identifier); + } + + /// Destructor. + ~basic_logger() + { + service_.destroy(impl_); + } + + /// Get the io_context associated with the object. + boost::asio::io_context& get_io_context() + { + return service_.get_io_context(); + } + + /// Set the output file for all logger instances. + void use_file(const std::string& file) + { + service_.use_file(impl_, file); + } + + /// Log a message. + void log(const std::string& message) + { + service_.log(impl_, message); + } + +private: + /// The backend service implementation. + service_type& service_; + + /// The underlying native implementation. + impl_type impl_; +}; + +} // namespace services + +#endif // SERVICES_BASIC_LOGGER_HPP diff --git a/src/boost/libs/asio/example/cpp03/services/daytime_client.cpp b/src/boost/libs/asio/example/cpp03/services/daytime_client.cpp new file mode 100644 index 000000000..59a0a6269 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/services/daytime_client.cpp @@ -0,0 +1,97 @@ +// +// daytime_client.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio.hpp> +#include <boost/bind/bind.hpp> +#include <iostream> +#include "logger.hpp" + +using boost::asio::ip::tcp; + +char read_buffer[1024]; + +void read_handler(const boost::system::error_code& e, + std::size_t bytes_transferred, tcp::socket* s) +{ + if (!e) + { + std::cout.write(read_buffer, bytes_transferred); + + s->async_read_some(boost::asio::buffer(read_buffer), + boost::bind(read_handler, boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred, s)); + } + else + { + services::logger logger(s->get_executor().context(), "read_handler"); + + std::string msg = "Read error: "; + msg += e.message(); + logger.log(msg); + } +} + +void connect_handler(const boost::system::error_code& e, tcp::socket* s) +{ + services::logger logger(s->get_executor().context(), "connect_handler"); + + if (!e) + { + logger.log("Connection established"); + + s->async_read_some(boost::asio::buffer(read_buffer), + boost::bind(read_handler, boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred, s)); + } + else + { + std::string msg = "Unable to establish connection: "; + msg += e.message(); + logger.log(msg); + } +} + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: daytime_client <host>" << std::endl; + return 1; + } + + boost::asio::io_context io_context; + + // Set the name of the file that all logger instances will use. + services::logger logger(io_context, ""); + logger.use_file("log.txt"); + + // Resolve the address corresponding to the given host. + tcp::resolver resolver(io_context); + tcp::resolver::results_type endpoints = + resolver.resolve(argv[1], "daytime"); + + // Start an asynchronous connect. + tcp::socket socket(io_context); + boost::asio::async_connect(socket, endpoints, + boost::bind(connect_handler, + boost::asio::placeholders::error, &socket)); + + // Run the io_context until all operations have finished. + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/services/logger.hpp b/src/boost/libs/asio/example/cpp03/services/logger.hpp new file mode 100644 index 000000000..4eb00268a --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/services/logger.hpp @@ -0,0 +1,24 @@ +// +// logger.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef SERVICES_LOGGER_HPP +#define SERVICES_LOGGER_HPP + +#include "basic_logger.hpp" +#include "logger_service.hpp" + +namespace services { + +/// Typedef for typical logger usage. +typedef basic_logger<logger_service> logger; + +} // namespace services + +#endif // SERVICES_LOGGER_HPP diff --git a/src/boost/libs/asio/example/cpp03/services/logger_service.cpp b/src/boost/libs/asio/example/cpp03/services/logger_service.cpp new file mode 100644 index 000000000..c1c9ac4e5 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/services/logger_service.cpp @@ -0,0 +1,11 @@ +// +// logger_service.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "logger_service.hpp" diff --git a/src/boost/libs/asio/example/cpp03/services/logger_service.hpp b/src/boost/libs/asio/example/cpp03/services/logger_service.hpp new file mode 100644 index 000000000..4f439c5e1 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/services/logger_service.hpp @@ -0,0 +1,146 @@ +// +// logger_service.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef SERVICES_LOGGER_SERVICE_HPP +#define SERVICES_LOGGER_SERVICE_HPP + +#include <boost/asio.hpp> +#include <boost/thread/thread.hpp> +#include <boost/bind/bind.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> +#include <fstream> +#include <sstream> +#include <string> + +namespace services { + +/// Service implementation for the logger. +class logger_service + : public boost::asio::execution_context::service +{ +public: + /// The type used to identify this service in the execution context. + typedef logger_service key_type; + + /// The backend implementation of a logger. + struct logger_impl + { + explicit logger_impl(const std::string& ident) : identifier(ident) {} + std::string identifier; + }; + + /// The type for an implementation of the logger. + typedef logger_impl* impl_type; + + /// Constructor creates a thread to run a private io_context. + logger_service(boost::asio::execution_context& context) + : boost::asio::execution_context::service(context), + work_io_context_(), + work_(boost::asio::make_work_guard(work_io_context_)), + work_thread_(new boost::thread( + boost::bind(&boost::asio::io_context::run, &work_io_context_))) + { + } + + /// Destructor shuts down the private io_context. + ~logger_service() + { + /// Indicate that we have finished with the private io_context. Its + /// io_context::run() function will exit once all other work has completed. + work_.reset(); + if (work_thread_) + work_thread_->join(); + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown() + { + } + + /// Return a null logger implementation. + impl_type null() const + { + return 0; + } + + /// Create a new logger implementation. + void create(impl_type& impl, const std::string& identifier) + { + impl = new logger_impl(identifier); + } + + /// Destroy a logger implementation. + void destroy(impl_type& impl) + { + delete impl; + impl = null(); + } + + /// Set the output file for the logger. The current implementation sets the + /// output file for all logger instances, and so the impl parameter is not + /// actually needed. It is retained here to illustrate how service functions + /// are typically defined. + void use_file(impl_type& /*impl*/, const std::string& file) + { + // Pass the work of opening the file to the background thread. + boost::asio::post(work_io_context_, boost::bind( + &logger_service::use_file_impl, this, file)); + } + + /// Log a message. + void log(impl_type& impl, const std::string& message) + { + // Format the text to be logged. + std::ostringstream os; + os << impl->identifier << ": " << message; + + // Pass the work of writing to the file to the background thread. + boost::asio::post(work_io_context_, boost::bind( + &logger_service::log_impl, this, os.str())); + } + +private: + /// Helper function used to open the output file from within the private + /// io_context's thread. + void use_file_impl(const std::string& file) + { + ofstream_.close(); + ofstream_.clear(); + ofstream_.open(file.c_str()); + } + + /// Helper function used to log a message from within the private io_context's + /// thread. + void log_impl(const std::string& text) + { + ofstream_ << text << std::endl; + } + + /// Private io_context used for performing logging operations. + boost::asio::io_context work_io_context_; + + /// Work for the private io_context to perform. If we do not give the + /// io_context some work to do then the io_context::run() function will exit + /// immediately. + boost::asio::executor_work_guard< + boost::asio::io_context::executor_type> work_; + + /// Thread used for running the work io_context's run loop. + boost::scoped_ptr<boost::thread> work_thread_; + + /// The file to which log messages will be written. + std::ofstream ofstream_; +}; + +} // namespace services + +#endif // SERVICES_LOGGER_SERVICE_HPP diff --git a/src/boost/libs/asio/example/cpp03/socks4/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/socks4/Jamfile.v2 new file mode 100644 index 000000000..aa1492bad --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/socks4/Jamfile.v2 @@ -0,0 +1,30 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe server + : sync_client.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/socks4/socks4.hpp b/src/boost/libs/asio/example/cpp03/socks4/socks4.hpp new file mode 100644 index 000000000..ad72b120f --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/socks4/socks4.hpp @@ -0,0 +1,144 @@ +// +// socks4.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef SOCKS4_HPP +#define SOCKS4_HPP + +#include <string> +#include <boost/asio.hpp> +#include <boost/array.hpp> + +namespace socks4 { + +const unsigned char version = 0x04; + +class request +{ +public: + enum command_type + { + connect = 0x01, + bind = 0x02 + }; + + request(command_type cmd, const boost::asio::ip::tcp::endpoint& endpoint, + const std::string& user_id) + : version_(version), + command_(cmd), + user_id_(user_id), + null_byte_(0) + { + // Only IPv4 is supported by the SOCKS 4 protocol. + if (endpoint.protocol() != boost::asio::ip::tcp::v4()) + { + throw boost::system::system_error( + boost::asio::error::address_family_not_supported); + } + + // Convert port number to network byte order. + unsigned short port = endpoint.port(); + port_high_byte_ = (port >> 8) & 0xff; + port_low_byte_ = port & 0xff; + + // Save IP address in network byte order. + address_ = endpoint.address().to_v4().to_bytes(); + } + + boost::array<boost::asio::const_buffer, 7> buffers() const + { + boost::array<boost::asio::const_buffer, 7> bufs = + { + { + boost::asio::buffer(&version_, 1), + boost::asio::buffer(&command_, 1), + boost::asio::buffer(&port_high_byte_, 1), + boost::asio::buffer(&port_low_byte_, 1), + boost::asio::buffer(address_), + boost::asio::buffer(user_id_), + boost::asio::buffer(&null_byte_, 1) + } + }; + return bufs; + } + +private: + unsigned char version_; + unsigned char command_; + unsigned char port_high_byte_; + unsigned char port_low_byte_; + boost::asio::ip::address_v4::bytes_type address_; + std::string user_id_; + unsigned char null_byte_; +}; + +class reply +{ +public: + enum status_type + { + request_granted = 0x5a, + request_failed = 0x5b, + request_failed_no_identd = 0x5c, + request_failed_bad_user_id = 0x5d + }; + + reply() + : null_byte_(0), + status_() + { + } + + boost::array<boost::asio::mutable_buffer, 5> buffers() + { + boost::array<boost::asio::mutable_buffer, 5> bufs = + { + { + boost::asio::buffer(&null_byte_, 1), + boost::asio::buffer(&status_, 1), + boost::asio::buffer(&port_high_byte_, 1), + boost::asio::buffer(&port_low_byte_, 1), + boost::asio::buffer(address_) + } + }; + return bufs; + } + + bool success() const + { + return null_byte_ == 0 && status_ == request_granted; + } + + unsigned char status() const + { + return status_; + } + + boost::asio::ip::tcp::endpoint endpoint() const + { + unsigned short port = port_high_byte_; + port = (port << 8) & 0xff00; + port = port | port_low_byte_; + + boost::asio::ip::address_v4 address(address_); + + return boost::asio::ip::tcp::endpoint(address, port); + } + +private: + unsigned char null_byte_; + unsigned char status_; + unsigned char port_high_byte_; + unsigned char port_low_byte_; + boost::asio::ip::address_v4::bytes_type address_; +}; + +} // namespace socks4 + +#endif // SOCKS4_HPP diff --git a/src/boost/libs/asio/example/cpp03/socks4/sync_client.cpp b/src/boost/libs/asio/example/cpp03/socks4/sync_client.cpp new file mode 100644 index 000000000..a2edd89cd --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/socks4/sync_client.cpp @@ -0,0 +1,94 @@ +// +// sync_client.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <iomanip> +#include <ostream> +#include <string> +#include <boost/asio.hpp> +#include <boost/array.hpp> +#include "socks4.hpp" + +using boost::asio::ip::tcp; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 4) + { + std::cout << "Usage: sync_client <socks4server> <socks4port> <user>\n"; + std::cout << "Examples:\n"; + std::cout << " sync_client 127.0.0.1 1080 chris\n"; + std::cout << " sync_client localhost socks chris\n"; + return 1; + } + + boost::asio::io_context io_context; + + // Get a list of endpoints corresponding to the SOCKS 4 server name. + tcp::resolver resolver(io_context); + tcp::resolver::results_type endpoints = resolver.resolve(argv[1], argv[2]); + + // Try each endpoint until we successfully establish a connection to the + // SOCKS 4 server. + tcp::socket socket(io_context); + boost::asio::connect(socket, endpoints); + + // Get an endpoint for the Boost website. This will be passed to the SOCKS + // 4 server. Explicitly specify IPv4 since SOCKS 4 does not support IPv6. + tcp::endpoint http_endpoint = + *resolver.resolve(tcp::v4(), "www.boost.org", "http").begin(); + + // Send the request to the SOCKS 4 server. + socks4::request socks_request( + socks4::request::connect, http_endpoint, argv[3]); + boost::asio::write(socket, socks_request.buffers()); + + // Receive a response from the SOCKS 4 server. + socks4::reply socks_reply; + boost::asio::read(socket, socks_reply.buffers()); + + // Check whether we successfully negotiated with the SOCKS 4 server. + if (!socks_reply.success()) + { + std::cout << "Connection failed.\n"; + std::cout << "status = 0x" << std::hex << socks_reply.status(); + return 1; + } + + // Form the HTTP request. We specify the "Connection: close" header so that + // the server will close the socket after transmitting the response. This + // will allow us to treat all data up until the EOF as the response. + std::string request = + "GET / HTTP/1.0\r\n" + "Host: www.boost.org\r\n" + "Accept: */*\r\n" + "Connection: close\r\n\r\n"; + + // Send the HTTP request. + boost::asio::write(socket, boost::asio::buffer(request)); + + // Read until EOF, writing data to output as we go. + boost::array<char, 512> response; + boost::system::error_code error; + while (std::size_t s = socket.read_some( + boost::asio::buffer(response), error)) + std::cout.write(response.data(), s); + if (error != boost::asio::error::eof) + throw boost::system::system_error(error); + } + catch (std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/spawn/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/spawn/Jamfile.v2 new file mode 100644 index 000000000..7e4b320ef --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/spawn/Jamfile.v2 @@ -0,0 +1,50 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe server + : echo_server.cpp + /boost/context//boost_context + /boost/coroutine//boost_coroutine + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe parallel_grep + : parallel_grep.cpp + /boost/context//boost_context + /boost/coroutine//boost_coroutine + /boost/system//boost_system + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/spawn/echo_server.cpp b/src/boost/libs/asio/example/cpp03/spawn/echo_server.cpp new file mode 100644 index 000000000..9efe62b92 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/spawn/echo_server.cpp @@ -0,0 +1,122 @@ +// +// echo_server.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/spawn.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/asio/write.hpp> +#include <boost/bind/bind.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <iostream> + +using boost::asio::ip::tcp; + +class session : public boost::enable_shared_from_this<session> +{ +public: + explicit session(boost::asio::io_context& io_context) + : strand_(boost::asio::make_strand(io_context)), + socket_(io_context), + timer_(io_context) + { + } + + tcp::socket& socket() + { + return socket_; + } + + void go() + { + boost::asio::spawn(strand_, + boost::bind(&session::echo, + shared_from_this(), boost::placeholders::_1)); + boost::asio::spawn(strand_, + boost::bind(&session::timeout, + shared_from_this(), boost::placeholders::_1)); + } + +private: + void echo(boost::asio::yield_context yield) + { + try + { + char data[128]; + for (;;) + { + timer_.expires_after(boost::asio::chrono::seconds(10)); + std::size_t n = socket_.async_read_some(boost::asio::buffer(data), yield); + boost::asio::async_write(socket_, boost::asio::buffer(data, n), yield); + } + } + catch (std::exception& e) + { + socket_.close(); + timer_.cancel(); + } + } + + void timeout(boost::asio::yield_context yield) + { + while (socket_.is_open()) + { + boost::system::error_code ignored_ec; + timer_.async_wait(yield[ignored_ec]); + if (timer_.expiry() <= boost::asio::steady_timer::clock_type::now()) + socket_.close(); + } + } + + boost::asio::strand<boost::asio::io_context::executor_type> strand_; + tcp::socket socket_; + boost::asio::steady_timer timer_; +}; + +void do_accept(boost::asio::io_context& io_context, + unsigned short port, boost::asio::yield_context yield) +{ + tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), port)); + + for (;;) + { + boost::system::error_code ec; + boost::shared_ptr<session> new_session(new session(io_context)); + acceptor.async_accept(new_session->socket(), yield[ec]); + if (!ec) new_session->go(); + } +} + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: echo_server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + boost::asio::spawn(io_context, + boost::bind(do_accept, + boost::ref(io_context), atoi(argv[1]), boost::placeholders::_1)); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/spawn/parallel_grep.cpp b/src/boost/libs/asio/example/cpp03/spawn/parallel_grep.cpp new file mode 100644 index 000000000..832b09ab6 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/spawn/parallel_grep.cpp @@ -0,0 +1,90 @@ +// +// parallel_grep.cpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/dispatch.hpp> +#include <boost/asio/post.hpp> +#include <boost/asio/spawn.hpp> +#include <boost/asio/strand.hpp> +#include <boost/asio/thread_pool.hpp> +#include <boost/thread/thread.hpp> +#include <boost/bind/bind.hpp> +#include <fstream> +#include <iostream> +#include <string> + +using boost::asio::dispatch; +using boost::asio::spawn; +using boost::asio::strand; +using boost::asio::thread_pool; +using boost::asio::yield_context; + +void print_match(std::string input_file, std::string line) +{ + std::cout << input_file << ':' << line << std::endl; +} + +void search_file(std::string search_string, std::string input_file, + strand<thread_pool::executor_type> output_strand, yield_context yield) +{ + std::ifstream is(input_file.c_str()); + std::string line; + std::size_t line_num = 0; + while (std::getline(is, line)) + { + // If we find a match, send a message to the output. + if (line.find(search_string) != std::string::npos) + { + dispatch(output_strand, boost::bind(&print_match, input_file, line)); + } + + // Every so often we yield control to another coroutine. + if (++line_num % 10 == 0) + post(yield); + } +} + +int main(int argc, char* argv[]) +{ + try + { + if (argc < 2) + { + std::cerr << "Usage: parallel_grep <string> <files...>\n"; + return 1; + } + + // We use a fixed size pool of threads for reading the input files. The + // number of threads is automatically determined based on the number of + // CPUs available in the system. + thread_pool pool; + + // To prevent the output from being garbled, we use a strand to synchronise + // printing. + strand<thread_pool::executor_type> output_strand(pool.get_executor()); + + // Spawn a new coroutine for each file specified on the command line. + std::string search_string = argv[1]; + for (int argn = 2; argn < argc; ++argn) + { + std::string input_file = argv[argn]; + spawn(pool, boost::bind(&search_file, search_string, + input_file, output_strand, boost::placeholders::_1)); + } + + // Join the thread pool to wait for all the spawned tasks to complete. + pool.join(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/ssl/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/ssl/Jamfile.v2 new file mode 100644 index 000000000..a7805abb8 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/ssl/Jamfile.v2 @@ -0,0 +1,48 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +import os ; + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +if [ os.name ] = NT +{ + lib ssl : : <name>ssleay32 ; + lib crypto : : <name>libeay32 ; +} +else +{ + lib ssl ; + lib crypto ; +} + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/chrono//boost_chrono + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + <library>ssl + <library>crypto + ; + +exe client : client.cpp ; +exe server : server.cpp ; diff --git a/src/boost/libs/asio/example/cpp03/ssl/README b/src/boost/libs/asio/example/cpp03/ssl/README new file mode 100644 index 000000000..7dea8cc9b --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/ssl/README @@ -0,0 +1,8 @@ +The passphrase for both the CA and server private keys is "test". + + +------------------------------------------------------------------------------- +Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) diff --git a/src/boost/libs/asio/example/cpp03/ssl/ca.pem b/src/boost/libs/asio/example/cpp03/ssl/ca.pem new file mode 100644 index 000000000..1ee5f2ca4 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/ssl/ca.pem @@ -0,0 +1,49 @@ +-----BEGIN CERTIFICATE----- +MIIDlzCCAn+gAwIBAgIJAMJYU3U6A0IRMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNV +BAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChME +YXNpbzAeFw0xNTExMTgyMjMzNDhaFw0yMDExMTYyMjMzNDhaMDsxCzAJBgNVBAYT +AkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChMEYXNp +bzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMcRJocHdVMdLUJ/pypY +QVSTC0t3IIgjwjazrK3kAaoIMvzPmDFxEXWcDx+nyz8kQ/E38Ir/ef2BCNGci5hu +wkfMSuMoW9l2N4hx3QCcF46tTDEZztFxWAH7QbE2wYMlMgKZSxWimNfq0YjxEEXb +QM0lGPLFh7Xoko29H0F3LKaaQV9u/vop3Hs0h12HeWlY4PiLp7QQTNGqbWcXycA0 +NZ/fyismireyEvPAgo6L8iXuAi7g0TVKVNlrticGGjMcMq6IMvxzEpSMkuMQ5rWj +pZjWOoBjSYBuXdblcBRvXhOr2Ws8jJLMZfehKq9q1reQfoGV6xMnbwmumSXbWRWT +0vkCAwEAAaOBnTCBmjAdBgNVHQ4EFgQUK/Zv/AVtfIeucJw8VEtux1dhI1YwawYD +VR0jBGQwYoAUK/Zv/AVtfIeucJw8VEtux1dhI1ahP6Q9MDsxCzAJBgNVBAYTAkFV +MQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChMEYXNpb4IJ +AMJYU3U6A0IRMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBABLYXimq +v/HLyIJi7Xn8AJUsICj8LKF/J24nwwiF+ibf7UkoChJURs4nN78bod/lpDVPTEVl +gTBdV/vBJs416sCEFfsGjqB9OBYj4gb0VaJDsQd0+NMvXp0faKv2y9wgScxG9/cg +aM7eRmyfMn1qjb6tpNxVOPpe/nFi8Vx/1orejBRaZr4zF5TkoPepfwLWQeXDUIdE ++QHZ60jZAkR5RXTVU4u3kOKcJs839pmJYyxM4H2VxpR18vy4/YdIVWkREIUM2OgT +5iznIQIIgR56QRGP85uef+I6n0BHzrBk6du69bkQFxrFjLVGlal4bIQqSg4KGWgx +dEdymMWzmMxpO9s= +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIIEpgIBAAKCAQEAxxEmhwd1Ux0tQn+nKlhBVJMLS3cgiCPCNrOsreQBqggy/M+Y +MXERdZwPH6fLPyRD8Tfwiv95/YEI0ZyLmG7CR8xK4yhb2XY3iHHdAJwXjq1MMRnO +0XFYAftBsTbBgyUyAplLFaKY1+rRiPEQRdtAzSUY8sWHteiSjb0fQXcspppBX27+ ++incezSHXYd5aVjg+IuntBBM0aptZxfJwDQ1n9/KKyaKt7IS88CCjovyJe4CLuDR +NUpU2Wu2JwYaMxwyrogy/HMSlIyS4xDmtaOlmNY6gGNJgG5d1uVwFG9eE6vZazyM +ksxl96Eqr2rWt5B+gZXrEydvCa6ZJdtZFZPS+QIDAQABAoIBAQCOma+SvPoDzvvU +DiPOxqgOEMPfjHfGbm86xl0luBalGfiEd6WbjVanfGKtF4MWOUFec+chez+FJMEP +fufVC0qrKiJfNVMOpYvEd2SMgkSx1VymM8me6WXVDYsSipn2+1cm228ZEYAR9Emj +oqQ4loaGLlP/3RaJbhBF7ruMJvXaZZQ4fZy74Z4tyRaaE1B659ua7Rjne7eNhQE8 +cR7cQDkxsNNN3LTbfLRwEc/gcDXWgLe5JlR/K4ZrdKc3lyivm+Uew3ubKs+fgkyY +kHmuI3RJGIjpnsZW0/So+pHm3b/fo6lmlhTXtNNd+tkkKn2K9ttbXT3Sc13Pc+4w +c4MLyUpdAoGBAOxTtGDpeF6U4s+GPuOCzHCwKQyzfOyCL/UTZv1UJX7Kn1FYycJH +eOjtBRtS661cGkGd1MPfjdX2VV84AmBGDUmRqJ2KfTI1NjLAEJ115ANTpmSTm3lF +UYncgbzl6aflLpjE1mgY+JTJykYeN5jhhO0r2bsdY7S+zaMCSI5NLuznAoGBANej +aMtqLg2qKoq+fUkNBHHLXelR5dBXFnKgSrTj++H4yeW9pYbl8bK3gTF3I5+dSjHW +DdC4+X09iPqY7p8vm8Gq/vgO8Bu+EnKNVr80PJSj7AzFGd6mk/CVrAzoY2XJWbAp +YFwpo1WfHjS5wBfQzBlXY7kWVB7fj32kk14PYmUfAoGBAJXfd7NGHPoOfdCSGGv8 +VV7ZuQ6+/WiYH4XS6iuaI7VHFsZmAn3dCcbeGbD8Y04r7NLUH0yhB7g7YmTihk87 +3c1cPIy8eS1QJbEFsQPK8fFSKWH7YkwEM/O0DesX+5hodaaYnkiiHXNujYLuQuAH +lV87wfcyajsEDjFkj1L/i9TdAoGBAKYfRUQv8HqmdU+doHb+iEYCHb75UMpHzQtR +YTwpxoo3V5Kdnz9lNeYwaF7rIY59ZgMunEYHumw5U6V625nW228/hF0lZOR6cUu+ +hu2WGHWKMvdDgMJ+IcpeA8WN4cUwcN+9gHZ/vUzg4CxOTSYLvLBpGnIkOXnvUGPC +vaTgxTSRAoGBAOHcuZ9hcUrPuVI1HVkjQQLu5mLZ3tz6linEbe/RCdJMK8JrRX4w +ubB7gFclMYGbLlDNAJVYkydJaCy/2NAI3rfsOda+VmDqGx6z4BbSGceHhomyU1Oo +1H7YaXsuzDkzl23HRsyp0pKJpTdghZdbVsGF8vAB8ygK3ehM233neSln +-----END RSA PRIVATE KEY----- diff --git a/src/boost/libs/asio/example/cpp03/ssl/client.cpp b/src/boost/libs/asio/example/cpp03/ssl/client.cpp new file mode 100644 index 000000000..d00e15eb1 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/ssl/client.cpp @@ -0,0 +1,157 @@ +// +// client.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <iostream> +#include <boost/bind/bind.hpp> +#include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> + +enum { max_length = 1024 }; + +class client +{ +public: + client(boost::asio::io_context& io_context, + boost::asio::ssl::context& context, + boost::asio::ip::tcp::resolver::results_type endpoints) + : socket_(io_context, context) + { + socket_.set_verify_mode(boost::asio::ssl::verify_peer); + socket_.set_verify_callback( + boost::bind(&client::verify_certificate, this, + boost::placeholders::_1, boost::placeholders::_2)); + + boost::asio::async_connect(socket_.lowest_layer(), endpoints, + boost::bind(&client::handle_connect, this, + boost::asio::placeholders::error)); + } + + bool verify_certificate(bool preverified, + boost::asio::ssl::verify_context& ctx) + { + // The verify callback can be used to check whether the certificate that is + // being presented is valid for the peer. For example, RFC 2818 describes + // the steps involved in doing this for HTTPS. Consult the OpenSSL + // documentation for more details. Note that the callback is called once + // for each certificate in the certificate chain, starting from the root + // certificate authority. + + // In this example we will simply print the certificate's subject name. + char subject_name[256]; + X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); + X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256); + std::cout << "Verifying " << subject_name << "\n"; + + return preverified; + } + + void handle_connect(const boost::system::error_code& error) + { + if (!error) + { + socket_.async_handshake(boost::asio::ssl::stream_base::client, + boost::bind(&client::handle_handshake, this, + boost::asio::placeholders::error)); + } + else + { + std::cout << "Connect failed: " << error.message() << "\n"; + } + } + + void handle_handshake(const boost::system::error_code& error) + { + if (!error) + { + std::cout << "Enter message: "; + std::cin.getline(request_, max_length); + size_t request_length = strlen(request_); + + boost::asio::async_write(socket_, + boost::asio::buffer(request_, request_length), + boost::bind(&client::handle_write, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + else + { + std::cout << "Handshake failed: " << error.message() << "\n"; + } + } + + void handle_write(const boost::system::error_code& error, + size_t bytes_transferred) + { + if (!error) + { + boost::asio::async_read(socket_, + boost::asio::buffer(reply_, bytes_transferred), + boost::bind(&client::handle_read, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + else + { + std::cout << "Write failed: " << error.message() << "\n"; + } + } + + void handle_read(const boost::system::error_code& error, + size_t bytes_transferred) + { + if (!error) + { + std::cout << "Reply: "; + std::cout.write(reply_, bytes_transferred); + std::cout << "\n"; + } + else + { + std::cout << "Read failed: " << error.message() << "\n"; + } + } + +private: + boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_; + char request_[max_length]; + char reply_[max_length]; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: client <host> <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + boost::asio::ip::tcp::resolver resolver(io_context); + boost::asio::ip::tcp::resolver::results_type endpoints = + resolver.resolve(argv[1], argv[2]); + + boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); + ctx.load_verify_file("ca.pem"); + + client c(io_context, ctx, endpoints); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/ssl/dh2048.pem b/src/boost/libs/asio/example/cpp03/ssl/dh2048.pem new file mode 100644 index 000000000..07250cca6 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/ssl/dh2048.pem @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEAyNnxZSYc6J89mDNnqOH8bnwBiAJxcaUS3PkIEcwW8D9o2BlNq6EO +XKMIbdfwPFZi80GMpNu3YP2A2B42sAHmb7w7ZA92QDv3JjqzR0QuS/CkMv4CEjha +QBFwBDDWnnHBSj4w/t54ii0SH34mWcjBItI2eMtnM9J6fnvNiWqJxdt4iA4mZjZD +qZTjIRyjgKAevzkqAlBqQRoVUUgu+9Cf29wXjVl3bE+0VU5CdFeyT+Y9yunz88mq +rGyx1uPt+zbIfxuNLH+coY67y1ht7iZEL5WLd3wGCycRT+lYy2AL/rxGBPxStFIT +2bOkQao6sAfb4UdGEUlwHUXZrAV51oM30wIBAg== +-----END DH PARAMETERS----- diff --git a/src/boost/libs/asio/example/cpp03/ssl/server.cpp b/src/boost/libs/asio/example/cpp03/ssl/server.cpp new file mode 100644 index 000000000..fde441505 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/ssl/server.cpp @@ -0,0 +1,170 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <iostream> +#include <boost/bind/bind.hpp> +#include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> + +typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket; + +class session +{ +public: + session(boost::asio::io_context& io_context, + boost::asio::ssl::context& context) + : socket_(io_context, context) + { + } + + ssl_socket::lowest_layer_type& socket() + { + return socket_.lowest_layer(); + } + + void start() + { + socket_.async_handshake(boost::asio::ssl::stream_base::server, + boost::bind(&session::handle_handshake, this, + boost::asio::placeholders::error)); + } + + void handle_handshake(const boost::system::error_code& error) + { + if (!error) + { + socket_.async_read_some(boost::asio::buffer(data_, max_length), + boost::bind(&session::handle_read, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + else + { + delete this; + } + } + + void handle_read(const boost::system::error_code& error, + size_t bytes_transferred) + { + if (!error) + { + boost::asio::async_write(socket_, + boost::asio::buffer(data_, bytes_transferred), + boost::bind(&session::handle_write, this, + boost::asio::placeholders::error)); + } + else + { + delete this; + } + } + + void handle_write(const boost::system::error_code& error) + { + if (!error) + { + socket_.async_read_some(boost::asio::buffer(data_, max_length), + boost::bind(&session::handle_read, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + else + { + delete this; + } + } + +private: + ssl_socket socket_; + enum { max_length = 1024 }; + char data_[max_length]; +}; + +class server +{ +public: + server(boost::asio::io_context& io_context, unsigned short port) + : io_context_(io_context), + acceptor_(io_context, + boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), + context_(boost::asio::ssl::context::sslv23) + { + context_.set_options( + boost::asio::ssl::context::default_workarounds + | boost::asio::ssl::context::no_sslv2 + | boost::asio::ssl::context::single_dh_use); + context_.set_password_callback(boost::bind(&server::get_password, this)); + context_.use_certificate_chain_file("server.pem"); + context_.use_private_key_file("server.pem", boost::asio::ssl::context::pem); + context_.use_tmp_dh_file("dh2048.pem"); + + start_accept(); + } + + std::string get_password() const + { + return "test"; + } + + void start_accept() + { + session* new_session = new session(io_context_, context_); + acceptor_.async_accept(new_session->socket(), + boost::bind(&server::handle_accept, this, new_session, + boost::asio::placeholders::error)); + } + + void handle_accept(session* new_session, + const boost::system::error_code& error) + { + if (!error) + { + new_session->start(); + } + else + { + delete new_session; + } + + start_accept(); + } + +private: + boost::asio::io_context& io_context_; + boost::asio::ip::tcp::acceptor acceptor_; + boost::asio::ssl::context context_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + using namespace std; // For atoi. + server s(io_context, atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/ssl/server.pem b/src/boost/libs/asio/example/cpp03/ssl/server.pem new file mode 100644 index 000000000..37ea6e267 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/ssl/server.pem @@ -0,0 +1,71 @@ +-----BEGIN CERTIFICATE----- +MIIDAzCCAesCCQD9QcRiWk0y9TANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJB +VTEMMAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZTeWRuZXkxDTALBgNVBAoTBGFzaW8w +HhcNMTUxMTE4MjIzNzMxWhcNMjAxMTE2MjIzNzMxWjBMMQswCQYDVQQGEwJBVTEM +MAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZTeWRuZXkxDTALBgNVBAoTBGFzaW8xDzAN +BgNVBAsTBnNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALr0 ++NXSklsGJR7HYHP/H4V5+KpYrmFKva/K7iiqi+XyWEjGnj+/iImJW26phhg9GouN +JJxdrP7/0LwpMsEC/9v09dMNAEewtYhPgD4kiUH/E/79wVmayMZZZGrpF9Rw+wWv +q58y3L1wKge3qilX6slVDdNhqU3vBiMKEJfsjE4PKcEVjPCjVJG2562eHK9FxyjQ +DykyH61lQKBQOiElilPQKzAO7U36yTvs+chWuUfK47B8EC+PJ5KcLEppli4ljlwE +w01HnGxwvjDLobKm2jL6CWi3aYGWudyTsNAd7YC5C7psktBypQLBcfp7uUrrR5Bb +PEjFHJUWIlyoYvm2OjMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAtceVW6tixFsB +ZRhjL5aRCcbx2iMwEXd54lcP6BWe1qOcDPHoSYI1zvvGzohbEvBfqUv78S9MtzaT +gMe5rIU9M1ZM09PyaM6ZutGpKHE8L4qcOslTt41GQFsSqPFdcbgSV20MvBzjGayR +AI/WV0avW3oasdetJPZCR7bRbCbMbWTgclUfv5F25ENcR+BhNuilfL15owL0s4sS +Wb4jOOHhXV9iXeS2dH0snFqv4BmQ9ZoA7zbM9lG3EU5DuxHESYkCnzJyEqqY3vWv +PFRViCxLp5LQLmkTQ3dglVQA4x6ZaonaewdPtdhjkLUuIqDvQx5+kIaOELbSws+c +bREYlnGrFw== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,D459676347D389E9135496D8AAFA7953 + +wbrjxr9NHur8kgxDsgXOY9qFGKpONIQLxkuahUrDD/H+s/l7ugsLWOPsOXbjNL/7 +QYUBAx85HKm9D8BQ5g78Y82qfArap3/3IIuysDfQDh4fQodhVtmGTFiCOvudlGEp +lq1niQRLThlxeRoFphH8KKiOTO9a/d8tdL7zRmiFwnVnhK4014mgVmgcSefA1AF5 +RbJAeMclUKddG6ltQK00ptg84CDXiMWQXFBGGmQ1av2lyFzC+xLP+qDqZAYTM9lZ +NFRo2oEZP1ozfOVNSbXTanJgZ0DSSmhGE1PcVrHSeE/v+k1kPh3oVKi9GV51kIDC +Zd9f/XltuDOzy1Ybn6gRy4nzNpzcwjSCIHEdSD5nxU5JfHfQ3OtnsEab7qf989iP +s2LbCSp5uGTMvfesMIkixIZAQp2FeahZTAgU2Vx+wi5Kks68rOqeywEfzACL/Um5 +7XZu8gDs4MgRRWnxK1BbJDPifICLvSJZvgB9FKX/hk4FHFF+MtcrkalehCuLooDV +3rfHNvRSbg7J97XQ3QC+k9ZDaumpy6n+LhaVv7BIJRBnBBtZ5Eg3DmPg6flqaHAU +Y/8d82wb/pCmbvR3B1/Ebgs84DPJ+uZnY9M5Iwx19oqlVSR2ts/Tx619LGAm+BiQ +7YDoC4CFmpAA8Uw0xnUbNgx94NdNmlnLeLtS50b0XlWpHKbVzmVbNYEjY6NHMlLt +aqxWHTYTa7g/c1bg2/nxF1Lbfu5VSTROGBUuer1c3yzVuyBrjcX92Jp4BJH78qOp +N6lY6MnH4HYRXHjzlt/S0ZzO0faPPe18Q8SWvnDVuE3fYzzL772B56d2t8eodc+/ +t6M3qJ60eXdsmgYOaPRLRUovN2xT2UUr0+biuguHyqfaVfcEU/adw+b9oUVE+5Nw +nZHI5qhPnhLxChyZqbBl68zMUyKlfff4OyLvRGpfcHwBw6DTGjduB+DDsqqkcIB9 +2VL6nps7ZVCwMPI18siUd6cttEOf6ZXrVqHg9wfDvJOlh2NNKNLxSAFubHc90Jlj +KejrWenXo2w6YkSUeTV4t4cWu7U8rXIkTJXDl1S6NO8DWqNDo5KjgJ2SK5NlSOJ7 +jgECn390ooneJOxxytPVQO2xppXQZZS65RHrvhB+ss5xUknly9q+ICyt6xTR9nqA +PKkeSE6qVY0J4JgFXpkgQxgwMnjSED3LKr3jlz28pr5cC6tsc5SSlekHjT2fcSrX +uccaVahaJRigf+q+4XzmJtdwbZU+YWGZRVMlQLA5yzPHQHDYkPpOeYU4WReND8S4 +TZRkPHaxOZ2lKQwJB93V8Vbt2MvwRy392452a33S4TcQLaWzoOljXjmZjrp2rvRz +prBaNe8LnO4V8Oliv+H+E0UWiWFDuI+HBy4X4O9plsbw/gk64Phl9qLiBwaX/AIR +66FXvC/czABo9oSt2jekcMtJofYr8Gr2bsJlt5ZX+GEOxz4jMv7xvz5/L3W7jVav +pHGIv4xfN9FrXzL47O7UuUF9xZg4Rp/fxwpgEDNZmX/3DnP0ewZQUcgUX0pdqNGQ +YVqJXcRF7KqG2NSQFuwPESZQnxU0WzSgRyUae7xg1WKfSuN8NVAzKhOgeqlD2IAo +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIDlzCCAn+gAwIBAgIJAMJYU3U6A0IRMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNV +BAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChME +YXNpbzAeFw0xNTExMTgyMjMzNDhaFw0yMDExMTYyMjMzNDhaMDsxCzAJBgNVBAYT +AkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChMEYXNp +bzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMcRJocHdVMdLUJ/pypY +QVSTC0t3IIgjwjazrK3kAaoIMvzPmDFxEXWcDx+nyz8kQ/E38Ir/ef2BCNGci5hu +wkfMSuMoW9l2N4hx3QCcF46tTDEZztFxWAH7QbE2wYMlMgKZSxWimNfq0YjxEEXb +QM0lGPLFh7Xoko29H0F3LKaaQV9u/vop3Hs0h12HeWlY4PiLp7QQTNGqbWcXycA0 +NZ/fyismireyEvPAgo6L8iXuAi7g0TVKVNlrticGGjMcMq6IMvxzEpSMkuMQ5rWj +pZjWOoBjSYBuXdblcBRvXhOr2Ws8jJLMZfehKq9q1reQfoGV6xMnbwmumSXbWRWT +0vkCAwEAAaOBnTCBmjAdBgNVHQ4EFgQUK/Zv/AVtfIeucJw8VEtux1dhI1YwawYD +VR0jBGQwYoAUK/Zv/AVtfIeucJw8VEtux1dhI1ahP6Q9MDsxCzAJBgNVBAYTAkFV +MQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChMEYXNpb4IJ +AMJYU3U6A0IRMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBABLYXimq +v/HLyIJi7Xn8AJUsICj8LKF/J24nwwiF+ibf7UkoChJURs4nN78bod/lpDVPTEVl +gTBdV/vBJs416sCEFfsGjqB9OBYj4gb0VaJDsQd0+NMvXp0faKv2y9wgScxG9/cg +aM7eRmyfMn1qjb6tpNxVOPpe/nFi8Vx/1orejBRaZr4zF5TkoPepfwLWQeXDUIdE ++QHZ60jZAkR5RXTVU4u3kOKcJs839pmJYyxM4H2VxpR18vy4/YdIVWkREIUM2OgT +5iznIQIIgR56QRGP85uef+I6n0BHzrBk6du69bkQFxrFjLVGlal4bIQqSg4KGWgx +dEdymMWzmMxpO9s= +-----END CERTIFICATE----- diff --git a/src/boost/libs/asio/example/cpp03/timeouts/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/timeouts/Jamfile.v2 new file mode 100644 index 000000000..dcc83c435 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/timeouts/Jamfile.v2 @@ -0,0 +1,36 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/chrono//boost_chrono + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe async_tcp_client : async_tcp_client.cpp ; +exe blocking_tcp_client : blocking_tcp_client.cpp ; +exe blocking_token_tcp_client : blocking_token_tcp_client.cpp ; +exe blocking_udp_client : blocking_udp_client.cpp ; +exe server : server.cpp ; diff --git a/src/boost/libs/asio/example/cpp03/timeouts/async_tcp_client.cpp b/src/boost/libs/asio/example/cpp03/timeouts/async_tcp_client.cpp new file mode 100644 index 000000000..1f8ba01ed --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/timeouts/async_tcp_client.cpp @@ -0,0 +1,311 @@ +// +// async_tcp_client.cpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/buffer.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/read_until.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/asio/write.hpp> +#include <boost/bind/bind.hpp> +#include <iostream> +#include <string> + +using boost::asio::steady_timer; +using boost::asio::ip::tcp; + +// +// This class manages socket timeouts by applying the concept of a deadline. +// Some asynchronous operations are given deadlines by which they must complete. +// Deadlines are enforced by an "actor" that persists for the lifetime of the +// client object: +// +// +----------------+ +// | | +// | check_deadline |<---+ +// | | | +// +----------------+ | async_wait() +// | | +// +---------+ +// +// If the deadline actor determines that the deadline has expired, the socket +// is closed and any outstanding operations are consequently cancelled. +// +// Connection establishment involves trying each endpoint in turn until a +// connection is successful, or the available endpoints are exhausted. If the +// deadline actor closes the socket, the connect actor is woken up and moves to +// the next endpoint. +// +// +---------------+ +// | | +// | start_connect |<---+ +// | | | +// +---------------+ | +// | | +// async_- | +----------------+ +// connect() | | | +// +--->| handle_connect | +// | | +// +----------------+ +// : +// Once a connection is : +// made, the connect : +// actor forks in two - : +// : +// an actor for reading : and an actor for +// inbound messages: : sending heartbeats: +// : +// +------------+ : +-------------+ +// | |<- - - - -+- - - - ->| | +// | start_read | | start_write |<---+ +// | |<---+ | | | +// +------------+ | +-------------+ | async_wait() +// | | | | +// async_- | +-------------+ async_- | +--------------+ +// read_- | | | write() | | | +// until() +--->| handle_read | +--->| handle_write | +// | | | | +// +-------------+ +--------------+ +// +// The input actor reads messages from the socket, where messages are delimited +// by the newline character. The deadline for a complete message is 30 seconds. +// +// The heartbeat actor sends a heartbeat (a message that consists of a single +// newline character) every 10 seconds. In this example, no deadline is applied +// to message sending. +// +class client +{ +public: + client(boost::asio::io_context& io_context) + : stopped_(false), + socket_(io_context), + deadline_(io_context), + heartbeat_timer_(io_context) + { + } + + // Called by the user of the client class to initiate the connection process. + // The endpoints will have been obtained using a tcp::resolver. + void start(tcp::resolver::results_type endpoints) + { + // Start the connect actor. + endpoints_ = endpoints; + start_connect(endpoints_.begin()); + + // Start the deadline actor. You will note that we're not setting any + // particular deadline here. Instead, the connect and input actors will + // update the deadline prior to each asynchronous operation. + deadline_.async_wait(boost::bind(&client::check_deadline, this)); + } + + // This function terminates all the actors to shut down the connection. It + // may be called by the user of the client class, or by the class itself in + // response to graceful termination or an unrecoverable error. + void stop() + { + stopped_ = true; + boost::system::error_code ignored_ec; + socket_.close(ignored_ec); + deadline_.cancel(); + heartbeat_timer_.cancel(); + } + +private: + void start_connect(tcp::resolver::results_type::iterator endpoint_iter) + { + if (endpoint_iter != endpoints_.end()) + { + std::cout << "Trying " << endpoint_iter->endpoint() << "...\n"; + + // Set a deadline for the connect operation. + deadline_.expires_after(boost::asio::chrono::seconds(60)); + + // Start the asynchronous connect operation. + socket_.async_connect(endpoint_iter->endpoint(), + boost::bind(&client::handle_connect, this, + boost::placeholders::_1, endpoint_iter)); + } + else + { + // There are no more endpoints to try. Shut down the client. + stop(); + } + } + + void handle_connect(const boost::system::error_code& ec, + tcp::resolver::results_type::iterator endpoint_iter) + { + if (stopped_) + return; + + // The async_connect() function automatically opens the socket at the start + // of the asynchronous operation. If the socket is closed at this time then + // the timeout handler must have run first. + if (!socket_.is_open()) + { + std::cout << "Connect timed out\n"; + + // Try the next available endpoint. + start_connect(++endpoint_iter); + } + + // Check if the connect operation failed before the deadline expired. + else if (ec) + { + std::cout << "Connect error: " << ec.message() << "\n"; + + // We need to close the socket used in the previous connection attempt + // before starting a new one. + socket_.close(); + + // Try the next available endpoint. + start_connect(++endpoint_iter); + } + + // Otherwise we have successfully established a connection. + else + { + std::cout << "Connected to " << endpoint_iter->endpoint() << "\n"; + + // Start the input actor. + start_read(); + + // Start the heartbeat actor. + start_write(); + } + } + + void start_read() + { + // Set a deadline for the read operation. + deadline_.expires_after(boost::asio::chrono::seconds(30)); + + // Start an asynchronous operation to read a newline-delimited message. + boost::asio::async_read_until(socket_, + boost::asio::dynamic_buffer(input_buffer_), '\n', + boost::bind(&client::handle_read, this, + boost::placeholders::_1, boost::placeholders::_2)); + } + + void handle_read(const boost::system::error_code& ec, std::size_t n) + { + if (stopped_) + return; + + if (!ec) + { + // Extract the newline-delimited message from the buffer. + std::string line(input_buffer_.substr(0, n - 1)); + input_buffer_.erase(0, n); + + // Empty messages are heartbeats and so ignored. + if (!line.empty()) + { + std::cout << "Received: " << line << "\n"; + } + + start_read(); + } + else + { + std::cout << "Error on receive: " << ec.message() << "\n"; + + stop(); + } + } + + void start_write() + { + if (stopped_) + return; + + // Start an asynchronous operation to send a heartbeat message. + boost::asio::async_write(socket_, boost::asio::buffer("\n", 1), + boost::bind(&client::handle_write, this, boost::placeholders::_1)); + } + + void handle_write(const boost::system::error_code& ec) + { + if (stopped_) + return; + + if (!ec) + { + // Wait 10 seconds before sending the next heartbeat. + heartbeat_timer_.expires_after(boost::asio::chrono::seconds(10)); + heartbeat_timer_.async_wait(boost::bind(&client::start_write, this)); + } + else + { + std::cout << "Error on heartbeat: " << ec.message() << "\n"; + + stop(); + } + } + + void check_deadline() + { + if (stopped_) + return; + + // Check whether the deadline has passed. We compare the deadline against + // the current time since a new asynchronous operation may have moved the + // deadline before this actor had a chance to run. + if (deadline_.expiry() <= steady_timer::clock_type::now()) + { + // The deadline has passed. The socket is closed so that any outstanding + // asynchronous operations are cancelled. + socket_.close(); + + // There is no longer an active deadline. The expiry is set to the + // maximum time point so that the actor takes no action until a new + // deadline is set. + deadline_.expires_at(steady_timer::time_point::max()); + } + + // Put the actor back to sleep. + deadline_.async_wait(boost::bind(&client::check_deadline, this)); + } + +private: + bool stopped_; + tcp::resolver::results_type endpoints_; + tcp::socket socket_; + std::string input_buffer_; + steady_timer deadline_; + steady_timer heartbeat_timer_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: client <host> <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + tcp::resolver r(io_context); + client c(io_context); + + c.start(r.resolve(argv[1], argv[2])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/timeouts/blocking_tcp_client.cpp b/src/boost/libs/asio/example/cpp03/timeouts/blocking_tcp_client.cpp new file mode 100644 index 000000000..a0bbaeac6 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/timeouts/blocking_tcp_client.cpp @@ -0,0 +1,191 @@ +// +// blocking_tcp_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/buffer.hpp> +#include <boost/asio/connect.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/read_until.hpp> +#include <boost/system/system_error.hpp> +#include <boost/asio/write.hpp> +#include <cstdlib> +#include <iostream> +#include <string> +#include <boost/lambda/bind.hpp> +#include <boost/lambda/lambda.hpp> + +using boost::asio::ip::tcp; +using boost::lambda::bind; +using boost::lambda::var; +using boost::lambda::_1; +using boost::lambda::_2; + +//---------------------------------------------------------------------- + +// +// This class manages socket timeouts by running the io_context using the timed +// io_context::run_for() member function. Each asynchronous operation is given +// a timeout within which it must complete. The socket operations themselves +// use boost::lambda function objects as completion handlers. For a given +// socket operation, the client object runs the io_context to block thread +// execution until the operation completes or the timeout is reached. If the +// io_context::run_for() function times out, the socket is closed and the +// outstanding asynchronous operation is cancelled. +// +class client +{ +public: + client() + : socket_(io_context_) + { + } + + void connect(const std::string& host, const std::string& service, + boost::asio::chrono::steady_clock::duration timeout) + { + // Resolve the host name and service to a list of endpoints. + tcp::resolver::results_type endpoints = + tcp::resolver(io_context_).resolve(host, service); + + // Start the asynchronous operation itself. The boost::lambda function + // object is used as a callback and will update the ec variable when the + // operation completes. The blocking_udp_client.cpp example shows how you + // can use boost::bind rather than boost::lambda. + boost::system::error_code ec; + boost::asio::async_connect(socket_, endpoints, var(ec) = _1); + + // Run the operation until it completes, or until the timeout. + run(timeout); + + // Determine whether a connection was successfully established. + if (ec) + throw boost::system::system_error(ec); + } + + std::string read_line(boost::asio::chrono::steady_clock::duration timeout) + { + // Start the asynchronous operation. The boost::lambda function object is + // used as a callback and will update the ec variable when the operation + // completes. The blocking_udp_client.cpp example shows how you can use + // boost::bind rather than boost::lambda. + boost::system::error_code ec; + std::size_t n = 0; + boost::asio::async_read_until(socket_, + boost::asio::dynamic_buffer(input_buffer_), + '\n', (var(ec) = _1, var(n) = _2)); + + // Run the operation until it completes, or until the timeout. + run(timeout); + + // Determine whether the read completed successfully. + if (ec) + throw boost::system::system_error(ec); + + std::string line(input_buffer_.substr(0, n - 1)); + input_buffer_.erase(0, n); + return line; + } + + void write_line(const std::string& line, + boost::asio::chrono::steady_clock::duration timeout) + { + std::string data = line + "\n"; + + // Start the asynchronous operation. The boost::lambda function object is + // used as a callback and will update the ec variable when the operation + // completes. The blocking_udp_client.cpp example shows how you can use + // boost::bind rather than boost::lambda. + boost::system::error_code ec; + boost::asio::async_write(socket_, boost::asio::buffer(data), var(ec) = _1); + + // Run the operation until it completes, or until the timeout. + run(timeout); + + // Determine whether the read completed successfully. + if (ec) + throw boost::system::system_error(ec); + } + +private: + void run(boost::asio::chrono::steady_clock::duration timeout) + { + // Restart the io_context, as it may have been left in the "stopped" state + // by a previous operation. + io_context_.restart(); + + // Block until the asynchronous operation has completed, or timed out. If + // the pending asynchronous operation is a composed operation, the deadline + // applies to the entire operation, rather than individual operations on + // the socket. + io_context_.run_for(timeout); + + // If the asynchronous operation completed successfully then the io_context + // would have been stopped due to running out of work. If it was not + // stopped, then the io_context::run_for call must have timed out. + if (!io_context_.stopped()) + { + // Close the socket to cancel the outstanding asynchronous operation. + socket_.close(); + + // Run the io_context again until the operation completes. + io_context_.run(); + } + } + + boost::asio::io_context io_context_; + tcp::socket socket_; + std::string input_buffer_; +}; + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 4) + { + std::cerr << "Usage: blocking_tcp_client <host> <port> <message>\n"; + return 1; + } + + client c; + c.connect(argv[1], argv[2], boost::asio::chrono::seconds(10)); + + boost::asio::chrono::steady_clock::time_point time_sent = + boost::asio::chrono::steady_clock::now(); + + c.write_line(argv[3], boost::asio::chrono::seconds(10)); + + for (;;) + { + std::string line = c.read_line(boost::asio::chrono::seconds(10)); + + // Keep going until we get back the line that was sent. + if (line == argv[3]) + break; + } + + boost::asio::chrono::steady_clock::time_point time_received = + boost::asio::chrono::steady_clock::now(); + + std::cout << "Round trip time: "; + std::cout << boost::asio::chrono::duration_cast< + boost::asio::chrono::microseconds>( + time_received - time_sent).count(); + std::cout << " microseconds\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/timeouts/blocking_token_tcp_client.cpp b/src/boost/libs/asio/example/cpp03/timeouts/blocking_token_tcp_client.cpp new file mode 100644 index 000000000..effb491c2 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/timeouts/blocking_token_tcp_client.cpp @@ -0,0 +1,202 @@ +// +// blocking_token_tcp_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/connect.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/read_until.hpp> +#include <boost/asio/streambuf.hpp> +#include <boost/system/system_error.hpp> +#include <boost/asio/write.hpp> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <string> + +using boost::asio::ip::tcp; + +// We will use our sockets only with an io_context. +typedef boost::asio::basic_stream_socket<tcp, + boost::asio::io_context::executor_type> tcp_socket; + +//---------------------------------------------------------------------- + +// A custom completion token that makes asynchronous operations behave as +// though they are blocking calls with a timeout. +struct close_after +{ + close_after(boost::asio::chrono::steady_clock::duration t, tcp_socket& s) + : timeout_(t), socket_(s) + { + } + + // The maximum time to wait for an asynchronous operation to complete. + boost::asio::chrono::steady_clock::duration timeout_; + + // The socket to be closed if the operation does not complete in time. + tcp_socket& socket_; +}; + +namespace boost { +namespace asio { + +// The async_result template is specialised to allow the close_after token to +// be used with asynchronous operations that have a completion signature of +// void(error_code, T). Generalising this for all completion signature forms is +// left as an exercise for the reader. +template <typename T> +class async_result<close_after, void(boost::system::error_code, T)> +{ +public: + // An asynchronous operation's initiating function automatically creates an + // completion_handler_type object from the token. This function object is + // then called on completion of the asynchronous operation. + class completion_handler_type + { + public: + completion_handler_type(const close_after& token) + : token_(token) + { + } + + void operator()(boost::system::error_code ec, T t) + { + *ec_ = ec; + *t_ = t; + } + + private: + friend class async_result; + close_after token_; + boost::system::error_code* ec_; + T* t_; + }; + + // The async_result constructor associates the completion handler object with + // the result of the initiating function. + explicit async_result(completion_handler_type& h) + : timeout_(h.token_.timeout_), + socket_(h.token_.socket_) + { + h.ec_ = &ec_; + h.t_ = &t_; + } + + // The return_type typedef determines the result type of the asynchronous + // operation's initiating function. + typedef T return_type; + + // The get() function is used to obtain the result of the asynchronous + // operation's initiating function. For the close_after completion token, we + // use this function to run the io_context until the operation is complete. + return_type get() + { + boost::asio::io_context& io_context = socket_.get_executor().context(); + + // Restart the io_context, as it may have been left in the "stopped" state + // by a previous operation. + io_context.restart(); + + // Block until the asynchronous operation has completed, or timed out. If + // the pending asynchronous operation is a composed operation, the deadline + // applies to the entire operation, rather than individual operations on + // the socket. + io_context.run_for(timeout_); + + // If the asynchronous operation completed successfully then the io_context + // would have been stopped due to running out of work. If it was not + // stopped, then the io_context::run_for call must have timed out and the + // operation is still incomplete. + if (!io_context.stopped()) + { + // Close the socket to cancel the outstanding asynchronous operation. + socket_.close(); + + // Run the io_context again until the operation completes. + io_context.run(); + } + + // If the operation failed, throw an exception. Otherwise return the result. + return ec_ ? throw boost::system::system_error(ec_) : t_; + } + +private: + boost::asio::chrono::steady_clock::duration timeout_; + tcp_socket& socket_; + boost::system::error_code ec_; + T t_; +}; + +} // namespace asio +} // namespace boost + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 4) + { + std::cerr << "Usage: blocking_tcp_client <host> <port> <message>\n"; + return 1; + } + + boost::asio::io_context io_context; + + // Resolve the host name and service to a list of endpoints. + tcp::resolver::results_type endpoints = + tcp::resolver(io_context).resolve(argv[1], argv[2]); + + tcp_socket socket(io_context); + + // Run an asynchronous connect operation with a timeout. + boost::asio::async_connect(socket, endpoints, + close_after(boost::asio::chrono::seconds(10), socket)); + + boost::asio::chrono::steady_clock::time_point time_sent = + boost::asio::chrono::steady_clock::now(); + + // Run an asynchronous write operation with a timeout. + std::string msg = argv[3] + std::string("\n"); + boost::asio::async_write(socket, boost::asio::buffer(msg), + close_after(boost::asio::chrono::seconds(10), socket)); + + for (std::string input_buffer;;) + { + // Run an asynchronous read operation with a timeout. + std::size_t n = boost::asio::async_read_until(socket, + boost::asio::dynamic_buffer(input_buffer), '\n', + close_after(boost::asio::chrono::seconds(10), socket)); + + std::string line(input_buffer.substr(0, n - 1)); + input_buffer.erase(0, n); + + // Keep going until we get back the line that was sent. + if (line == argv[3]) + break; + } + + boost::asio::chrono::steady_clock::time_point time_received = + boost::asio::chrono::steady_clock::now(); + + std::cout << "Round trip time: "; + std::cout << boost::asio::chrono::duration_cast< + boost::asio::chrono::microseconds>( + time_received - time_sent).count(); + std::cout << " microseconds\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/timeouts/blocking_udp_client.cpp b/src/boost/libs/asio/example/cpp03/timeouts/blocking_udp_client.cpp new file mode 100644 index 000000000..95c3480c1 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/timeouts/blocking_udp_client.cpp @@ -0,0 +1,154 @@ +// +// blocking_udp_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/buffer.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/udp.hpp> +#include <cstdlib> +#include <boost/bind/bind.hpp> +#include <iostream> + +using boost::asio::ip::udp; + +//---------------------------------------------------------------------- + +// +// This class manages socket timeouts by running the io_context using the timed +// io_context::run_for() member function. Each asynchronous operation is given +// a timeout within which it must complete. The socket operations themselves +// use boost::bind to specify the completion handler: +// +// +---------------+ +// | | +// | receive | +// | | +// +---------------+ +// | +// async_- | +----------------+ +// receive() | | | +// +--->| handle_receive | +// | | +// +----------------+ +// +// For a given socket operation, the client object runs the io_context to block +// thread execution until the operation completes or the timeout is reached. If +// the io_context::run_for() function times out, the socket is closed and the +// outstanding asynchronous operation is cancelled. +// +class client +{ +public: + client(const udp::endpoint& listen_endpoint) + : socket_(io_context_, listen_endpoint) + { + } + + std::size_t receive(const boost::asio::mutable_buffer& buffer, + boost::asio::chrono::steady_clock::duration timeout, + boost::system::error_code& ec) + { + // Start the asynchronous operation. The handle_receive function used as a + // callback will update the ec and length variables. + std::size_t length = 0; + socket_.async_receive(boost::asio::buffer(buffer), + boost::bind(&client::handle_receive, + boost::placeholders::_1, boost::placeholders::_2, &ec, &length)); + + // Run the operation until it completes, or until the timeout. + run(timeout); + + return length; + } + +private: + void run(boost::asio::chrono::steady_clock::duration timeout) + { + // Restart the io_context, as it may have been left in the "stopped" state + // by a previous operation. + io_context_.restart(); + + // Block until the asynchronous operation has completed, or timed out. If + // the pending asynchronous operation is a composed operation, the deadline + // applies to the entire operation, rather than individual operations on + // the socket. + io_context_.run_for(timeout); + + // If the asynchronous operation completed successfully then the io_context + // would have been stopped due to running out of work. If it was not + // stopped, then the io_context::run_for call must have timed out. + if (!io_context_.stopped()) + { + // Cancel the outstanding asynchronous operation. + socket_.cancel(); + + // Run the io_context again until the operation completes. + io_context_.run(); + } + } + + static void handle_receive( + const boost::system::error_code& ec, std::size_t length, + boost::system::error_code* out_ec, std::size_t* out_length) + { + *out_ec = ec; + *out_length = length; + } + +private: + boost::asio::io_context io_context_; + udp::socket socket_; +}; + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + try + { + using namespace std; // For atoi. + + if (argc != 3) + { + std::cerr << "Usage: blocking_udp_client <listen_addr> <listen_port>\n"; + return 1; + } + + udp::endpoint listen_endpoint( + boost::asio::ip::make_address(argv[1]), + std::atoi(argv[2])); + + client c(listen_endpoint); + + for (;;) + { + char data[1024]; + boost::system::error_code ec; + std::size_t n = c.receive(boost::asio::buffer(data), + boost::asio::chrono::seconds(10), ec); + + if (ec) + { + std::cout << "Receive error: " << ec.message() << "\n"; + } + else + { + std::cout << "Received: "; + std::cout.write(data, n); + std::cout << "\n"; + } + } + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/timeouts/server.cpp b/src/boost/libs/asio/example/cpp03/timeouts/server.cpp new file mode 100644 index 000000000..0b48ea01d --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/timeouts/server.cpp @@ -0,0 +1,433 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <algorithm> +#include <cstdlib> +#include <deque> +#include <iostream> +#include <set> +#include <string> +#include <boost/bind/bind.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/asio/buffer.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/ip/udp.hpp> +#include <boost/asio/read_until.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/asio/write.hpp> + +using boost::asio::steady_timer; +using boost::asio::ip::tcp; +using boost::asio::ip::udp; + +//---------------------------------------------------------------------- + +class subscriber +{ +public: + virtual ~subscriber() {} + virtual void deliver(const std::string& msg) = 0; +}; + +typedef boost::shared_ptr<subscriber> subscriber_ptr; + +//---------------------------------------------------------------------- + +class channel +{ +public: + void join(subscriber_ptr subscriber) + { + subscribers_.insert(subscriber); + } + + void leave(subscriber_ptr subscriber) + { + subscribers_.erase(subscriber); + } + + void deliver(const std::string& msg) + { + std::for_each(subscribers_.begin(), subscribers_.end(), + boost::bind(&subscriber::deliver, + boost::placeholders::_1, boost::ref(msg))); + } + +private: + std::set<subscriber_ptr> subscribers_; +}; + +//---------------------------------------------------------------------- + +// +// This class manages socket timeouts by applying the concept of a deadline. +// Some asynchronous operations are given deadlines by which they must complete. +// Deadlines are enforced by two "actors" that persist for the lifetime of the +// session object, one for input and one for output: +// +// +----------------+ +----------------+ +// | | | | +// | check_deadline |<---+ | check_deadline |<---+ +// | | | async_wait() | | | async_wait() +// +----------------+ | on input +----------------+ | on output +// | | deadline | | deadline +// +---------+ +---------+ +// +// If either deadline actor determines that the corresponding deadline has +// expired, the socket is closed and any outstanding operations are cancelled. +// +// The input actor reads messages from the socket, where messages are delimited +// by the newline character: +// +// +------------+ +// | | +// | start_read |<---+ +// | | | +// +------------+ | +// | | +// async_- | +-------------+ +// read_- | | | +// until() +--->| handle_read | +// | | +// +-------------+ +// +// The deadline for receiving a complete message is 30 seconds. If a non-empty +// message is received, it is delivered to all subscribers. If a heartbeat (a +// message that consists of a single newline character) is received, a heartbeat +// is enqueued for the client, provided there are no other messages waiting to +// be sent. +// +// The output actor is responsible for sending messages to the client: +// +// +--------------+ +// | |<---------------------+ +// | await_output | | +// | |<---+ | +// +--------------+ | | +// | | | async_wait() | +// | +--------+ | +// V | +// +-------------+ +--------------+ +// | | async_write() | | +// | start_write |-------------->| handle_write | +// | | | | +// +-------------+ +--------------+ +// +// The output actor first waits for an output message to be enqueued. It does +// this by using a steady_timer as an asynchronous condition variable. The +// steady_timer will be signalled whenever the output queue is non-empty. +// +// Once a message is available, it is sent to the client. The deadline for +// sending a complete message is 30 seconds. After the message is successfully +// sent, the output actor again waits for the output queue to become non-empty. +// +class tcp_session + : public subscriber, + public boost::enable_shared_from_this<tcp_session> +{ +public: + tcp_session(boost::asio::io_context& io_context, channel& ch) + : channel_(ch), + socket_(io_context), + input_deadline_(io_context), + non_empty_output_queue_(io_context), + output_deadline_(io_context) + { + input_deadline_.expires_at(steady_timer::time_point::max()); + output_deadline_.expires_at(steady_timer::time_point::max()); + + // The non_empty_output_queue_ steady_timer is set to the maximum time + // point whenever the output queue is empty. This ensures that the output + // actor stays asleep until a message is put into the queue. + non_empty_output_queue_.expires_at(steady_timer::time_point::max()); + } + + tcp::socket& socket() + { + return socket_; + } + + // Called by the server object to initiate the four actors. + void start() + { + channel_.join(shared_from_this()); + + start_read(); + + input_deadline_.async_wait( + boost::bind(&tcp_session::check_deadline, + shared_from_this(), &input_deadline_)); + + await_output(); + + output_deadline_.async_wait( + boost::bind(&tcp_session::check_deadline, + shared_from_this(), &output_deadline_)); + } + +private: + void stop() + { + channel_.leave(shared_from_this()); + + boost::system::error_code ignored_ec; + socket_.close(ignored_ec); + input_deadline_.cancel(); + non_empty_output_queue_.cancel(); + output_deadline_.cancel(); + } + + bool stopped() const + { + return !socket_.is_open(); + } + + void deliver(const std::string& msg) + { + output_queue_.push_back(msg + "\n"); + + // Signal that the output queue contains messages. Modifying the expiry + // will wake the output actor, if it is waiting on the timer. + non_empty_output_queue_.expires_at(steady_timer::time_point::min()); + } + + void start_read() + { + // Set a deadline for the read operation. + input_deadline_.expires_after(boost::asio::chrono::seconds(30)); + + // Start an asynchronous operation to read a newline-delimited message. + boost::asio::async_read_until(socket_, + boost::asio::dynamic_buffer(input_buffer_), '\n', + boost::bind(&tcp_session::handle_read, shared_from_this(), + boost::placeholders::_1, boost::placeholders::_2)); + } + + void handle_read(const boost::system::error_code& ec, std::size_t n) + { + if (stopped()) + return; + + if (!ec) + { + // Extract the newline-delimited message from the buffer. + std::string msg(input_buffer_.substr(0, n - 1)); + input_buffer_.erase(0, n); + + if (!msg.empty()) + { + channel_.deliver(msg); + } + else + { + // We received a heartbeat message from the client. If there's nothing + // else being sent or ready to be sent, send a heartbeat right back. + if (output_queue_.empty()) + { + output_queue_.push_back("\n"); + + // Signal that the output queue contains messages. Modifying the + // expiry will wake the output actor, if it is waiting on the timer. + non_empty_output_queue_.expires_at(steady_timer::time_point::min()); + } + } + + start_read(); + } + else + { + stop(); + } + } + + void await_output() + { + if (stopped()) + return; + + if (output_queue_.empty()) + { + // There are no messages that are ready to be sent. The actor goes to + // sleep by waiting on the non_empty_output_queue_ timer. When a new + // message is added, the timer will be modified and the actor will wake. + non_empty_output_queue_.expires_at(steady_timer::time_point::max()); + non_empty_output_queue_.async_wait( + boost::bind(&tcp_session::await_output, shared_from_this())); + } + else + { + start_write(); + } + } + + void start_write() + { + // Set a deadline for the write operation. + output_deadline_.expires_after(boost::asio::chrono::seconds(30)); + + // Start an asynchronous operation to send a message. + boost::asio::async_write(socket_, + boost::asio::buffer(output_queue_.front()), + boost::bind(&tcp_session::handle_write, + shared_from_this(), boost::placeholders::_1)); + } + + void handle_write(const boost::system::error_code& ec) + { + if (stopped()) + return; + + if (!ec) + { + output_queue_.pop_front(); + + await_output(); + } + else + { + stop(); + } + } + + void check_deadline(steady_timer* deadline) + { + if (stopped()) + return; + + // Check whether the deadline has passed. We compare the deadline against + // the current time since a new asynchronous operation may have moved the + // deadline before this actor had a chance to run. + if (deadline->expiry() <= steady_timer::clock_type::now()) + { + // The deadline has passed. Stop the session. The other actors will + // terminate as soon as possible. + stop(); + } + else + { + // Put the actor back to sleep. + deadline->async_wait( + boost::bind(&tcp_session::check_deadline, + shared_from_this(), deadline)); + } + } + + channel& channel_; + tcp::socket socket_; + std::string input_buffer_; + steady_timer input_deadline_; + std::deque<std::string> output_queue_; + steady_timer non_empty_output_queue_; + steady_timer output_deadline_; +}; + +typedef boost::shared_ptr<tcp_session> tcp_session_ptr; + +//---------------------------------------------------------------------- + +class udp_broadcaster + : public subscriber +{ +public: + udp_broadcaster(boost::asio::io_context& io_context, + const udp::endpoint& broadcast_endpoint) + : socket_(io_context) + { + socket_.connect(broadcast_endpoint); + socket_.set_option(udp::socket::broadcast(true)); + } + +private: + void deliver(const std::string& msg) + { + boost::system::error_code ignored_ec; + socket_.send(boost::asio::buffer(msg), 0, ignored_ec); + } + + udp::socket socket_; +}; + +//---------------------------------------------------------------------- + +class server +{ +public: + server(boost::asio::io_context& io_context, + const tcp::endpoint& listen_endpoint, + const udp::endpoint& broadcast_endpoint) + : io_context_(io_context), + acceptor_(io_context, listen_endpoint) + { + subscriber_ptr bc(new udp_broadcaster(io_context_, broadcast_endpoint)); + channel_.join(bc); + + start_accept(); + } + + void start_accept() + { + tcp_session_ptr new_session(new tcp_session(io_context_, channel_)); + + acceptor_.async_accept(new_session->socket(), + boost::bind(&server::handle_accept, + this, new_session, boost::placeholders::_1)); + } + + void handle_accept(tcp_session_ptr session, + const boost::system::error_code& ec) + { + if (!ec) + { + session->start(); + } + + start_accept(); + } + +private: + boost::asio::io_context& io_context_; + tcp::acceptor acceptor_; + channel channel_; +}; + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + try + { + using namespace std; // For atoi. + + if (argc != 4) + { + std::cerr << "Usage: server <listen_port> <bcast_address> <bcast_port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + tcp::endpoint listen_endpoint(tcp::v4(), atoi(argv[1])); + + udp::endpoint broadcast_endpoint( + boost::asio::ip::make_address(argv[2]), atoi(argv[3])); + + server s(io_context, listen_endpoint, broadcast_endpoint); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/timers/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/timers/Jamfile.v2 new file mode 100644 index 000000000..7c57391ed --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/timers/Jamfile.v2 @@ -0,0 +1,30 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe time_t_timer + : time_t_timer.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/timers/time_t_timer.cpp b/src/boost/libs/asio/example/cpp03/timers/time_t_timer.cpp new file mode 100644 index 000000000..6efc29ebc --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/timers/time_t_timer.cpp @@ -0,0 +1,106 @@ +// +// time_t_timer.cpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio.hpp> +#include <ctime> +#include <iostream> + +// A custom implementation of the Clock concept from the standard C++ library. +struct time_t_clock +{ + // The duration type. + typedef boost::asio::chrono::steady_clock::duration duration; + + // The duration's underlying arithmetic representation. + typedef duration::rep rep; + + // The ratio representing the duration's tick period. + typedef duration::period period; + + // An absolute time point represented using the clock. + typedef boost::asio::chrono::time_point<time_t_clock> time_point; + + // The clock is not monotonically increasing. + static const bool is_steady = false; + + // Get the current time. + static time_point now() + { + return time_point() + boost::asio::chrono::seconds(std::time(0)); + } +}; + +// The boost::asio::basic_waitable_timer template accepts an optional WaitTraits +// template parameter. The underlying time_t clock has one-second granularity, +// so these traits may be customised to reduce the latency between the clock +// ticking over and a wait operation's completion. When the timeout is near +// (less than one second away) we poll the clock more frequently to detect the +// time change closer to when it occurs. The user can select the appropriate +// trade off between accuracy and the increased CPU cost of polling. In extreme +// cases, a zero duration may be returned to make the timers as accurate as +// possible, albeit with 100% CPU usage. +struct time_t_wait_traits +{ + // Determine how long until the clock should be next polled to determine + // whether the duration has elapsed. + static time_t_clock::duration to_wait_duration( + const time_t_clock::duration& d) + { + if (d > boost::asio::chrono::seconds(1)) + return d - boost::asio::chrono::seconds(1); + else if (d > boost::asio::chrono::seconds(0)) + return boost::asio::chrono::milliseconds(10); + else + return boost::asio::chrono::seconds(0); + } + + // Determine how long until the clock should be next polled to determine + // whether the absoluate time has been reached. + static time_t_clock::duration to_wait_duration( + const time_t_clock::time_point& t) + { + return to_wait_duration(t - time_t_clock::now()); + } +}; + +typedef boost::asio::basic_waitable_timer< + time_t_clock, time_t_wait_traits> time_t_timer; + +void handle_timeout(const boost::system::error_code&) +{ + std::cout << "handle_timeout\n"; +} + +int main() +{ + try + { + boost::asio::io_context io_context; + + time_t_timer timer(io_context); + + timer.expires_after(boost::asio::chrono::seconds(5)); + std::cout << "Starting synchronous wait\n"; + timer.wait(); + std::cout << "Finished synchronous wait\n"; + + timer.expires_after(boost::asio::chrono::seconds(5)); + std::cout << "Starting asynchronous wait\n"; + timer.async_wait(&handle_timeout); + io_context.run(); + std::cout << "Finished asynchronous wait\n"; + } + catch (std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/tutorial/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/tutorial/Jamfile.v2 new file mode 100644 index 000000000..3cc39ec0e --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/Jamfile.v2 @@ -0,0 +1,67 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/chrono//boost_chrono + <library>/boost/thread//boost_thread + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +obj timer1.obj : timer1/timer.cpp ; +exe timer1 : timer1.obj ; + +obj timer2.obj : timer2/timer.cpp ; +exe timer2 : timer2.obj ; + +obj timer3.obj : timer3/timer.cpp ; +exe timer3 : timer3.obj ; + +obj timer4.obj : timer4/timer.cpp ; +exe timer4 : timer4.obj ; + +obj timer5.obj : timer5/timer.cpp ; +exe timer5 : timer5.obj ; + +obj daytime1_client.obj : daytime1/client.cpp ; +exe daytime1_client : daytime1_client.obj ; + +obj daytime2_server.obj : daytime2/server.cpp ; +exe daytime2_server : daytime2_server.obj ; + +obj daytime3_server.obj : daytime3/server.cpp ; +exe daytime3_server : daytime3_server.obj ; + +obj daytime4_client.obj : daytime4/client.cpp ; +exe daytime4_client : daytime4_client.obj ; + +obj daytime5_server.obj : daytime5/server.cpp ; +exe daytime5_server : daytime5_server.obj ; + +obj daytime6_server.obj : daytime6/server.cpp ; +exe daytime6_server : daytime6_server.obj ; + +obj daytime7_server.obj : daytime7/server.cpp ; +exe daytime7_server : daytime7_server.obj ; diff --git a/src/boost/libs/asio/example/cpp03/tutorial/daytime1/client.cpp b/src/boost/libs/asio/example/cpp03/tutorial/daytime1/client.cpp new file mode 100644 index 000000000..42acecaef --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/daytime1/client.cpp @@ -0,0 +1,57 @@ +// +// client.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <boost/array.hpp> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: client <host>" << std::endl; + return 1; + } + + boost::asio::io_context io_context; + + tcp::resolver resolver(io_context); + tcp::resolver::results_type endpoints = + resolver.resolve(argv[1], "daytime"); + + tcp::socket socket(io_context); + boost::asio::connect(socket, endpoints); + + for (;;) + { + boost::array<char, 128> buf; + boost::system::error_code error; + + size_t len = socket.read_some(boost::asio::buffer(buf), error); + + if (error == boost::asio::error::eof) + break; // Connection closed cleanly by peer. + else if (error) + throw boost::system::system_error(error); // Some other error. + + std::cout.write(buf.data(), len); + } + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/tutorial/daytime2/server.cpp b/src/boost/libs/asio/example/cpp03/tutorial/daytime2/server.cpp new file mode 100644 index 000000000..332eb159f --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/daytime2/server.cpp @@ -0,0 +1,50 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <ctime> +#include <iostream> +#include <string> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +std::string make_daytime_string() +{ + using namespace std; // For time_t, time and ctime; + time_t now = time(0); + return ctime(&now); +} + +int main() +{ + try + { + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 13)); + + for (;;) + { + tcp::socket socket(io_context); + acceptor.accept(socket); + + std::string message = make_daytime_string(); + + boost::system::error_code ignored_error; + boost::asio::write(socket, boost::asio::buffer(message), ignored_error); + } + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/tutorial/daytime3/server.cpp b/src/boost/libs/asio/example/cpp03/tutorial/daytime3/server.cpp new file mode 100644 index 000000000..01a2a8838 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/daytime3/server.cpp @@ -0,0 +1,119 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <ctime> +#include <iostream> +#include <string> +#include <boost/bind/bind.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +std::string make_daytime_string() +{ + using namespace std; // For time_t, time and ctime; + time_t now = time(0); + return ctime(&now); +} + +class tcp_connection + : public boost::enable_shared_from_this<tcp_connection> +{ +public: + typedef boost::shared_ptr<tcp_connection> pointer; + + static pointer create(boost::asio::io_context& io_context) + { + return pointer(new tcp_connection(io_context)); + } + + tcp::socket& socket() + { + return socket_; + } + + void start() + { + message_ = make_daytime_string(); + + boost::asio::async_write(socket_, boost::asio::buffer(message_), + boost::bind(&tcp_connection::handle_write, shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + +private: + tcp_connection(boost::asio::io_context& io_context) + : socket_(io_context) + { + } + + void handle_write(const boost::system::error_code& /*error*/, + size_t /*bytes_transferred*/) + { + } + + tcp::socket socket_; + std::string message_; +}; + +class tcp_server +{ +public: + tcp_server(boost::asio::io_context& io_context) + : io_context_(io_context), + acceptor_(io_context, tcp::endpoint(tcp::v4(), 13)) + { + start_accept(); + } + +private: + void start_accept() + { + tcp_connection::pointer new_connection = + tcp_connection::create(io_context_); + + acceptor_.async_accept(new_connection->socket(), + boost::bind(&tcp_server::handle_accept, this, new_connection, + boost::asio::placeholders::error)); + } + + void handle_accept(tcp_connection::pointer new_connection, + const boost::system::error_code& error) + { + if (!error) + { + new_connection->start(); + } + + start_accept(); + } + + boost::asio::io_context& io_context_; + tcp::acceptor acceptor_; +}; + +int main() +{ + try + { + boost::asio::io_context io_context; + tcp_server server(io_context); + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/tutorial/daytime4/client.cpp b/src/boost/libs/asio/example/cpp03/tutorial/daytime4/client.cpp new file mode 100644 index 000000000..df3092da6 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/daytime4/client.cpp @@ -0,0 +1,52 @@ +// +// client.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <boost/array.hpp> +#include <boost/asio.hpp> + +using boost::asio::ip::udp; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: client <host>" << std::endl; + return 1; + } + + boost::asio::io_context io_context; + + udp::resolver resolver(io_context); + udp::endpoint receiver_endpoint = + *resolver.resolve(udp::v4(), argv[1], "daytime").begin(); + + udp::socket socket(io_context); + socket.open(udp::v4()); + + boost::array<char, 1> send_buf = {{ 0 }}; + socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint); + + boost::array<char, 128> recv_buf; + udp::endpoint sender_endpoint; + size_t len = socket.receive_from( + boost::asio::buffer(recv_buf), sender_endpoint); + + std::cout.write(recv_buf.data(), len); + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/tutorial/daytime5/server.cpp b/src/boost/libs/asio/example/cpp03/tutorial/daytime5/server.cpp new file mode 100644 index 000000000..316a72923 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/daytime5/server.cpp @@ -0,0 +1,53 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <ctime> +#include <iostream> +#include <string> +#include <boost/array.hpp> +#include <boost/asio.hpp> + +using boost::asio::ip::udp; + +std::string make_daytime_string() +{ + using namespace std; // For time_t, time and ctime; + time_t now = time(0); + return ctime(&now); +} + +int main() +{ + try + { + boost::asio::io_context io_context; + + udp::socket socket(io_context, udp::endpoint(udp::v4(), 13)); + + for (;;) + { + boost::array<char, 1> recv_buf; + udp::endpoint remote_endpoint; + socket.receive_from(boost::asio::buffer(recv_buf), remote_endpoint); + + std::string message = make_daytime_string(); + + boost::system::error_code ignored_error; + socket.send_to(boost::asio::buffer(message), + remote_endpoint, 0, ignored_error); + } + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/tutorial/daytime6/server.cpp b/src/boost/libs/asio/example/cpp03/tutorial/daytime6/server.cpp new file mode 100644 index 000000000..df1f2fa47 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/daytime6/server.cpp @@ -0,0 +1,89 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <ctime> +#include <iostream> +#include <string> +#include <boost/array.hpp> +#include <boost/bind/bind.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/asio.hpp> + +using boost::asio::ip::udp; + +std::string make_daytime_string() +{ + using namespace std; // For time_t, time and ctime; + time_t now = time(0); + return ctime(&now); +} + +class udp_server +{ +public: + udp_server(boost::asio::io_context& io_context) + : socket_(io_context, udp::endpoint(udp::v4(), 13)) + { + start_receive(); + } + +private: + void start_receive() + { + socket_.async_receive_from( + boost::asio::buffer(recv_buffer_), remote_endpoint_, + boost::bind(&udp_server::handle_receive, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + + void handle_receive(const boost::system::error_code& error, + std::size_t /*bytes_transferred*/) + { + if (!error) + { + boost::shared_ptr<std::string> message( + new std::string(make_daytime_string())); + + socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_, + boost::bind(&udp_server::handle_send, this, message, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + + start_receive(); + } + } + + void handle_send(boost::shared_ptr<std::string> /*message*/, + const boost::system::error_code& /*error*/, + std::size_t /*bytes_transferred*/) + { + } + + udp::socket socket_; + udp::endpoint remote_endpoint_; + boost::array<char, 1> recv_buffer_; +}; + +int main() +{ + try + { + boost::asio::io_context io_context; + udp_server server(io_context); + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/tutorial/daytime7/server.cpp b/src/boost/libs/asio/example/cpp03/tutorial/daytime7/server.cpp new file mode 100644 index 000000000..f52b25754 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/daytime7/server.cpp @@ -0,0 +1,160 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <ctime> +#include <iostream> +#include <string> +#include <boost/array.hpp> +#include <boost/bind/bind.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; +using boost::asio::ip::udp; + +std::string make_daytime_string() +{ + using namespace std; // For time_t, time and ctime; + time_t now = time(0); + return ctime(&now); +} + +class tcp_connection + : public boost::enable_shared_from_this<tcp_connection> +{ +public: + typedef boost::shared_ptr<tcp_connection> pointer; + + static pointer create(boost::asio::io_context& io_context) + { + return pointer(new tcp_connection(io_context)); + } + + tcp::socket& socket() + { + return socket_; + } + + void start() + { + message_ = make_daytime_string(); + + boost::asio::async_write(socket_, boost::asio::buffer(message_), + boost::bind(&tcp_connection::handle_write, shared_from_this())); + } + +private: + tcp_connection(boost::asio::io_context& io_context) + : socket_(io_context) + { + } + + void handle_write() + { + } + + tcp::socket socket_; + std::string message_; +}; + +class tcp_server +{ +public: + tcp_server(boost::asio::io_context& io_context) + : io_context_(io_context), + acceptor_(io_context, tcp::endpoint(tcp::v4(), 13)) + { + start_accept(); + } + +private: + void start_accept() + { + tcp_connection::pointer new_connection = + tcp_connection::create(io_context_); + + acceptor_.async_accept(new_connection->socket(), + boost::bind(&tcp_server::handle_accept, this, new_connection, + boost::asio::placeholders::error)); + } + + void handle_accept(tcp_connection::pointer new_connection, + const boost::system::error_code& error) + { + if (!error) + { + new_connection->start(); + } + + start_accept(); + } + + boost::asio::io_context& io_context_; + tcp::acceptor acceptor_; +}; + +class udp_server +{ +public: + udp_server(boost::asio::io_context& io_context) + : socket_(io_context, udp::endpoint(udp::v4(), 13)) + { + start_receive(); + } + +private: + void start_receive() + { + socket_.async_receive_from( + boost::asio::buffer(recv_buffer_), remote_endpoint_, + boost::bind(&udp_server::handle_receive, this, + boost::asio::placeholders::error)); + } + + void handle_receive(const boost::system::error_code& error) + { + if (!error) + { + boost::shared_ptr<std::string> message( + new std::string(make_daytime_string())); + + socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_, + boost::bind(&udp_server::handle_send, this, message)); + + start_receive(); + } + } + + void handle_send(boost::shared_ptr<std::string> /*message*/) + { + } + + udp::socket socket_; + udp::endpoint remote_endpoint_; + boost::array<char, 1> recv_buffer_; +}; + +int main() +{ + try + { + boost::asio::io_context io_context; + tcp_server server1(io_context); + udp_server server2(io_context); + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/tutorial/daytime_dox.txt b/src/boost/libs/asio/example/cpp03/tutorial/daytime_dox.txt new file mode 100644 index 000000000..5ec49c67c --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/daytime_dox.txt @@ -0,0 +1,500 @@ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +/** +\page tutdaytime1 Daytime.1 - A synchronous TCP daytime client + +This tutorial program shows how to use asio to implement a client application +with TCP. + +\dontinclude daytime1/client.cpp +\skip #include + +We start by including the necessary header files. + +\until asio.hpp + +The purpose of this application is to access a daytime service, +so we need the user to specify the server. + +\until } + +All programs that use asio need to have at least one boost::asio::io_context +object. + +\until boost::asio::io_context + +We need to turn the server name that was specified as a parameter to the +application, into a TCP endpoint. To do this we use an +boost::asio::ip::tcp::resolver object. + +\until tcp::resolver + +A resolver takes a host name and service name and turns them into a list of +endpoints. We perform a resolve call using the name of the server, specified in +<tt>argv[1]</tt>, and the name of the service, in this case <tt>"daytime"</tt>. + +The list of endpoints is returned using an object of type +boost::asio::ip::tcp::resolver::results_type. This object is a range, with begin() and +end() member functions that may be used for iterating over the results. + +\until resolver.resolve + +Now we create and connect the socket. The list of endpoints obtained above may +contain both IPv4 and IPv6 endpoints, so we need to try each of them until we +find one that works. This keeps the client program independent of a specific IP +version. The boost::asio::connect() function does this for us automatically. + +\until boost::asio::connect + +The connection is open. All we need to do now is read the response from the +daytime service. + +We use a <tt>boost::array</tt> to hold the received data. The boost::asio::buffer() +function automatically determines the size of the array to help prevent buffer +overruns. Instead of a <tt>boost::array</tt>, we could have used a <tt>char +[]</tt> or <tt>std::vector</tt>. + +\until read_some + +When the server closes the connection, the boost::asio::ip::tcp::socket::read_some() +function will exit with the boost::asio::error::eof error, which is how we know to +exit the loop. + +\until } + +Finally, handle any exceptions that may have been thrown. + +\until } +\until } + +See the \ref tutdaytime1src "full source listing" \n +Return to the \ref index "tutorial index" \n +Next: \ref tutdaytime2 + +*/ + +/** +\page tutdaytime1src Source listing for Daytime.1 +\include daytime1/client.cpp +Return to \ref tutdaytime1 +*/ + +/** +\page tutdaytime2 Daytime.2 - A synchronous TCP daytime server + +This tutorial program shows how to use asio to implement a server application +with TCP. + +\dontinclude daytime2/server.cpp +\skip #include + +\until using + +We define the function <tt>make_daytime_string()</tt> to create the string to +be sent back to the client. This function will be reused in all of our daytime +server applications. + +\until boost::asio::io_context + +A boost::asio::ip::tcp::acceptor object needs to be created to listen +for new connections. It is initialised to listen on TCP port 13, for IP version 4. + +\until tcp::acceptor + +This is an iterative server, which means that it will handle one +connection at a time. Create a socket that will represent the connection to the +client, and then wait for a connection. + +\until acceptor.accept + +A client is accessing our service. Determine the current time +and transfer this information to the client. + +\until } +\until } + +Finally, handle any exceptions. + +\until } +\until } + +See the \ref tutdaytime2src "full source listing" \n +Return to the \ref index "tutorial index" \n +Previous: \ref tutdaytime1 \n +Next: \ref tutdaytime3 + +*/ + +/** +\page tutdaytime2src Source listing for Daytime.2 +\include daytime2/server.cpp +Return to \ref tutdaytime2 +*/ + +/** +\page tutdaytime3 Daytime.3 - An asynchronous TCP daytime server + +\section tutdaytime3funcmain The main() function + +\dontinclude daytime3/server.cpp +\skip int main() +\until try +\until { + +We need to create a server object to accept incoming client connections. The +boost::asio::io_context object provides I/O services, such as sockets, that the +server object will use. + +\until tcp_server + +Run the boost::asio::io_context object so that it will perform asynchronous operations +on your behalf. + +\until return 0; +\until } + +\section tutdaytime3classtcp_server The tcp_server class + +\dontinclude daytime3/server.cpp +\skip class tcp_server +\until public: + +The constructor initialises an acceptor to listen on TCP port 13. + +\until private: + +The function <tt>start_accept()</tt> creates a socket and initiates an +asynchronous accept operation to wait for a new connection. + +\until } + +The function <tt>handle_accept()</tt> is called when the asynchronous accept +operation initiated by <tt>start_accept()</tt> finishes. It services the client +request, and then calls <tt>start_accept()</tt> to initiate the next accept +operation. + +\until } +\until } + +\section tutdaytime3classtcp_connection The tcp_connection class + +We will use <tt>shared_ptr</tt> and <tt>enable_shared_from_this</tt> because we +want to keep the <tt>tcp_connection</tt> object alive as long as there is an +operation that refers to it. + +\dontinclude daytime3/server.cpp +\skip class tcp_connection +\until shared_ptr +\until } +\until } + +In the function <tt>start()</tt>, we call boost::asio::async_write() to serve the data +to the client. Note that we are using boost::asio::async_write(), rather than +boost::asio::ip::tcp::socket::async_write_some(), to ensure that the entire block of +data is sent. + +\until { + +The data to be sent is stored in the class member <tt>message_</tt> as we need +to keep the data valid until the asynchronous operation is complete. + +\until message_ + +When initiating the asynchronous operation, and if using boost::bind(), you +must specify only the arguments that match the handler's parameter list. In +this program, both of the argument placeholders (boost::asio::placeholders::error and +boost::asio::placeholders::bytes_transferred) could potentially have been removed, +since they are not being used in <tt>handle_write()</tt>. + +\until placeholders::bytes_transferred + +Any further actions for this client connection are now the responsibility of +<tt>handle_write()</tt>. + +\until }; + +\section tutdaytime3remunused Removing unused handler parameters + +You may have noticed that the <tt>error</tt>, and <tt>bytes_transferred</tt> +parameters are not used in the body of the <tt>handle_write()</tt> function. If +parameters are not needed, it is possible to remove them from the function so +that it looks like: + +\code + void handle_write() + { + } +\endcode + +The boost::asio::async_write() call used to initiate the call can then be changed to +just: + +\code + boost::asio::async_write(socket_, boost::asio::buffer(message_), + boost::bind(&tcp_connection::handle_write, shared_from_this())); +\endcode + +See the \ref tutdaytime3src "full source listing" \n +Return to the \ref index "tutorial index" \n +Previous: \ref tutdaytime2 \n +Next: \ref tutdaytime4 + +*/ + +/** +\page tutdaytime3src Source listing for Daytime.3 +\include daytime3/server.cpp +Return to \ref tutdaytime3 +*/ + +/** +\page tutdaytime4 Daytime.4 - A synchronous UDP daytime client + +This tutorial program shows how to use asio to implement a client application +with UDP. + +\dontinclude daytime4/client.cpp +\skip #include +\until using boost::asio::ip::udp; + +The start of the application is essentially the same as for the TCP daytime +client. + +\until boost::asio::io_context + +We use an boost::asio::ip::udp::resolver object to find the correct remote endpoint to +use based on the host and service names. The query is restricted to return only +IPv4 endpoints by the boost::asio::ip::udp::v4() argument. + +\until udp::v4 + +The boost::asio::ip::udp::resolver::resolve() function is guaranteed to return at +least one endpoint in the list if it does not fail. This means it is safe to +dereference the return value directly. + +\until udp::endpoint + +Since UDP is datagram-oriented, we will not be using a stream socket. Create an +boost::asio::ip::udp::socket and initiate contact with the remote endpoint. + +\until receiver_endpoint + +Now we need to be ready to accept whatever the server sends back to us. The +endpoint on our side that receives the server's response will be initialised by +boost::asio::ip::udp::socket::receive_from(). + +\until } + +Finally, handle any exceptions that may have been thrown. + +\until } +\until } +See the \ref tutdaytime4src "full source listing" \n +Return to the \ref index "tutorial index" \n +Previous: \ref tutdaytime3 \n +Next: \ref tutdaytime5 + +*/ + +/** +\page tutdaytime4src Source listing for Daytime.4 +\include daytime4/client.cpp +Return to \ref tutdaytime4 +*/ + +/** +\page tutdaytime5 Daytime.5 - A synchronous UDP daytime server + +This tutorial program shows how to use asio to implement a server application +with UDP. + +\dontinclude daytime5/server.cpp +\skip int main() +\until boost::asio::io_context + +Create an boost::asio::ip::udp::socket object to receive requests on UDP port 13. + +\until udp::socket + +Wait for a client to initiate contact with us. The remote_endpoint object will +be populated by boost::asio::ip::udp::socket::receive_from(). + +\until receive_from + +Determine what we are going to send back to the client. + +\until std::string message + +Send the response to the remote_endpoint. + +\until } +\until } + +Finally, handle any exceptions. + +\until } +\until } + +See the \ref tutdaytime5src "full source listing" \n +Return to the \ref index "tutorial index" \n +Previous: \ref tutdaytime4 \n +Next: \ref tutdaytime6 + +*/ + +/** +\page tutdaytime5src Source listing for Daytime.5 +\include daytime5/server.cpp +Return to \ref tutdaytime5 +*/ + +/** +\page tutdaytime6 Daytime.6 - An asynchronous UDP daytime server + +\section tutdaytime6funcmain The main() function + +\dontinclude daytime6/server.cpp +\skip int main() +\until try +\until { + +Create a server object to accept incoming client requests, and run +the boost::asio::io_context object. + +\until return 0; +\until } + +\section tutdaytime6classudp_server The udp_server class + +\dontinclude daytime6/server.cpp +\skip class udp_server +\until public: + +The constructor initialises a socket to listen on UDP port 13. + +\until private: +\until { + +The function boost::asio::ip::udp::socket::async_receive_from() will cause the +application to listen in the background for a new request. When such a request +is received, the boost::asio::io_context object will invoke the +<tt>handle_receive()</tt> function with two arguments: a value of type +boost::system::error_code indicating whether the operation succeeded or failed, and a +<tt>size_t</tt> value <tt>bytes_transferred</tt> specifying the number of bytes +received. + +\until } + +The function <tt>handle_receive()</tt> will service the client request. + +\until { + +The <tt>error</tt> parameter contains the result of the asynchronous operation. +Since we only provide the 1-byte <tt>recv_buffer_</tt> to contain the client's +request, the boost::asio::io_context object would return an error if the client sent +anything larger. We can ignore such an error if it comes up. + +\until { + +Determine what we are going to send. + +\until make_daytime_string() + +We now call boost::asio::ip::udp::socket::async_send_to() to serve the data to the +client. + +\until boost::asio::placeholders::bytes_transferred + +When initiating the asynchronous operation, and if using boost::bind(), you +must specify only the arguments that match the handler's parameter list. In +this program, both of the argument placeholders (boost::asio::placeholders::error and +boost::asio::placeholders::bytes_transferred) could potentially have been removed. + +Start listening for the next client request. + +\until start_receive + +Any further actions for this client request are now the responsibility of +<tt>handle_send()</tt>. + +\until } +\until } + +The function <tt>handle_send()</tt> is invoked after the service request has +been completed. + +\until } +\until } + +See the \ref tutdaytime6src "full source listing" \n +Return to the \ref index "tutorial index" \n +Previous: \ref tutdaytime5 \n +Next: \ref tutdaytime7 + +*/ + +/** +\page tutdaytime6src Source listing for Daytime.6 +\include daytime6/server.cpp +Return to \ref tutdaytime6 +*/ + +/** +\page tutdaytime7 Daytime.7 - A combined TCP/UDP asynchronous server + +This tutorial program shows how to combine the two asynchronous servers that we +have just written, into a single server application. + +\section tutdaytime7funcmain The main() function + +\dontinclude daytime7/server.cpp +\skip int main() +\until boost::asio::io_context + +We will begin by creating a server object to accept a TCP client connection. + +\until tcp_server + +We also need a server object to accept a UDP client request. + +\until udp_server + +We have created two lots of work for the boost::asio::io_context object to do. + +\until return 0; +\until } + +\section tutdaytime7classtcp The tcp_connection and tcp_server classes + +The following two classes are taken from \ref tutdaytime3 "Daytime.3". + +\dontinclude daytime7/server.cpp +\skip class tcp_connection +\until }; +\until }; + +\section tutdaytime7classudp The udp_server class + +Similarly, this next class is taken from the +\ref tutdaytime6 "previous tutorial step". + +\dontinclude daytime7/server.cpp +\skip class udp_server +\until }; + +See the \ref tutdaytime7src "full source listing" \n +Return to the \ref index "tutorial index" \n +Previous: \ref tutdaytime6 + +*/ + +/** +\page tutdaytime7src Source listing for Daytime.7 +\include daytime7/server.cpp +Return to \ref tutdaytime7 +*/ diff --git a/src/boost/libs/asio/example/cpp03/tutorial/index_dox.txt b/src/boost/libs/asio/example/cpp03/tutorial/index_dox.txt new file mode 100644 index 000000000..a57c0f2d3 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/index_dox.txt @@ -0,0 +1,48 @@ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +/** +\mainpage asio Tutorial + +\section tuttimer Basic Skills + +The tutorial programs in this first section introduce the fundamental concepts +required to use the asio toolkit. Before plunging into the complex world of +network programming, these tutorial programs illustrate the basic skills using +simple asynchronous timers. + +\li \ref tuttimer1 +\li \ref tuttimer2 +\li \ref tuttimer3 +\li \ref tuttimer4 +\li \ref tuttimer5 + +\section tutdaytime Introduction to Sockets + +The tutorial programs in this section show how to use asio to develop simple +client and server programs. These tutorial programs are based around the <a +href="http://www.ietf.org/rfc/rfc867.txt">daytime</a> protocol, which supports +both TCP and UDP. + +The first three tutorial programs implement the daytime protocol using TCP. + +\li \ref tutdaytime1 +\li \ref tutdaytime2 +\li \ref tutdaytime3 + +The next three tutorial programs implement the daytime protocol using UDP. + +\li \ref tutdaytime4 +\li \ref tutdaytime5 +\li \ref tutdaytime6 + +The last tutorial program in this section demonstrates how asio allows the TCP +and UDP servers to be easily combined into a single program. + +\li \ref tutdaytime7 + +*/ diff --git a/src/boost/libs/asio/example/cpp03/tutorial/timer1/timer.cpp b/src/boost/libs/asio/example/cpp03/tutorial/timer1/timer.cpp new file mode 100644 index 000000000..77906d15a --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/timer1/timer.cpp @@ -0,0 +1,24 @@ +// +// timer.cpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <boost/asio.hpp> + +int main() +{ + boost::asio::io_context io; + + boost::asio::steady_timer t(io, boost::asio::chrono::seconds(5)); + t.wait(); + + std::cout << "Hello, world!" << std::endl; + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/tutorial/timer2/timer.cpp b/src/boost/libs/asio/example/cpp03/tutorial/timer2/timer.cpp new file mode 100644 index 000000000..030910b51 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/timer2/timer.cpp @@ -0,0 +1,29 @@ +// +// timer.cpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <boost/asio.hpp> + +void print(const boost::system::error_code& /*e*/) +{ + std::cout << "Hello, world!" << std::endl; +} + +int main() +{ + boost::asio::io_context io; + + boost::asio::steady_timer t(io, boost::asio::chrono::seconds(5)); + t.async_wait(&print); + + io.run(); + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/tutorial/timer3/timer.cpp b/src/boost/libs/asio/example/cpp03/tutorial/timer3/timer.cpp new file mode 100644 index 000000000..44c9044de --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/timer3/timer.cpp @@ -0,0 +1,43 @@ +// +// timer.cpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <boost/asio.hpp> +#include <boost/bind/bind.hpp> + +void print(const boost::system::error_code& /*e*/, + boost::asio::steady_timer* t, int* count) +{ + if (*count < 5) + { + std::cout << *count << std::endl; + ++(*count); + + t->expires_at(t->expiry() + boost::asio::chrono::seconds(1)); + t->async_wait(boost::bind(print, + boost::asio::placeholders::error, t, count)); + } +} + +int main() +{ + boost::asio::io_context io; + + int count = 0; + boost::asio::steady_timer t(io, boost::asio::chrono::seconds(1)); + t.async_wait(boost::bind(print, + boost::asio::placeholders::error, &t, &count)); + + io.run(); + + std::cout << "Final count is " << count << std::endl; + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/tutorial/timer4/timer.cpp b/src/boost/libs/asio/example/cpp03/tutorial/timer4/timer.cpp new file mode 100644 index 000000000..afcdcb396 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/timer4/timer.cpp @@ -0,0 +1,54 @@ +// +// timer.cpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <boost/asio.hpp> +#include <boost/bind/bind.hpp> + +class printer +{ +public: + printer(boost::asio::io_context& io) + : timer_(io, boost::asio::chrono::seconds(1)), + count_(0) + { + timer_.async_wait(boost::bind(&printer::print, this)); + } + + ~printer() + { + std::cout << "Final count is " << count_ << std::endl; + } + + void print() + { + if (count_ < 5) + { + std::cout << count_ << std::endl; + ++count_; + + timer_.expires_at(timer_.expiry() + boost::asio::chrono::seconds(1)); + timer_.async_wait(boost::bind(&printer::print, this)); + } + } + +private: + boost::asio::steady_timer timer_; + int count_; +}; + +int main() +{ + boost::asio::io_context io; + printer p(io); + io.run(); + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/tutorial/timer5/timer.cpp b/src/boost/libs/asio/example/cpp03/tutorial/timer5/timer.cpp new file mode 100644 index 000000000..b5d48f8e2 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/timer5/timer.cpp @@ -0,0 +1,81 @@ +// +// timer.cpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <boost/asio.hpp> +#include <boost/thread/thread.hpp> +#include <boost/bind/bind.hpp> + +class printer +{ +public: + printer(boost::asio::io_context& io) + : strand_(boost::asio::make_strand(io)), + timer1_(io, boost::asio::chrono::seconds(1)), + timer2_(io, boost::asio::chrono::seconds(1)), + count_(0) + { + timer1_.async_wait(boost::asio::bind_executor(strand_, + boost::bind(&printer::print1, this))); + + timer2_.async_wait(boost::asio::bind_executor(strand_, + boost::bind(&printer::print2, this))); + } + + ~printer() + { + std::cout << "Final count is " << count_ << std::endl; + } + + void print1() + { + if (count_ < 10) + { + std::cout << "Timer 1: " << count_ << std::endl; + ++count_; + + timer1_.expires_at(timer1_.expiry() + boost::asio::chrono::seconds(1)); + + timer1_.async_wait(boost::asio::bind_executor(strand_, + boost::bind(&printer::print1, this))); + } + } + + void print2() + { + if (count_ < 10) + { + std::cout << "Timer 2: " << count_ << std::endl; + ++count_; + + timer2_.expires_at(timer2_.expiry() + boost::asio::chrono::seconds(1)); + + timer2_.async_wait(boost::asio::bind_executor(strand_, + boost::bind(&printer::print2, this))); + } + } + +private: + boost::asio::strand<boost::asio::io_context::executor_type> strand_; + boost::asio::steady_timer timer1_; + boost::asio::steady_timer timer2_; + int count_; +}; + +int main() +{ + boost::asio::io_context io; + printer p(io); + boost::thread t(boost::bind(&boost::asio::io_context::run, &io)); + io.run(); + t.join(); + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp03/tutorial/timer_dox.txt b/src/boost/libs/asio/example/cpp03/tutorial/timer_dox.txt new file mode 100644 index 000000000..fdf7fb186 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/tutorial/timer_dox.txt @@ -0,0 +1,378 @@ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +/** +\page tuttimer1 Timer.1 - Using a timer synchronously + +This tutorial program introduces asio by showing how to perform a blocking +wait on a timer. + +\dontinclude timer1/timer.cpp +\skip #include + +We start by including the necessary header files. + +All of the asio classes can be used by simply including the <tt>"asio.hpp"</tt> +header file. + +\until asio.hpp + +All programs that use asio need to have at least one boost::asio::io_context object. +This class provides access to I/O functionality. We declare an object of this +type first thing in the main function. + +\until boost::asio::io_context + +Next we declare an object of type boost::asio::steady_timer. The core asio classes +that provide I/O functionality (or as in this case timer functionality) always +take a reference to an io_context as their first constructor argument. The +second argument to the constructor sets the timer to expire 5 seconds from now. + +\until boost::asio::steady_timer + +In this simple example we perform a blocking wait on the timer. +That is, the call to boost::asio::steady_timer::wait() will not return until the +timer has expired, 5 seconds after it was created (i.e. <b>not</b> from when the +wait starts). + +A timer is always in one of two states: "expired" or "not expired". If the +boost::asio::steady_timer::wait() function is called on an expired timer, it will +return immediately. + +\until wait + +Finally we print the obligatory <tt>"Hello, world!"</tt> +message to show when the timer has expired. + +\until } + +See the \ref tuttimer1src "full source listing" \n +Return to the \ref index "tutorial index" \n +Next: \ref tuttimer2 + +*/ + +/** +\page tuttimer1src Source listing for Timer.1 +\include timer1/timer.cpp +Return to \ref tuttimer1 +*/ + +/** +\page tuttimer2 Timer.2 - Using a timer asynchronously + +This tutorial program demonstrates how to use asio's asynchronous callback +functionality by modifying the program from tutorial Timer.1 to perform an +asynchronous wait on the timer. + +\dontinclude timer2/timer.cpp +\skip #include + +\until asio.hpp + +Using asio's asynchronous functionality means having a callback +function that will be called when an asynchronous operation completes. In this +program we define a function called <tt>print</tt> to be called when the +asynchronous wait finishes. + +\until boost::asio::steady_timer + +Next, instead of doing a blocking wait as in tutorial Timer.1, +we call the boost::asio::steady_timer::async_wait() function to perform an +asynchronous wait. When calling this function we pass the <tt>print</tt> +callback handler that was defined above. + +\skipline async_wait + +Finally, we must call the boost::asio::io_context::run() member function +on the io_context object. + +The asio library provides a guarantee that callback handlers will <b>only</b> +be called from threads that are currently calling boost::asio::io_context::run(). +Therefore unless the boost::asio::io_context::run() function is called the callback for +the asynchronous wait completion will never be invoked. + +The boost::asio::io_context::run() function will also continue to run while there is +still "work" to do. In this example, the work is the asynchronous wait on the +timer, so the call will not return until the timer has expired and the +callback has completed. + +It is important to remember to give the io_context some work to do before +calling boost::asio::io_context::run(). For example, if we had omitted the above call +to boost::asio::steady_timer::async_wait(), the io_context would not have had any +work to do, and consequently boost::asio::io_context::run() would have returned +immediately. + +\skip run +\until } + +See the \ref tuttimer2src "full source listing" \n +Return to the \ref index "tutorial index" \n +Previous: \ref tuttimer1 \n +Next: \ref tuttimer3 + +*/ + +/** +\page tuttimer2src Source listing for Timer.2 +\include timer2/timer.cpp +Return to \ref tuttimer2 +*/ + +/** +\page tuttimer3 Timer.3 - Binding arguments to a handler + +In this tutorial we will modify the program from tutorial Timer.2 so that the +timer fires once a second. This will show how to pass additional parameters to +your handler function. + +\dontinclude timer3/timer.cpp +\skip #include + +\until bind.hpp + +To implement a repeating timer using asio you need to change +the timer's expiry time in your callback function, and to then start a new +asynchronous wait. Obviously this means that the callback function will need +to be able to access the timer object. To this end we add two new parameters +to the <tt>print</tt> function: + +\li A pointer to a timer object. + +\li A counter so that we can stop the program when the timer fires for the +sixth time. + +\until { + +As mentioned above, this tutorial program uses a counter to +stop running when the timer fires for the sixth time. However you will observe +that there is no explicit call to ask the io_context to stop. Recall that in +tutorial Timer.2 we learnt that the boost::asio::io_context::run() function completes +when there is no more "work" to do. By not starting a new asynchronous wait on +the timer when <tt>count</tt> reaches 5, the io_context will run out of work and +stop running. + +\until ++ + +Next we move the expiry time for the timer along by one second +from the previous expiry time. By calculating the new expiry time relative to +the old, we can ensure that the timer does not drift away from the +whole-second mark due to any delays in processing the handler. + +\until expires_at + +Then we start a new asynchronous wait on the timer. As you can +see, the boost::bind() function is used to associate the extra parameters +with your callback handler. The boost::asio::steady_timer::async_wait() function +expects a handler function (or function object) with the signature +<tt>void(const boost::system::error_code&)</tt>. Binding the additional parameters +converts your <tt>print</tt> function into a function object that matches the +signature correctly. + +See the <a href="http://www.boost.org/libs/bind/bind.html">Boost.Bind +documentation</a> for more information on how to use boost::bind(). + +In this example, the boost::asio::placeholders::error argument to boost::bind() is a +named placeholder for the error object passed to the handler. When initiating +the asynchronous operation, and if using boost::bind(), you must specify only +the arguments that match the handler's parameter list. In tutorial Timer.4 you +will see that this placeholder may be elided if the parameter is not needed by +the callback handler. + +\until boost::asio::io_context + +A new <tt>count</tt> variable is added so that we can stop the +program when the timer fires for the sixth time. + +\until boost::asio::steady_timer + +As in Step 4, when making the call to +boost::asio::steady_timer::async_wait() from <tt>main</tt> we bind the additional +parameters needed for the <tt>print</tt> function. + +\until run + +Finally, just to prove that the <tt>count</tt> variable was +being used in the <tt>print</tt> handler function, we will print out its new +value. + +\until } + +See the \ref tuttimer3src "full source listing" \n +Return to the \ref index "tutorial index" \n +Previous: \ref tuttimer2 \n +Next: \ref tuttimer4 + +*/ + +/** +\page tuttimer3src Source listing for Timer.3 +\include timer3/timer.cpp +Return to \ref tuttimer3 +*/ + +/** +\page tuttimer4 Timer.4 - Using a member function as a handler + +In this tutorial we will see how to use a class member function as a callback +handler. The program should execute identically to the tutorial program from +tutorial Timer.3. + +\dontinclude timer4/timer.cpp +\skip #include + +\until bind.hpp + +Instead of defining a free function <tt>print</tt> as the +callback handler, as we did in the earlier tutorial programs, we now define a +class called <tt>printer</tt>. + +\until public + +The constructor of this class will take a reference to the +io_context object and use it when initialising the <tt>timer_</tt> member. The +counter used to shut down the program is now also a member of the class. + +\until { + +The boost::bind() function works just as well with class +member functions as with free functions. Since all non-static class member +functions have an implicit <tt>this</tt> parameter, we need to bind +<tt>this</tt> to the function. As in tutorial Timer.3, boost::bind() +converts our callback handler (now a member function) into a function object +that can be invoked as though it has the signature <tt>void(const +boost::system::error_code&)</tt>. + +You will note that the boost::asio::placeholders::error placeholder is not specified +here, as the <tt>print</tt> member function does not accept an error object as +a parameter. + +\until } + +In the class destructor we will print out the final value of +the counter. + +\until } + +The <tt>print</tt> member function is very similar to the +<tt>print</tt> function from tutorial Timer.3, except that it now operates on +the class data members instead of having the timer and counter passed in as +parameters. + +\until }; + +The <tt>main</tt> function is much simpler than before, as it +now declares a local <tt>printer</tt> object before running the io_context as +normal. + +\until } + +See the \ref tuttimer4src "full source listing" \n +Return to the \ref index "tutorial index" \n +Previous: \ref tuttimer3 \n +Next: \ref tuttimer5 \n + +*/ + +/** +\page tuttimer4src Source listing for Timer.4 +\include timer4/timer.cpp +Return to \ref tuttimer4 +*/ + +/** +\page tuttimer5 Timer.5 - Synchronising handlers in multithreaded programs + +This tutorial demonstrates the use of the boost::asio::strand class template to +synchronise callback handlers in a multithreaded program. + +The previous four tutorials avoided the issue of handler synchronisation by +calling the boost::asio::io_context::run() function from one thread only. As you +already know, the asio library provides a guarantee that callback handlers will +<b>only</b> be called from threads that are currently calling +boost::asio::io_context::run(). Consequently, calling boost::asio::io_context::run() from +only one thread ensures that callback handlers cannot run concurrently. + +The single threaded approach is usually the best place to start when +developing applications using asio. The downside is the limitations it places +on programs, particularly servers, including: + +<ul> +<li>Poor responsiveness when handlers can take a long time to complete.</li> +<li>An inability to scale on multiprocessor systems.</li> +</ul> + +If you find yourself running into these limitations, an alternative approach +is to have a pool of threads calling boost::asio::io_context::run(). However, as this +allows handlers to execute concurrently, we need a method of synchronisation +when handlers might be accessing a shared, thread-unsafe resource. + +\dontinclude timer5/timer.cpp +\skip #include + +\until bind.hpp + +We start by defining a class called <tt>printer</tt>, similar +to the class in the previous tutorial. This class will extend the previous +tutorial by running two timers in parallel. + +\until public + +In addition to initialising a pair of boost::asio::steady_timer members, the +constructor initialises the <tt>strand_</tt> member, an object of type +boost::asio::strand<boost::asio::io_context::executor_type>. + +The boost::asio::strand class template is an executor adapter that guarantees +that, for those handlers that are dispatched through it, an executing handler +will be allowed to complete before the next one is started. This is guaranteed +irrespective of the number of threads that are calling +boost::asio::io_context::run(). Of course, the handlers may still execute +concurrently with other handlers that were <b>not</b> dispatched through an +boost::asio::strand, or were dispatched through a different boost::asio::strand +object. + +\until { + +When initiating the asynchronous operations, each callback handler is "bound" +to an boost::asio::strand<boost::asio::io_context::executor_type> object. The +boost::asio::bind_executor() function returns a new handler that automatically +dispatches its contained handler through the boost::asio::strand object. By +binding the handlers to the same boost::asio::strand, we are ensuring that they +cannot execute concurrently. + +\until } +\until } + +In a multithreaded program, the handlers for asynchronous +operations should be synchronised if they access shared resources. In this +tutorial, the shared resources used by the handlers (<tt>print1</tt> and +<tt>print2</tt>) are <tt>std::cout</tt> and the <tt>count_</tt> data member. + +\until }; + +The <tt>main</tt> function now causes boost::asio::io_context::run() to +be called from two threads: the main thread and one additional thread. This is +accomplished using an boost::thread object. + +Just as it would with a call from a single thread, concurrent calls to +boost::asio::io_context::run() will continue to execute while there is "work" left to +do. The background thread will not exit until all asynchronous operations have +completed. + +\until } + +See the \ref tuttimer5src "full source listing" \n +Return to the \ref index "tutorial index" \n +Previous: \ref tuttimer4 \n + +*/ + +/** +\page tuttimer5src Source listing for Timer.5 +\include timer5/timer.cpp +Return to \ref tuttimer5 +*/ diff --git a/src/boost/libs/asio/example/cpp03/windows/Jamfile.v2 b/src/boost/libs/asio/example/cpp03/windows/Jamfile.v2 new file mode 100644 index 000000000..c34228303 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/windows/Jamfile.v2 @@ -0,0 +1,30 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe transmit_file + : transmit_file.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp03/windows/transmit_file.cpp b/src/boost/libs/asio/example/cpp03/windows/transmit_file.cpp new file mode 100644 index 000000000..a544eb904 --- /dev/null +++ b/src/boost/libs/asio/example/cpp03/windows/transmit_file.cpp @@ -0,0 +1,177 @@ +// +// transmit_file.cpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <ctime> +#include <iostream> +#include <string> +#include <boost/bind/bind.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/asio.hpp> + +#if defined(BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR) + +using boost::asio::ip::tcp; +using boost::asio::windows::overlapped_ptr; +using boost::asio::windows::random_access_handle; + +typedef boost::asio::basic_stream_socket<tcp, + boost::asio::io_context::executor_type> tcp_socket; + +typedef boost::asio::basic_socket_acceptor<tcp, + boost::asio::io_context::executor_type> tcp_acceptor; + +// A wrapper for the TransmitFile overlapped I/O operation. +template <typename Handler> +void transmit_file(tcp_socket& socket, + random_access_handle& file, Handler handler) +{ + // Construct an OVERLAPPED-derived object to contain the handler. + overlapped_ptr overlapped(socket.get_executor().context(), handler); + + // Initiate the TransmitFile operation. + BOOL ok = ::TransmitFile(socket.native_handle(), + file.native_handle(), 0, 0, overlapped.get(), 0, 0); + DWORD last_error = ::GetLastError(); + + // Check if the operation completed immediately. + if (!ok && last_error != ERROR_IO_PENDING) + { + // The operation completed immediately, so a completion notification needs + // to be posted. When complete() is called, ownership of the OVERLAPPED- + // derived object passes to the io_context. + boost::system::error_code ec(last_error, + boost::asio::error::get_system_category()); + overlapped.complete(ec, 0); + } + else + { + // The operation was successfully initiated, so ownership of the + // OVERLAPPED-derived object has passed to the io_context. + overlapped.release(); + } +} + +class connection + : public boost::enable_shared_from_this<connection> +{ +public: + typedef boost::shared_ptr<connection> pointer; + + static pointer create(boost::asio::io_context& io_context, + const std::string& filename) + { + return pointer(new connection(io_context, filename)); + } + + tcp_socket& socket() + { + return socket_; + } + + void start() + { + boost::system::error_code ec; + file_.assign(::CreateFile(filename_.c_str(), GENERIC_READ, 0, 0, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0), ec); + if (file_.is_open()) + { + transmit_file(socket_, file_, + boost::bind(&connection::handle_write, shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + } + +private: + connection(boost::asio::io_context& io_context, const std::string& filename) + : socket_(io_context), + filename_(filename), + file_(io_context) + { + } + + void handle_write(const boost::system::error_code& /*error*/, + size_t /*bytes_transferred*/) + { + boost::system::error_code ignored_ec; + socket_.shutdown(tcp_socket::shutdown_both, ignored_ec); + } + + tcp_socket socket_; + std::string filename_; + random_access_handle file_; +}; + +class server +{ +public: + server(boost::asio::io_context& io_context, + unsigned short port, const std::string& filename) + : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)), + filename_(filename) + { + start_accept(); + } + +private: + void start_accept() + { + connection::pointer new_connection = + connection::create(acceptor_.get_executor().context(), filename_); + + acceptor_.async_accept(new_connection->socket(), + boost::bind(&server::handle_accept, this, new_connection, + boost::asio::placeholders::error)); + } + + void handle_accept(connection::pointer new_connection, + const boost::system::error_code& error) + { + if (!error) + { + new_connection->start(); + } + + start_accept(); + } + + tcp_acceptor acceptor_; + std::string filename_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: transmit_file <port> <filename>\n"; + return 1; + } + + boost::asio::io_context io_context; + + using namespace std; // For atoi. + server s(io_context, atoi(argv[1]), argv[2]); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + } + + return 0; +} + +#else // defined(BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR) +# error Overlapped I/O not available on this platform +#endif // defined(BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR) diff --git a/src/boost/libs/asio/example/cpp11/allocation/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/allocation/Jamfile.v2 new file mode 100644 index 000000000..4ff960f4c --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/allocation/Jamfile.v2 @@ -0,0 +1,29 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe server + : server.cpp + /boost/system//boost_system + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp11/allocation/server.cpp b/src/boost/libs/asio/example/cpp11/allocation/server.cpp new file mode 100644 index 000000000..0edb03190 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/allocation/server.cpp @@ -0,0 +1,255 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <array> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <type_traits> +#include <utility> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +// Class to manage the memory to be used for handler-based custom allocation. +// It contains a single block of memory which may be returned for allocation +// requests. If the memory is in use when an allocation request is made, the +// allocator delegates allocation to the global heap. +class handler_memory +{ +public: + handler_memory() + : in_use_(false) + { + } + + handler_memory(const handler_memory&) = delete; + handler_memory& operator=(const handler_memory&) = delete; + + void* allocate(std::size_t size) + { + if (!in_use_ && size < sizeof(storage_)) + { + in_use_ = true; + return &storage_; + } + else + { + return ::operator new(size); + } + } + + void deallocate(void* pointer) + { + if (pointer == &storage_) + { + in_use_ = false; + } + else + { + ::operator delete(pointer); + } + } + +private: + // Storage space used for handler-based custom memory allocation. + typename std::aligned_storage<1024>::type storage_; + + // Whether the handler-based custom allocation storage has been used. + bool in_use_; +}; + +// The allocator to be associated with the handler objects. This allocator only +// needs to satisfy the C++11 minimal allocator requirements. +template <typename T> +class handler_allocator +{ +public: + using value_type = T; + + explicit handler_allocator(handler_memory& mem) + : memory_(mem) + { + } + + template <typename U> + handler_allocator(const handler_allocator<U>& other) noexcept + : memory_(other.memory_) + { + } + + bool operator==(const handler_allocator& other) const noexcept + { + return &memory_ == &other.memory_; + } + + bool operator!=(const handler_allocator& other) const noexcept + { + return &memory_ != &other.memory_; + } + + T* allocate(std::size_t n) const + { + return static_cast<T*>(memory_.allocate(sizeof(T) * n)); + } + + void deallocate(T* p, std::size_t /*n*/) const + { + return memory_.deallocate(p); + } + +private: + template <typename> friend class handler_allocator; + + // The underlying memory. + handler_memory& memory_; +}; + +// Wrapper class template for handler objects to allow handler memory +// allocation to be customised. The allocator_type type and get_allocator() +// member function are used by the asynchronous operations to obtain the +// allocator. Calls to operator() are forwarded to the encapsulated handler. +template <typename Handler> +class custom_alloc_handler +{ +public: + using allocator_type = handler_allocator<Handler>; + + custom_alloc_handler(handler_memory& m, Handler h) + : memory_(m), + handler_(h) + { + } + + allocator_type get_allocator() const noexcept + { + return allocator_type(memory_); + } + + template <typename ...Args> + void operator()(Args&&... args) + { + handler_(std::forward<Args>(args)...); + } + +private: + handler_memory& memory_; + Handler handler_; +}; + +// Helper function to wrap a handler object to add custom allocation. +template <typename Handler> +inline custom_alloc_handler<Handler> make_custom_alloc_handler( + handler_memory& m, Handler h) +{ + return custom_alloc_handler<Handler>(m, h); +} + +class session + : public std::enable_shared_from_this<session> +{ +public: + session(tcp::socket socket) + : socket_(std::move(socket)) + { + } + + void start() + { + do_read(); + } + +private: + void do_read() + { + auto self(shared_from_this()); + socket_.async_read_some(boost::asio::buffer(data_), + make_custom_alloc_handler(handler_memory_, + [this, self](boost::system::error_code ec, std::size_t length) + { + if (!ec) + { + do_write(length); + } + })); + } + + void do_write(std::size_t length) + { + auto self(shared_from_this()); + boost::asio::async_write(socket_, boost::asio::buffer(data_, length), + make_custom_alloc_handler(handler_memory_, + [this, self](boost::system::error_code ec, std::size_t /*length*/) + { + if (!ec) + { + do_read(); + } + })); + } + + // The socket used to communicate with the client. + tcp::socket socket_; + + // Buffer used to store data received from the client. + std::array<char, 1024> data_; + + // The memory to use for handler-based custom memory allocation. + handler_memory handler_memory_; +}; + +class server +{ +public: + server(boost::asio::io_context& io_context, short port) + : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) + { + do_accept(); + } + +private: + void do_accept() + { + acceptor_.async_accept( + [this](boost::system::error_code ec, tcp::socket socket) + { + if (!ec) + { + std::make_shared<session>(std::move(socket))->start(); + } + + do_accept(); + }); + } + + tcp::acceptor acceptor_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + server s(io_context, std::atoi(argv[1])); + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/buffers/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/buffers/Jamfile.v2 new file mode 100644 index 000000000..f04d22493 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/buffers/Jamfile.v2 @@ -0,0 +1,29 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe server + : reference_counted.cpp + /boost/system//boost_system + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp11/buffers/reference_counted.cpp b/src/boost/libs/asio/example/cpp11/buffers/reference_counted.cpp new file mode 100644 index 000000000..7ee99d349 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/buffers/reference_counted.cpp @@ -0,0 +1,122 @@ +// +// reference_counted.cpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio.hpp> +#include <iostream> +#include <memory> +#include <utility> +#include <vector> +#include <ctime> + +using boost::asio::ip::tcp; + +// A reference-counted non-modifiable buffer class. +class shared_const_buffer +{ +public: + // Construct from a std::string. + explicit shared_const_buffer(const std::string& data) + : data_(new std::vector<char>(data.begin(), data.end())), + buffer_(boost::asio::buffer(*data_)) + { + } + + // Implement the ConstBufferSequence requirements. + typedef boost::asio::const_buffer value_type; + typedef const boost::asio::const_buffer* const_iterator; + const boost::asio::const_buffer* begin() const { return &buffer_; } + const boost::asio::const_buffer* end() const { return &buffer_ + 1; } + +private: + std::shared_ptr<std::vector<char> > data_; + boost::asio::const_buffer buffer_; +}; + +class session + : public std::enable_shared_from_this<session> +{ +public: + session(tcp::socket socket) + : socket_(std::move(socket)) + { + } + + void start() + { + do_write(); + } + +private: + void do_write() + { + std::time_t now = std::time(0); + shared_const_buffer buffer(std::ctime(&now)); + + auto self(shared_from_this()); + boost::asio::async_write(socket_, buffer, + [self](boost::system::error_code /*ec*/, std::size_t /*length*/) + { + }); + } + + // The socket used to communicate with the client. + tcp::socket socket_; +}; + +class server +{ +public: + server(boost::asio::io_context& io_context, short port) + : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) + { + do_accept(); + } + +private: + void do_accept() + { + acceptor_.async_accept( + [this](boost::system::error_code ec, tcp::socket socket) + { + if (!ec) + { + std::make_shared<session>(std::move(socket))->start(); + } + + do_accept(); + }); + } + + tcp::acceptor acceptor_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: reference_counted <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + server s(io_context, std::atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/chat/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/chat/Jamfile.v2 new file mode 100644 index 000000000..1908cf7ce --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/chat/Jamfile.v2 @@ -0,0 +1,33 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/thread//boost_thread + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe chat_server : chat_server.cpp ; +exe chat_client : chat_client.cpp ; diff --git a/src/boost/libs/asio/example/cpp11/chat/chat_client.cpp b/src/boost/libs/asio/example/cpp11/chat/chat_client.cpp new file mode 100644 index 000000000..c44e04fa4 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/chat/chat_client.cpp @@ -0,0 +1,167 @@ +// +// chat_client.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <deque> +#include <iostream> +#include <thread> +#include <boost/asio.hpp> +#include "chat_message.hpp" + +using boost::asio::ip::tcp; + +typedef std::deque<chat_message> chat_message_queue; + +class chat_client +{ +public: + chat_client(boost::asio::io_context& io_context, + const tcp::resolver::results_type& endpoints) + : io_context_(io_context), + socket_(io_context) + { + do_connect(endpoints); + } + + void write(const chat_message& msg) + { + boost::asio::post(io_context_, + [this, msg]() + { + bool write_in_progress = !write_msgs_.empty(); + write_msgs_.push_back(msg); + if (!write_in_progress) + { + do_write(); + } + }); + } + + void close() + { + boost::asio::post(io_context_, [this]() { socket_.close(); }); + } + +private: + void do_connect(const tcp::resolver::results_type& endpoints) + { + boost::asio::async_connect(socket_, endpoints, + [this](boost::system::error_code ec, tcp::endpoint) + { + if (!ec) + { + do_read_header(); + } + }); + } + + void do_read_header() + { + boost::asio::async_read(socket_, + boost::asio::buffer(read_msg_.data(), chat_message::header_length), + [this](boost::system::error_code ec, std::size_t /*length*/) + { + if (!ec && read_msg_.decode_header()) + { + do_read_body(); + } + else + { + socket_.close(); + } + }); + } + + void do_read_body() + { + boost::asio::async_read(socket_, + boost::asio::buffer(read_msg_.body(), read_msg_.body_length()), + [this](boost::system::error_code ec, std::size_t /*length*/) + { + if (!ec) + { + std::cout.write(read_msg_.body(), read_msg_.body_length()); + std::cout << "\n"; + do_read_header(); + } + else + { + socket_.close(); + } + }); + } + + void do_write() + { + boost::asio::async_write(socket_, + boost::asio::buffer(write_msgs_.front().data(), + write_msgs_.front().length()), + [this](boost::system::error_code ec, std::size_t /*length*/) + { + if (!ec) + { + write_msgs_.pop_front(); + if (!write_msgs_.empty()) + { + do_write(); + } + } + else + { + socket_.close(); + } + }); + } + +private: + boost::asio::io_context& io_context_; + tcp::socket socket_; + chat_message read_msg_; + chat_message_queue write_msgs_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: chat_client <host> <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + tcp::resolver resolver(io_context); + auto endpoints = resolver.resolve(argv[1], argv[2]); + chat_client c(io_context, endpoints); + + std::thread t([&io_context](){ io_context.run(); }); + + char line[chat_message::max_body_length + 1]; + while (std::cin.getline(line, chat_message::max_body_length + 1)) + { + chat_message msg; + msg.body_length(std::strlen(line)); + std::memcpy(msg.body(), line, msg.body_length()); + msg.encode_header(); + c.write(msg); + } + + c.close(); + t.join(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/chat/chat_message.hpp b/src/boost/libs/asio/example/cpp11/chat/chat_message.hpp new file mode 100644 index 000000000..9507f6ae9 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/chat/chat_message.hpp @@ -0,0 +1,91 @@ +// +// chat_message.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef CHAT_MESSAGE_HPP +#define CHAT_MESSAGE_HPP + +#include <cstdio> +#include <cstdlib> +#include <cstring> + +class chat_message +{ +public: + enum { header_length = 4 }; + enum { max_body_length = 512 }; + + chat_message() + : body_length_(0) + { + } + + const char* data() const + { + return data_; + } + + char* data() + { + return data_; + } + + std::size_t length() const + { + return header_length + body_length_; + } + + const char* body() const + { + return data_ + header_length; + } + + char* body() + { + return data_ + header_length; + } + + std::size_t body_length() const + { + return body_length_; + } + + void body_length(std::size_t new_length) + { + body_length_ = new_length; + if (body_length_ > max_body_length) + body_length_ = max_body_length; + } + + bool decode_header() + { + char header[header_length + 1] = ""; + std::strncat(header, data_, header_length); + body_length_ = std::atoi(header); + if (body_length_ > max_body_length) + { + body_length_ = 0; + return false; + } + return true; + } + + void encode_header() + { + char header[header_length + 1] = ""; + std::sprintf(header, "%4d", static_cast<int>(body_length_)); + std::memcpy(data_, header, header_length); + } + +private: + char data_[header_length + max_body_length]; + std::size_t body_length_; +}; + +#endif // CHAT_MESSAGE_HPP diff --git a/src/boost/libs/asio/example/cpp11/chat/chat_server.cpp b/src/boost/libs/asio/example/cpp11/chat/chat_server.cpp new file mode 100644 index 000000000..a96af8628 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/chat/chat_server.cpp @@ -0,0 +1,227 @@ +// +// chat_server.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <deque> +#include <iostream> +#include <list> +#include <memory> +#include <set> +#include <utility> +#include <boost/asio.hpp> +#include "chat_message.hpp" + +using boost::asio::ip::tcp; + +//---------------------------------------------------------------------- + +typedef std::deque<chat_message> chat_message_queue; + +//---------------------------------------------------------------------- + +class chat_participant +{ +public: + virtual ~chat_participant() {} + virtual void deliver(const chat_message& msg) = 0; +}; + +typedef std::shared_ptr<chat_participant> chat_participant_ptr; + +//---------------------------------------------------------------------- + +class chat_room +{ +public: + void join(chat_participant_ptr participant) + { + participants_.insert(participant); + for (auto msg: recent_msgs_) + participant->deliver(msg); + } + + void leave(chat_participant_ptr participant) + { + participants_.erase(participant); + } + + void deliver(const chat_message& msg) + { + recent_msgs_.push_back(msg); + while (recent_msgs_.size() > max_recent_msgs) + recent_msgs_.pop_front(); + + for (auto participant: participants_) + participant->deliver(msg); + } + +private: + std::set<chat_participant_ptr> participants_; + enum { max_recent_msgs = 100 }; + chat_message_queue recent_msgs_; +}; + +//---------------------------------------------------------------------- + +class chat_session + : public chat_participant, + public std::enable_shared_from_this<chat_session> +{ +public: + chat_session(tcp::socket socket, chat_room& room) + : socket_(std::move(socket)), + room_(room) + { + } + + void start() + { + room_.join(shared_from_this()); + do_read_header(); + } + + void deliver(const chat_message& msg) + { + bool write_in_progress = !write_msgs_.empty(); + write_msgs_.push_back(msg); + if (!write_in_progress) + { + do_write(); + } + } + +private: + void do_read_header() + { + auto self(shared_from_this()); + boost::asio::async_read(socket_, + boost::asio::buffer(read_msg_.data(), chat_message::header_length), + [this, self](boost::system::error_code ec, std::size_t /*length*/) + { + if (!ec && read_msg_.decode_header()) + { + do_read_body(); + } + else + { + room_.leave(shared_from_this()); + } + }); + } + + void do_read_body() + { + auto self(shared_from_this()); + boost::asio::async_read(socket_, + boost::asio::buffer(read_msg_.body(), read_msg_.body_length()), + [this, self](boost::system::error_code ec, std::size_t /*length*/) + { + if (!ec) + { + room_.deliver(read_msg_); + do_read_header(); + } + else + { + room_.leave(shared_from_this()); + } + }); + } + + void do_write() + { + auto self(shared_from_this()); + boost::asio::async_write(socket_, + boost::asio::buffer(write_msgs_.front().data(), + write_msgs_.front().length()), + [this, self](boost::system::error_code ec, std::size_t /*length*/) + { + if (!ec) + { + write_msgs_.pop_front(); + if (!write_msgs_.empty()) + { + do_write(); + } + } + else + { + room_.leave(shared_from_this()); + } + }); + } + + tcp::socket socket_; + chat_room& room_; + chat_message read_msg_; + chat_message_queue write_msgs_; +}; + +//---------------------------------------------------------------------- + +class chat_server +{ +public: + chat_server(boost::asio::io_context& io_context, + const tcp::endpoint& endpoint) + : acceptor_(io_context, endpoint) + { + do_accept(); + } + +private: + void do_accept() + { + acceptor_.async_accept( + [this](boost::system::error_code ec, tcp::socket socket) + { + if (!ec) + { + std::make_shared<chat_session>(std::move(socket), room_)->start(); + } + + do_accept(); + }); + } + + tcp::acceptor acceptor_; + chat_room room_; +}; + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + try + { + if (argc < 2) + { + std::cerr << "Usage: chat_server <port> [<port> ...]\n"; + return 1; + } + + boost::asio::io_context io_context; + + std::list<chat_server> servers; + for (int i = 1; i < argc; ++i) + { + tcp::endpoint endpoint(tcp::v4(), std::atoi(argv[i])); + servers.emplace_back(io_context, endpoint); + } + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/echo/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/echo/Jamfile.v2 new file mode 100644 index 000000000..8af5f5beb --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/echo/Jamfile.v2 @@ -0,0 +1,37 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/thread//boost_thread + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe async_tcp_echo_server : async_tcp_echo_server.cpp ; +exe async_udp_echo_server : async_udp_echo_server.cpp ; +exe blocking_tcp_echo_client : blocking_tcp_echo_client.cpp ; +exe blocking_tcp_echo_server : blocking_tcp_echo_server.cpp ; +exe blocking_udp_echo_client : blocking_udp_echo_client.cpp ; +exe blocking_udp_echo_server : blocking_udp_echo_server.cpp ; diff --git a/src/boost/libs/asio/example/cpp11/echo/async_tcp_echo_server.cpp b/src/boost/libs/asio/example/cpp11/echo/async_tcp_echo_server.cpp new file mode 100644 index 000000000..f0c674e18 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/echo/async_tcp_echo_server.cpp @@ -0,0 +1,114 @@ +// +// async_tcp_echo_server.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <iostream> +#include <memory> +#include <utility> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +class session + : public std::enable_shared_from_this<session> +{ +public: + session(tcp::socket socket) + : socket_(std::move(socket)) + { + } + + void start() + { + do_read(); + } + +private: + void do_read() + { + auto self(shared_from_this()); + socket_.async_read_some(boost::asio::buffer(data_, max_length), + [this, self](boost::system::error_code ec, std::size_t length) + { + if (!ec) + { + do_write(length); + } + }); + } + + void do_write(std::size_t length) + { + auto self(shared_from_this()); + boost::asio::async_write(socket_, boost::asio::buffer(data_, length), + [this, self](boost::system::error_code ec, std::size_t /*length*/) + { + if (!ec) + { + do_read(); + } + }); + } + + tcp::socket socket_; + enum { max_length = 1024 }; + char data_[max_length]; +}; + +class server +{ +public: + server(boost::asio::io_context& io_context, short port) + : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) + { + do_accept(); + } + +private: + void do_accept() + { + acceptor_.async_accept( + [this](boost::system::error_code ec, tcp::socket socket) + { + if (!ec) + { + std::make_shared<session>(std::move(socket))->start(); + } + + do_accept(); + }); + } + + tcp::acceptor acceptor_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: async_tcp_echo_server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + server s(io_context, std::atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/echo/async_udp_echo_server.cpp b/src/boost/libs/asio/example/cpp11/echo/async_udp_echo_server.cpp new file mode 100644 index 000000000..ce3a59990 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/echo/async_udp_echo_server.cpp @@ -0,0 +1,82 @@ +// +// async_udp_echo_server.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <iostream> +#include <boost/asio.hpp> + +using boost::asio::ip::udp; + +class server +{ +public: + server(boost::asio::io_context& io_context, short port) + : socket_(io_context, udp::endpoint(udp::v4(), port)) + { + do_receive(); + } + + void do_receive() + { + socket_.async_receive_from( + boost::asio::buffer(data_, max_length), sender_endpoint_, + [this](boost::system::error_code ec, std::size_t bytes_recvd) + { + if (!ec && bytes_recvd > 0) + { + do_send(bytes_recvd); + } + else + { + do_receive(); + } + }); + } + + void do_send(std::size_t length) + { + socket_.async_send_to( + boost::asio::buffer(data_, length), sender_endpoint_, + [this](boost::system::error_code /*ec*/, std::size_t /*bytes_sent*/) + { + do_receive(); + }); + } + +private: + udp::socket socket_; + udp::endpoint sender_endpoint_; + enum { max_length = 1024 }; + char data_[max_length]; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: async_udp_echo_server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + server s(io_context, std::atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/echo/blocking_tcp_echo_client.cpp b/src/boost/libs/asio/example/cpp11/echo/blocking_tcp_echo_client.cpp new file mode 100644 index 000000000..8e73811b7 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/echo/blocking_tcp_echo_client.cpp @@ -0,0 +1,55 @@ +// +// blocking_tcp_echo_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +enum { max_length = 1024 }; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: blocking_tcp_echo_client <host> <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + tcp::socket s(io_context); + tcp::resolver resolver(io_context); + boost::asio::connect(s, resolver.resolve(argv[1], argv[2])); + + std::cout << "Enter message: "; + char request[max_length]; + std::cin.getline(request, max_length); + size_t request_length = std::strlen(request); + boost::asio::write(s, boost::asio::buffer(request, request_length)); + + char reply[max_length]; + size_t reply_length = boost::asio::read(s, + boost::asio::buffer(reply, request_length)); + std::cout << "Reply is: "; + std::cout.write(reply, reply_length); + std::cout << "\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/echo/blocking_tcp_echo_server.cpp b/src/boost/libs/asio/example/cpp11/echo/blocking_tcp_echo_server.cpp new file mode 100644 index 000000000..588b4a0c7 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/echo/blocking_tcp_echo_server.cpp @@ -0,0 +1,74 @@ +// +// blocking_tcp_echo_server.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <iostream> +#include <thread> +#include <utility> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +const int max_length = 1024; + +void session(tcp::socket sock) +{ + try + { + for (;;) + { + char data[max_length]; + + boost::system::error_code error; + size_t length = sock.read_some(boost::asio::buffer(data), error); + if (error == boost::asio::error::eof) + break; // Connection closed cleanly by peer. + else if (error) + throw boost::system::system_error(error); // Some other error. + + boost::asio::write(sock, boost::asio::buffer(data, length)); + } + } + catch (std::exception& e) + { + std::cerr << "Exception in thread: " << e.what() << "\n"; + } +} + +void server(boost::asio::io_context& io_context, unsigned short port) +{ + tcp::acceptor a(io_context, tcp::endpoint(tcp::v4(), port)); + for (;;) + { + std::thread(session, a.accept()).detach(); + } +} + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: blocking_tcp_echo_server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + server(io_context, std::atoi(argv[1])); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/echo/blocking_udp_echo_client.cpp b/src/boost/libs/asio/example/cpp11/echo/blocking_udp_echo_client.cpp new file mode 100644 index 000000000..8ed9e3a29 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/echo/blocking_udp_echo_client.cpp @@ -0,0 +1,58 @@ +// +// blocking_udp_echo_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <boost/asio.hpp> + +using boost::asio::ip::udp; + +enum { max_length = 1024 }; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: blocking_udp_echo_client <host> <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + udp::socket s(io_context, udp::endpoint(udp::v4(), 0)); + + udp::resolver resolver(io_context); + udp::resolver::results_type endpoints = + resolver.resolve(udp::v4(), argv[1], argv[2]); + + std::cout << "Enter message: "; + char request[max_length]; + std::cin.getline(request, max_length); + size_t request_length = std::strlen(request); + s.send_to(boost::asio::buffer(request, request_length), *endpoints.begin()); + + char reply[max_length]; + udp::endpoint sender_endpoint; + size_t reply_length = s.receive_from( + boost::asio::buffer(reply, max_length), sender_endpoint); + std::cout << "Reply is: "; + std::cout.write(reply, reply_length); + std::cout << "\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/echo/blocking_udp_echo_server.cpp b/src/boost/libs/asio/example/cpp11/echo/blocking_udp_echo_server.cpp new file mode 100644 index 000000000..e1f41e28f --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/echo/blocking_udp_echo_server.cpp @@ -0,0 +1,52 @@ +// +// blocking_udp_echo_server.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <iostream> +#include <boost/asio.hpp> + +using boost::asio::ip::udp; + +enum { max_length = 1024 }; + +void server(boost::asio::io_context& io_context, unsigned short port) +{ + udp::socket sock(io_context, udp::endpoint(udp::v4(), port)); + for (;;) + { + char data[max_length]; + udp::endpoint sender_endpoint; + size_t length = sock.receive_from( + boost::asio::buffer(data, max_length), sender_endpoint); + sock.send_to(boost::asio::buffer(data, length), sender_endpoint); + } +} + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: blocking_udp_echo_server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + server(io_context, std::atoi(argv[1])); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/executors/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/executors/Jamfile.v2 new file mode 100644 index 000000000..26ef1fda1 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/executors/Jamfile.v2 @@ -0,0 +1,36 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe actor : actor.cpp ; +exe bank_account_1 : bank_account_1.cpp ; +exe bank_account_2 : bank_account_2.cpp ; +exe fork_join : fork_join.cpp ; +exe pipeline : pipeline.cpp ; +exe priority_scheduler : priority_scheduler.cpp ; diff --git a/src/boost/libs/asio/example/cpp11/executors/actor.cpp b/src/boost/libs/asio/example/cpp11/executors/actor.cpp new file mode 100644 index 000000000..2e3311d40 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/executors/actor.cpp @@ -0,0 +1,286 @@ +#include <boost/asio/defer.hpp> +#include <boost/asio/executor.hpp> +#include <boost/asio/post.hpp> +#include <boost/asio/strand.hpp> +#include <boost/asio/system_executor.hpp> +#include <condition_variable> +#include <deque> +#include <memory> +#include <mutex> +#include <typeinfo> +#include <vector> + +using boost::asio::defer; +using boost::asio::executor; +using boost::asio::post; +using boost::asio::strand; +using boost::asio::system_executor; + +//------------------------------------------------------------------------------ +// A tiny actor framework +// ~~~~~~~~~~~~~~~~~~~~~~ + +class actor; + +// Used to identify the sender and recipient of messages. +typedef actor* actor_address; + +// Base class for all registered message handlers. +class message_handler_base +{ +public: + virtual ~message_handler_base() {} + + // Used to determine which message handlers receive an incoming message. + virtual const std::type_info& message_id() const = 0; +}; + +// Base class for a handler for a specific message type. +template <class Message> +class message_handler : public message_handler_base +{ +public: + // Handle an incoming message. + virtual void handle_message(Message msg, actor_address from) = 0; +}; + +// Concrete message handler for a specific message type. +template <class Actor, class Message> +class mf_message_handler : public message_handler<Message> +{ +public: + // Construct a message handler to invoke the specified member function. + mf_message_handler(void (Actor::* mf)(Message, actor_address), Actor* a) + : function_(mf), actor_(a) + { + } + + // Used to determine which message handlers receive an incoming message. + virtual const std::type_info& message_id() const + { + return typeid(Message); + } + + // Handle an incoming message. + virtual void handle_message(Message msg, actor_address from) + { + (actor_->*function_)(std::move(msg), from); + } + + // Determine whether the message handler represents the specified function. + bool is_function(void (Actor::* mf)(Message, actor_address)) const + { + return mf == function_; + } + +private: + void (Actor::* function_)(Message, actor_address); + Actor* actor_; +}; + +// Base class for all actors. +class actor +{ +public: + virtual ~actor() + { + } + + // Obtain the actor's address for use as a message sender or recipient. + actor_address address() + { + return this; + } + + // Send a message from one actor to another. + template <class Message> + friend void send(Message msg, actor_address from, actor_address to) + { + // Execute the message handler in the context of the target's executor. + post(to->executor_, + [=] + { + to->call_handler(std::move(msg), from); + }); + } + +protected: + // Construct the actor to use the specified executor for all message handlers. + actor(executor e) + : executor_(std::move(e)) + { + } + + // Register a handler for a specific message type. Duplicates are permitted. + template <class Actor, class Message> + void register_handler(void (Actor::* mf)(Message, actor_address)) + { + handlers_.push_back( + std::make_shared<mf_message_handler<Actor, Message>>( + mf, static_cast<Actor*>(this))); + } + + // Deregister a handler. Removes only the first matching handler. + template <class Actor, class Message> + void deregister_handler(void (Actor::* mf)(Message, actor_address)) + { + const std::type_info& id = typeid(message_handler<Message>); + for (auto iter = handlers_.begin(); iter != handlers_.end(); ++iter) + { + if ((*iter)->message_id() == id) + { + auto mh = static_cast<mf_message_handler<Actor, Message>*>(iter->get()); + if (mh->is_function(mf)) + { + handlers_.erase(iter); + return; + } + } + } + } + + // Send a message from within a message handler. + template <class Message> + void tail_send(Message msg, actor_address to) + { + // Execute the message handler in the context of the target's executor. + actor* from = this; + defer(to->executor_, + [=] + { + to->call_handler(std::move(msg), from); + }); + } + +private: + // Find the matching message handlers, if any, and call them. + template <class Message> + void call_handler(Message msg, actor_address from) + { + const std::type_info& message_id = typeid(Message); + for (auto& h: handlers_) + { + if (h->message_id() == message_id) + { + auto mh = static_cast<message_handler<Message>*>(h.get()); + mh->handle_message(msg, from); + } + } + } + + // All messages associated with a single actor object should be processed + // non-concurrently. We use a strand to ensure non-concurrent execution even + // if the underlying executor may use multiple threads. + strand<executor> executor_; + + std::vector<std::shared_ptr<message_handler_base>> handlers_; +}; + +// A concrete actor that allows synchronous message retrieval. +template <class Message> +class receiver : public actor +{ +public: + receiver() + : actor(system_executor()) + { + register_handler(&receiver::message_handler); + } + + // Block until a message has been received. + Message wait() + { + std::unique_lock<std::mutex> lock(mutex_); + condition_.wait(lock, [this]{ return !message_queue_.empty(); }); + Message msg(std::move(message_queue_.front())); + message_queue_.pop_front(); + return msg; + } + +private: + // Handle a new message by adding it to the queue and waking a waiter. + void message_handler(Message msg, actor_address /* from */) + { + std::lock_guard<std::mutex> lock(mutex_); + message_queue_.push_back(std::move(msg)); + condition_.notify_one(); + } + + std::mutex mutex_; + std::condition_variable condition_; + std::deque<Message> message_queue_; +}; + +//------------------------------------------------------------------------------ + +#include <boost/asio/thread_pool.hpp> +#include <iostream> + +using boost::asio::thread_pool; + +class member : public actor +{ +public: + explicit member(executor e) + : actor(std::move(e)) + { + register_handler(&member::init_handler); + } + +private: + void init_handler(actor_address next, actor_address from) + { + next_ = next; + caller_ = from; + + register_handler(&member::token_handler); + deregister_handler(&member::init_handler); + } + + void token_handler(int token, actor_address /*from*/) + { + int msg(token); + actor_address to(caller_); + + if (token > 0) + { + msg = token - 1; + to = next_; + } + + tail_send(msg, to); + } + + actor_address next_; + actor_address caller_; +}; + +int main() +{ + const std::size_t num_threads = 16; + const int num_hops = 50000000; + const std::size_t num_actors = 503; + const int token_value = (num_hops + num_actors - 1) / num_actors; + const std::size_t actors_per_thread = num_actors / num_threads; + + struct single_thread_pool : thread_pool { single_thread_pool() : thread_pool(1) {} }; + single_thread_pool pools[num_threads]; + std::vector<std::shared_ptr<member>> members(num_actors); + receiver<int> rcvr; + + // Create the member actors. + for (std::size_t i = 0; i < num_actors; ++i) + members[i] = std::make_shared<member>(pools[(i / actors_per_thread) % num_threads].get_executor()); + + // Initialise the actors by passing each one the address of the next actor in the ring. + for (std::size_t i = num_actors, next_i = 0; i > 0; next_i = --i) + send(members[next_i]->address(), rcvr.address(), members[i - 1]->address()); + + // Send exactly one token to each actor, all with the same initial value, rounding up if required. + for (std::size_t i = 0; i < num_actors; ++i) + send(token_value, rcvr.address(), members[i]->address()); + + // Wait for all signal messages, indicating the tokens have all reached zero. + for (std::size_t i = 0; i < num_actors; ++i) + rcvr.wait(); +} diff --git a/src/boost/libs/asio/example/cpp11/executors/bank_account_1.cpp b/src/boost/libs/asio/example/cpp11/executors/bank_account_1.cpp new file mode 100644 index 000000000..35b659be8 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/executors/bank_account_1.cpp @@ -0,0 +1,54 @@ +#include <boost/asio/post.hpp> +#include <boost/asio/thread_pool.hpp> +#include <iostream> + +using boost::asio::post; +using boost::asio::thread_pool; + +// Traditional active object pattern. +// Member functions do not block. + +class bank_account +{ + int balance_ = 0; + mutable thread_pool pool_{1}; + +public: + void deposit(int amount) + { + post(pool_, [=] + { + balance_ += amount; + }); + } + + void withdraw(int amount) + { + post(pool_, [=] + { + if (balance_ >= amount) + balance_ -= amount; + }); + } + + void print_balance() const + { + post(pool_, [=] + { + std::cout << "balance = " << balance_ << "\n"; + }); + } + + ~bank_account() + { + pool_.join(); + } +}; + +int main() +{ + bank_account acct; + acct.deposit(20); + acct.withdraw(10); + acct.print_balance(); +} diff --git a/src/boost/libs/asio/example/cpp11/executors/bank_account_2.cpp b/src/boost/libs/asio/example/cpp11/executors/bank_account_2.cpp new file mode 100644 index 000000000..aebff7645 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/executors/bank_account_2.cpp @@ -0,0 +1,54 @@ +#include <boost/asio/post.hpp> +#include <boost/asio/thread_pool.hpp> +#include <boost/asio/use_future.hpp> +#include <iostream> + +using boost::asio::post; +using boost::asio::thread_pool; +using boost::asio::use_future; + +// Traditional active object pattern. +// Member functions block until operation is finished. + +class bank_account +{ + int balance_ = 0; + mutable thread_pool pool_{1}; + +public: + void deposit(int amount) + { + post(pool_, + use_future([=] + { + balance_ += amount; + })).get(); + } + + void withdraw(int amount) + { + post(pool_, + use_future([=] + { + if (balance_ >= amount) + balance_ -= amount; + })).get(); + } + + int balance() const + { + return post(pool_, + use_future([=] + { + return balance_; + })).get(); + } +}; + +int main() +{ + bank_account acct; + acct.deposit(20); + acct.withdraw(10); + std::cout << "balance = " << acct.balance() << "\n"; +} diff --git a/src/boost/libs/asio/example/cpp11/executors/fork_join.cpp b/src/boost/libs/asio/example/cpp11/executors/fork_join.cpp new file mode 100644 index 000000000..98926d53f --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/executors/fork_join.cpp @@ -0,0 +1,328 @@ +#include <boost/asio/dispatch.hpp> +#include <boost/asio/execution_context.hpp> +#include <boost/asio/thread_pool.hpp> +#include <condition_variable> +#include <memory> +#include <mutex> +#include <queue> +#include <thread> +#include <numeric> + +using boost::asio::dispatch; +using boost::asio::execution_context; +using boost::asio::thread_pool; + +// A fixed-size thread pool used to implement fork/join semantics. Functions +// are scheduled using a simple FIFO queue. Implementing work stealing, or +// using a queue based on atomic operations, are left as tasks for the reader. +class fork_join_pool : public execution_context +{ +public: + // The constructor starts a thread pool with the specified number of threads. + // Note that the thread_count is not a fixed limit on the pool's concurrency. + // Additional threads may temporarily be added to the pool if they join a + // fork_executor. + explicit fork_join_pool( + std::size_t thread_count = std::thread::hardware_concurrency() * 2) + : use_count_(1), + threads_(thread_count) + { + try + { + // Ask each thread in the pool to dequeue and execute functions until + // it is time to shut down, i.e. the use count is zero. + for (thread_count_ = 0; thread_count_ < thread_count; ++thread_count_) + { + dispatch(threads_, [&] + { + std::unique_lock<std::mutex> lock(mutex_); + while (use_count_ > 0) + if (!execute_next(lock)) + condition_.wait(lock); + }); + } + } + catch (...) + { + stop_threads(); + threads_.join(); + throw; + } + } + + // The destructor waits for the pool to finish executing functions. + ~fork_join_pool() + { + stop_threads(); + threads_.join(); + } + +private: + friend class fork_executor; + + // The base for all functions that are queued in the pool. + struct function_base + { + std::shared_ptr<std::size_t> work_count_; + void (*execute_)(std::shared_ptr<function_base>& p); + }; + + // Execute the next function from the queue, if any. Returns true if a + // function was executed, and false if the queue was empty. + bool execute_next(std::unique_lock<std::mutex>& lock) + { + if (queue_.empty()) + return false; + auto p(queue_.front()); + queue_.pop(); + lock.unlock(); + execute(lock, p); + return true; + } + + // Execute a function and decrement the outstanding work. + void execute(std::unique_lock<std::mutex>& lock, + std::shared_ptr<function_base>& p) + { + std::shared_ptr<std::size_t> work_count(std::move(p->work_count_)); + try + { + p->execute_(p); + lock.lock(); + do_work_finished(work_count); + } + catch (...) + { + lock.lock(); + do_work_finished(work_count); + throw; + } + } + + // Increment outstanding work. + void do_work_started(const std::shared_ptr<std::size_t>& work_count) noexcept + { + if (++(*work_count) == 1) + ++use_count_; + } + + // Decrement outstanding work. Notify waiting threads if we run out. + void do_work_finished(const std::shared_ptr<std::size_t>& work_count) noexcept + { + if (--(*work_count) == 0) + { + --use_count_; + condition_.notify_all(); + } + } + + // Dispatch a function, executing it immediately if the queue is already + // loaded. Otherwise adds the function to the queue and wakes a thread. + void do_dispatch(std::shared_ptr<function_base> p, + const std::shared_ptr<std::size_t>& work_count) + { + std::unique_lock<std::mutex> lock(mutex_); + if (queue_.size() > thread_count_ * 16) + { + do_work_started(work_count); + lock.unlock(); + execute(lock, p); + } + else + { + queue_.push(p); + do_work_started(work_count); + condition_.notify_one(); + } + } + + // Add a function to the queue and wake a thread. + void do_post(std::shared_ptr<function_base> p, + const std::shared_ptr<std::size_t>& work_count) + { + std::lock_guard<std::mutex> lock(mutex_); + queue_.push(p); + do_work_started(work_count); + condition_.notify_one(); + } + + // Ask all threads to shut down. + void stop_threads() + { + std::lock_guard<std::mutex> lock(mutex_); + --use_count_; + condition_.notify_all(); + } + + std::mutex mutex_; + std::condition_variable condition_; + std::queue<std::shared_ptr<function_base>> queue_; + std::size_t use_count_; + std::size_t thread_count_; + thread_pool threads_; +}; + +// A class that satisfies the Executor requirements. Every function or piece of +// work associated with a fork_executor is part of a single, joinable group. +class fork_executor +{ +public: + fork_executor(fork_join_pool& ctx) + : context_(ctx), + work_count_(std::make_shared<std::size_t>(0)) + { + } + + fork_join_pool& context() const noexcept + { + return context_; + } + + void on_work_started() const noexcept + { + std::lock_guard<std::mutex> lock(context_.mutex_); + context_.do_work_started(work_count_); + } + + void on_work_finished() const noexcept + { + std::lock_guard<std::mutex> lock(context_.mutex_); + context_.do_work_finished(work_count_); + } + + template <class Func, class Alloc> + void dispatch(Func&& f, const Alloc& a) const + { + auto p(std::allocate_shared<function<Func>>( + typename std::allocator_traits<Alloc>::template rebind_alloc<char>(a), + std::move(f), work_count_)); + context_.do_dispatch(p, work_count_); + } + + template <class Func, class Alloc> + void post(Func f, const Alloc& a) const + { + auto p(std::allocate_shared<function<Func>>( + typename std::allocator_traits<Alloc>::template rebind_alloc<char>(a), + std::move(f), work_count_)); + context_.do_post(p, work_count_); + } + + template <class Func, class Alloc> + void defer(Func&& f, const Alloc& a) const + { + post(std::forward<Func>(f), a); + } + + friend bool operator==(const fork_executor& a, + const fork_executor& b) noexcept + { + return a.work_count_ == b.work_count_; + } + + friend bool operator!=(const fork_executor& a, + const fork_executor& b) noexcept + { + return a.work_count_ != b.work_count_; + } + + // Block until all work associated with the executor is complete. While it is + // waiting, the thread may be borrowed to execute functions from the queue. + void join() const + { + std::unique_lock<std::mutex> lock(context_.mutex_); + while (*work_count_ > 0) + if (!context_.execute_next(lock)) + context_.condition_.wait(lock); + } + +private: + template <class Func> + struct function : fork_join_pool::function_base + { + explicit function(Func f, const std::shared_ptr<std::size_t>& w) + : function_(std::move(f)) + { + work_count_ = w; + execute_ = [](std::shared_ptr<fork_join_pool::function_base>& p) + { + Func tmp(std::move(static_cast<function*>(p.get())->function_)); + p.reset(); + tmp(); + }; + } + + Func function_; + }; + + fork_join_pool& context_; + std::shared_ptr<std::size_t> work_count_; +}; + +// Helper class to automatically join a fork_executor when exiting a scope. +class join_guard +{ +public: + explicit join_guard(const fork_executor& ex) : ex_(ex) {} + join_guard(const join_guard&) = delete; + join_guard(join_guard&&) = delete; + ~join_guard() { ex_.join(); } + +private: + fork_executor ex_; +}; + +//------------------------------------------------------------------------------ + +#include <algorithm> +#include <iostream> +#include <random> +#include <vector> + +fork_join_pool pool; + +template <class Iterator> +void fork_join_sort(Iterator begin, Iterator end) +{ + std::size_t n = end - begin; + if (n > 32768) + { + { + fork_executor fork(pool); + join_guard join(fork); + dispatch(fork, [=]{ fork_join_sort(begin, begin + n / 2); }); + dispatch(fork, [=]{ fork_join_sort(begin + n / 2, end); }); + } + std::inplace_merge(begin, begin + n / 2, end); + } + else + { + std::sort(begin, end); + } +} + +int main(int argc, char* argv[]) +{ + if (argc != 2) + { + std::cerr << "Usage: fork_join <size>\n"; + return 1; + } + + std::vector<double> vec(std::atoll(argv[1])); + std::iota(vec.begin(), vec.end(), 0); + + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(vec.begin(), vec.end(), g); + + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + + fork_join_sort(vec.begin(), vec.end()); + + std::chrono::steady_clock::duration elapsed = std::chrono::steady_clock::now() - start; + + std::cout << "sort took "; + std::cout << std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count(); + std::cout << " microseconds" << std::endl; +} diff --git a/src/boost/libs/asio/example/cpp11/executors/pipeline.cpp b/src/boost/libs/asio/example/cpp11/executors/pipeline.cpp new file mode 100644 index 000000000..9c2a778fe --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/executors/pipeline.cpp @@ -0,0 +1,299 @@ +#include <boost/asio/associated_executor.hpp> +#include <boost/asio/bind_executor.hpp> +#include <boost/asio/execution_context.hpp> +#include <boost/asio/post.hpp> +#include <boost/asio/system_executor.hpp> +#include <boost/asio/use_future.hpp> +#include <condition_variable> +#include <future> +#include <memory> +#include <mutex> +#include <queue> +#include <thread> +#include <vector> +#include <cctype> + +using boost::asio::execution_context; +using boost::asio::executor_binder; +using boost::asio::get_associated_executor; +using boost::asio::post; +using boost::asio::system_executor; +using boost::asio::use_future; +using boost::asio::use_service; + +// An executor that launches a new thread for each function submitted to it. +// This class satisfies the Executor requirements. +class thread_executor +{ +private: + // Service to track all threads started through a thread_executor. + class thread_bag : public execution_context::service + { + public: + typedef thread_bag key_type; + + explicit thread_bag(execution_context& ctx) + : execution_context::service(ctx) + { + } + + void add_thread(std::thread&& t) + { + std::unique_lock<std::mutex> lock(mutex_); + threads_.push_back(std::move(t)); + } + + private: + virtual void shutdown() + { + for (auto& t : threads_) + t.join(); + } + + std::mutex mutex_; + std::vector<std::thread> threads_; + }; + +public: + execution_context& context() const noexcept + { + return system_executor().context(); + } + + void on_work_started() const noexcept + { + // This executor doesn't count work. + } + + void on_work_finished() const noexcept + { + // This executor doesn't count work. + } + + template <class Func, class Alloc> + void dispatch(Func&& f, const Alloc& a) const + { + post(std::forward<Func>(f), a); + } + + template <class Func, class Alloc> + void post(Func f, const Alloc&) const + { + thread_bag& bag = use_service<thread_bag>(context()); + bag.add_thread(std::thread(std::move(f))); + } + + template <class Func, class Alloc> + void defer(Func&& f, const Alloc& a) const + { + post(std::forward<Func>(f), a); + } + + friend bool operator==(const thread_executor&, + const thread_executor&) noexcept + { + return true; + } + + friend bool operator!=(const thread_executor&, + const thread_executor&) noexcept + { + return false; + } +}; + +// Base class for all thread-safe queue implementations. +class queue_impl_base +{ + template <class> friend class queue_front; + template <class> friend class queue_back; + std::mutex mutex_; + std::condition_variable condition_; + bool stop_ = false; +}; + +// Underlying implementation of a thread-safe queue, shared between the +// queue_front and queue_back classes. +template <class T> +class queue_impl : public queue_impl_base +{ + template <class> friend class queue_front; + template <class> friend class queue_back; + std::queue<T> queue_; +}; + +// The front end of a queue between consecutive pipeline stages. +template <class T> +class queue_front +{ +public: + typedef T value_type; + + explicit queue_front(std::shared_ptr<queue_impl<T>> impl) + : impl_(impl) + { + } + + void push(T t) + { + std::unique_lock<std::mutex> lock(impl_->mutex_); + impl_->queue_.push(std::move(t)); + impl_->condition_.notify_one(); + } + + void stop() + { + std::unique_lock<std::mutex> lock(impl_->mutex_); + impl_->stop_ = true; + impl_->condition_.notify_one(); + } + +private: + std::shared_ptr<queue_impl<T>> impl_; +}; + +// The back end of a queue between consecutive pipeline stages. +template <class T> +class queue_back +{ +public: + typedef T value_type; + + explicit queue_back(std::shared_ptr<queue_impl<T>> impl) + : impl_(impl) + { + } + + bool pop(T& t) + { + std::unique_lock<std::mutex> lock(impl_->mutex_); + while (impl_->queue_.empty() && !impl_->stop_) + impl_->condition_.wait(lock); + if (!impl_->queue_.empty()) + { + t = impl_->queue_.front(); + impl_->queue_.pop(); + return true; + } + return false; + } + +private: + std::shared_ptr<queue_impl<T>> impl_; +}; + +// Launch the last stage in a pipeline. +template <class T, class F> +std::future<void> pipeline(queue_back<T> in, F f) +{ + // Get the function's associated executor, defaulting to thread_executor. + auto ex = get_associated_executor(f, thread_executor()); + + // Run the function, and as we're the last stage return a future so that the + // caller can wait for the pipeline to finish. + return post(ex, use_future([in, f]() mutable { f(in); })); +} + +// Launch an intermediate stage in a pipeline. +template <class T, class F, class... Tail> +std::future<void> pipeline(queue_back<T> in, F f, Tail... t) +{ + // Determine the output queue type. + typedef typename executor_binder<F, thread_executor>::second_argument_type::value_type output_value_type; + + // Create the output queue and its implementation. + auto out_impl = std::make_shared<queue_impl<output_value_type>>(); + queue_front<output_value_type> out(out_impl); + queue_back<output_value_type> next_in(out_impl); + + // Get the function's associated executor, defaulting to thread_executor. + auto ex = get_associated_executor(f, thread_executor()); + + // Run the function. + post(ex, [in, out, f]() mutable + { + f(in, out); + out.stop(); + }); + + // Launch the rest of the pipeline. + return pipeline(next_in, std::move(t)...); +} + +// Launch the first stage in a pipeline. +template <class F, class... Tail> +std::future<void> pipeline(F f, Tail... t) +{ + // Determine the output queue type. + typedef typename executor_binder<F, thread_executor>::argument_type::value_type output_value_type; + + // Create the output queue and its implementation. + auto out_impl = std::make_shared<queue_impl<output_value_type>>(); + queue_front<output_value_type> out(out_impl); + queue_back<output_value_type> next_in(out_impl); + + // Get the function's associated executor, defaulting to thread_executor. + auto ex = get_associated_executor(f, thread_executor()); + + // Run the function. + post(ex, [out, f]() mutable + { + f(out); + out.stop(); + }); + + // Launch the rest of the pipeline. + return pipeline(next_in, std::move(t)...); +} + +//------------------------------------------------------------------------------ + +#include <boost/asio/thread_pool.hpp> +#include <iostream> +#include <string> + +using boost::asio::bind_executor; +using boost::asio::thread_pool; + +void reader(queue_front<std::string> out) +{ + std::string line; + while (std::getline(std::cin, line)) + out.push(line); +} + +void filter(queue_back<std::string> in, queue_front<std::string> out) +{ + std::string line; + while (in.pop(line)) + if (line.length() > 5) + out.push(line); +} + +void upper(queue_back<std::string> in, queue_front<std::string> out) +{ + std::string line; + while (in.pop(line)) + { + std::string new_line; + for (char c : line) + new_line.push_back(std::toupper(c)); + out.push(new_line); + } +} + +void writer(queue_back<std::string> in) +{ + std::size_t count = 0; + std::string line; + while (in.pop(line)) + std::cout << count++ << ": " << line << std::endl; +} + +int main() +{ + thread_pool pool; + + auto f = pipeline(reader, filter, bind_executor(pool, upper), writer); + f.wait(); +} diff --git a/src/boost/libs/asio/example/cpp11/executors/priority_scheduler.cpp b/src/boost/libs/asio/example/cpp11/executors/priority_scheduler.cpp new file mode 100644 index 000000000..7c9bc80d7 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/executors/priority_scheduler.cpp @@ -0,0 +1,174 @@ +#include <boost/asio/dispatch.hpp> +#include <boost/asio/execution_context.hpp> +#include <condition_variable> +#include <iostream> +#include <memory> +#include <mutex> +#include <queue> + +using boost::asio::dispatch; +using boost::asio::execution_context; + +class priority_scheduler : public execution_context +{ +public: + // A class that satisfies the Executor requirements. + class executor_type + { + public: + executor_type(priority_scheduler& ctx, int pri) noexcept + : context_(ctx), priority_(pri) + { + } + + priority_scheduler& context() const noexcept + { + return context_; + } + + void on_work_started() const noexcept + { + // This executor doesn't count work. Instead, the scheduler simply runs + // until explicitly stopped. + } + + void on_work_finished() const noexcept + { + // This executor doesn't count work. Instead, the scheduler simply runs + // until explicitly stopped. + } + + template <class Func, class Alloc> + void dispatch(Func&& f, const Alloc& a) const + { + post(std::forward<Func>(f), a); + } + + template <class Func, class Alloc> + void post(Func f, const Alloc& a) const + { + auto p(std::allocate_shared<item<Func>>( + typename std::allocator_traits< + Alloc>::template rebind_alloc<char>(a), + priority_, std::move(f))); + std::lock_guard<std::mutex> lock(context_.mutex_); + context_.queue_.push(p); + context_.condition_.notify_one(); + } + + template <class Func, class Alloc> + void defer(Func&& f, const Alloc& a) const + { + post(std::forward<Func>(f), a); + } + + friend bool operator==(const executor_type& a, + const executor_type& b) noexcept + { + return &a.context_ == &b.context_; + } + + friend bool operator!=(const executor_type& a, + const executor_type& b) noexcept + { + return &a.context_ != &b.context_; + } + + private: + priority_scheduler& context_; + int priority_; + }; + + ~priority_scheduler() noexcept + { + shutdown(); + destroy(); + } + + executor_type get_executor(int pri = 0) noexcept + { + return executor_type(*const_cast<priority_scheduler*>(this), pri); + } + + void run() + { + std::unique_lock<std::mutex> lock(mutex_); + for (;;) + { + condition_.wait(lock, [&]{ return stopped_ || !queue_.empty(); }); + if (stopped_) + return; + auto p(queue_.top()); + queue_.pop(); + lock.unlock(); + p->execute_(p); + lock.lock(); + } + } + + void stop() + { + std::lock_guard<std::mutex> lock(mutex_); + stopped_ = true; + condition_.notify_all(); + } + +private: + struct item_base + { + int priority_; + void (*execute_)(std::shared_ptr<item_base>&); + }; + + template <class Func> + struct item : item_base + { + item(int pri, Func f) : function_(std::move(f)) + { + priority_ = pri; + execute_ = [](std::shared_ptr<item_base>& p) + { + Func tmp(std::move(static_cast<item*>(p.get())->function_)); + p.reset(); + tmp(); + }; + } + + Func function_; + }; + + struct item_comp + { + bool operator()( + const std::shared_ptr<item_base>& a, + const std::shared_ptr<item_base>& b) + { + return a->priority_ < b->priority_; + } + }; + + std::mutex mutex_; + std::condition_variable condition_; + std::priority_queue< + std::shared_ptr<item_base>, + std::vector<std::shared_ptr<item_base>>, + item_comp> queue_; + bool stopped_ = false; +}; + +int main() +{ + priority_scheduler sched; + auto low = sched.get_executor(0); + auto med = sched.get_executor(1); + auto high = sched.get_executor(2); + dispatch(low, []{ std::cout << "1\n"; }); + dispatch(low, []{ std::cout << "11\n"; }); + dispatch(med, []{ std::cout << "2\n"; }); + dispatch(med, []{ std::cout << "22\n"; }); + dispatch(high, []{ std::cout << "3\n"; }); + dispatch(high, []{ std::cout << "33\n"; }); + dispatch(high, []{ std::cout << "333\n"; }); + dispatch(sched.get_executor(-1), [&]{ sched.stop(); }); + sched.run(); +} diff --git a/src/boost/libs/asio/example/cpp11/fork/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/fork/Jamfile.v2 new file mode 100644 index 000000000..d6b8eb264 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/fork/Jamfile.v2 @@ -0,0 +1,45 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe daemon + : daemon.cpp + /boost/system//boost_system + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe process_per_connection + : process_per_connection.cpp + /boost/system//boost_system + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp11/fork/daemon.cpp b/src/boost/libs/asio/example/cpp11/fork/daemon.cpp new file mode 100644 index 000000000..76f9571db --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/fork/daemon.cpp @@ -0,0 +1,189 @@ +// +// daemon.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/udp.hpp> +#include <boost/asio/signal_set.hpp> +#include <array> +#include <ctime> +#include <iostream> +#include <syslog.h> +#include <unistd.h> + +using boost::asio::ip::udp; + +class udp_daytime_server +{ +public: + udp_daytime_server(boost::asio::io_context& io_context) + : socket_(io_context, {udp::v4(), 13}) + { + receive(); + } + +private: + void receive() + { + socket_.async_receive_from( + boost::asio::buffer(recv_buffer_), remote_endpoint_, + [this](boost::system::error_code ec, std::size_t /*n*/) + { + if (!ec) + { + using namespace std; // For time_t, time and ctime; + time_t now = time(0); + std::string message = ctime(&now); + + boost::system::error_code ignored_ec; + socket_.send_to(boost::asio::buffer(message), + remote_endpoint_, 0, ignored_ec); + } + + receive(); + }); + } + + udp::socket socket_; + udp::endpoint remote_endpoint_; + std::array<char, 1> recv_buffer_; +}; + +int main() +{ + try + { + boost::asio::io_context io_context; + + // Initialise the server before becoming a daemon. If the process is + // started from a shell, this means any errors will be reported back to the + // user. + udp_daytime_server server(io_context); + + // Register signal handlers so that the daemon may be shut down. You may + // also want to register for other signals, such as SIGHUP to trigger a + // re-read of a configuration file. + boost::asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait( + [&](boost::system::error_code /*ec*/, int /*signo*/) + { + io_context.stop(); + }); + + // Inform the io_context that we are about to become a daemon. The + // io_context cleans up any internal resources, such as threads, that may + // interfere with forking. + io_context.notify_fork(boost::asio::io_context::fork_prepare); + + // Fork the process and have the parent exit. If the process was started + // from a shell, this returns control to the user. Forking a new process is + // also a prerequisite for the subsequent call to setsid(). + if (pid_t pid = fork()) + { + if (pid > 0) + { + // We're in the parent process and need to exit. + // + // When the exit() function is used, the program terminates without + // invoking local variables' destructors. Only global variables are + // destroyed. As the io_context object is a local variable, this means + // we do not have to call: + // + // io_context.notify_fork(boost::asio::io_context::fork_parent); + // + // However, this line should be added before each call to exit() if + // using a global io_context object. An additional call: + // + // io_context.notify_fork(boost::asio::io_context::fork_prepare); + // + // should also precede the second fork(). + exit(0); + } + else + { + syslog(LOG_ERR | LOG_USER, "First fork failed: %m"); + return 1; + } + } + + // Make the process a new session leader. This detaches it from the + // terminal. + setsid(); + + // A process inherits its working directory from its parent. This could be + // on a mounted filesystem, which means that the running daemon would + // prevent this filesystem from being unmounted. Changing to the root + // directory avoids this problem. + chdir("/"); + + // The file mode creation mask is also inherited from the parent process. + // We don't want to restrict the permissions on files created by the + // daemon, so the mask is cleared. + umask(0); + + // A second fork ensures the process cannot acquire a controlling terminal. + if (pid_t pid = fork()) + { + if (pid > 0) + { + exit(0); + } + else + { + syslog(LOG_ERR | LOG_USER, "Second fork failed: %m"); + return 1; + } + } + + // Close the standard streams. This decouples the daemon from the terminal + // that started it. + close(0); + close(1); + close(2); + + // We don't want the daemon to have any standard input. + if (open("/dev/null", O_RDONLY) < 0) + { + syslog(LOG_ERR | LOG_USER, "Unable to open /dev/null: %m"); + return 1; + } + + // Send standard output to a log file. + const char* output = "/tmp/asio.daemon.out"; + const int flags = O_WRONLY | O_CREAT | O_APPEND; + const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + if (open(output, flags, mode) < 0) + { + syslog(LOG_ERR | LOG_USER, "Unable to open output file %s: %m", output); + return 1; + } + + // Also send standard error to the same log file. + if (dup(1) < 0) + { + syslog(LOG_ERR | LOG_USER, "Unable to dup output descriptor: %m"); + return 1; + } + + // Inform the io_context that we have finished becoming a daemon. The + // io_context uses this opportunity to create any internal file descriptors + // that need to be private to the new process. + io_context.notify_fork(boost::asio::io_context::fork_child); + + // The io_context can now be used normally. + syslog(LOG_INFO | LOG_USER, "Daemon started"); + io_context.run(); + syslog(LOG_INFO | LOG_USER, "Daemon stopped"); + } + catch (std::exception& e) + { + syslog(LOG_ERR | LOG_USER, "Exception: %s", e.what()); + std::cerr << "Exception: " << e.what() << std::endl; + } +} diff --git a/src/boost/libs/asio/example/cpp11/fork/process_per_connection.cpp b/src/boost/libs/asio/example/cpp11/fork/process_per_connection.cpp new file mode 100644 index 000000000..4fb4ae889 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/fork/process_per_connection.cpp @@ -0,0 +1,162 @@ +// +// process_per_connection.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/signal_set.hpp> +#include <boost/asio/write.hpp> +#include <cstdlib> +#include <iostream> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +using boost::asio::ip::tcp; + +class server +{ +public: + server(boost::asio::io_context& io_context, unsigned short port) + : io_context_(io_context), + signal_(io_context, SIGCHLD), + acceptor_(io_context, {tcp::v4(), port}), + socket_(io_context) + { + wait_for_signal(); + accept(); + } + +private: + void wait_for_signal() + { + signal_.async_wait( + [this](boost::system::error_code /*ec*/, int /*signo*/) + { + // Only the parent process should check for this signal. We can + // determine whether we are in the parent by checking if the acceptor + // is still open. + if (acceptor_.is_open()) + { + // Reap completed child processes so that we don't end up with + // zombies. + int status = 0; + while (waitpid(-1, &status, WNOHANG) > 0) {} + + wait_for_signal(); + } + }); + } + + void accept() + { + acceptor_.async_accept( + [this](boost::system::error_code ec, tcp::socket new_socket) + { + if (!ec) + { + // Take ownership of the newly accepted socket. + socket_ = std::move(new_socket); + + // Inform the io_context that we are about to fork. The io_context + // cleans up any internal resources, such as threads, that may + // interfere with forking. + io_context_.notify_fork(boost::asio::io_context::fork_prepare); + + if (fork() == 0) + { + // Inform the io_context that the fork is finished and that this + // is the child process. The io_context uses this opportunity to + // create any internal file descriptors that must be private to + // the new process. + io_context_.notify_fork(boost::asio::io_context::fork_child); + + // The child won't be accepting new connections, so we can close + // the acceptor. It remains open in the parent. + acceptor_.close(); + + // The child process is not interested in processing the SIGCHLD + // signal. + signal_.cancel(); + + read(); + } + else + { + + // Inform the io_context that the fork is finished (or failed) + // and that this is the parent process. The io_context uses this + // opportunity to recreate any internal resources that were + // cleaned up during preparation for the fork. + io_context_.notify_fork(boost::asio::io_context::fork_parent); + + // The parent process can now close the newly accepted socket. It + // remains open in the child. + socket_.close(); + + accept(); + } + } + else + { + std::cerr << "Accept error: " << ec.message() << std::endl; + accept(); + } + }); + } + + void read() + { + socket_.async_read_some(boost::asio::buffer(data_), + [this](boost::system::error_code ec, std::size_t length) + { + if (!ec) + write(length); + }); + } + + void write(std::size_t length) + { + boost::asio::async_write(socket_, boost::asio::buffer(data_, length), + [this](boost::system::error_code ec, std::size_t /*length*/) + { + if (!ec) + read(); + }); + } + + boost::asio::io_context& io_context_; + boost::asio::signal_set signal_; + tcp::acceptor acceptor_; + tcp::socket socket_; + std::array<char, 1024> data_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: process_per_connection <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + using namespace std; // For atoi. + server s(io_context, atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + } +} diff --git a/src/boost/libs/asio/example/cpp11/futures/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/futures/Jamfile.v2 new file mode 100644 index 000000000..59c8a374b --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/futures/Jamfile.v2 @@ -0,0 +1,29 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe server + : daytime_client.cpp + /boost/system//boost_system + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp11/futures/daytime_client.cpp b/src/boost/libs/asio/example/cpp11/futures/daytime_client.cpp new file mode 100644 index 000000000..7f787ed0a --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/futures/daytime_client.cpp @@ -0,0 +1,94 @@ +// +// daytime_client.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <array> +#include <future> +#include <iostream> +#include <thread> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/udp.hpp> +#include <boost/asio/use_future.hpp> + +using boost::asio::ip::udp; + +void get_daytime(boost::asio::io_context& io_context, const char* hostname) +{ + try + { + udp::resolver resolver(io_context); + + std::future<udp::resolver::results_type> endpoints = + resolver.async_resolve( + udp::v4(), hostname, "daytime", + boost::asio::use_future); + + // The async_resolve operation above returns the endpoints as a future + // value that is not retrieved ... + + udp::socket socket(io_context, udp::v4()); + + std::array<char, 1> send_buf = {{ 0 }}; + std::future<std::size_t> send_length = + socket.async_send_to(boost::asio::buffer(send_buf), + *endpoints.get().begin(), // ... until here. This call may block. + boost::asio::use_future); + + // Do other things here while the send completes. + + send_length.get(); // Blocks until the send is complete. Throws any errors. + + std::array<char, 128> recv_buf; + udp::endpoint sender_endpoint; + std::future<std::size_t> recv_length = + socket.async_receive_from( + boost::asio::buffer(recv_buf), + sender_endpoint, + boost::asio::use_future); + + // Do other things here while the receive completes. + + std::cout.write( + recv_buf.data(), + recv_length.get()); // Blocks until receive is complete. + } + catch (std::system_error& e) + { + std::cerr << e.what() << std::endl; + } +} + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: daytime_client <host>" << std::endl; + return 1; + } + + // We run the io_context off in its own thread so that it operates + // completely asynchronously with respect to the rest of the program. + boost::asio::io_context io_context; + auto work = boost::asio::make_work_guard(io_context); + std::thread thread([&io_context](){ io_context.run(); }); + + get_daytime(io_context, argv[1]); + + io_context.stop(); + thread.join(); + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/handler_tracking/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/handler_tracking/Jamfile.v2 new file mode 100644 index 000000000..20eb3aae1 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/handler_tracking/Jamfile.v2 @@ -0,0 +1,31 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe async_tcp_echo_server + : async_tcp_echo_server.cpp + /boost/system//boost_system + : <include>. + <define>BOOST_ALL_NO_LIB=1 + <define>BOOST_ASIO_CUSTOM_HANDLER_TRACKING=\\\"custom_tracking.hpp\\\" + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp11/handler_tracking/async_tcp_echo_server.cpp b/src/boost/libs/asio/example/cpp11/handler_tracking/async_tcp_echo_server.cpp new file mode 100644 index 000000000..f0c674e18 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/handler_tracking/async_tcp_echo_server.cpp @@ -0,0 +1,114 @@ +// +// async_tcp_echo_server.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <iostream> +#include <memory> +#include <utility> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +class session + : public std::enable_shared_from_this<session> +{ +public: + session(tcp::socket socket) + : socket_(std::move(socket)) + { + } + + void start() + { + do_read(); + } + +private: + void do_read() + { + auto self(shared_from_this()); + socket_.async_read_some(boost::asio::buffer(data_, max_length), + [this, self](boost::system::error_code ec, std::size_t length) + { + if (!ec) + { + do_write(length); + } + }); + } + + void do_write(std::size_t length) + { + auto self(shared_from_this()); + boost::asio::async_write(socket_, boost::asio::buffer(data_, length), + [this, self](boost::system::error_code ec, std::size_t /*length*/) + { + if (!ec) + { + do_read(); + } + }); + } + + tcp::socket socket_; + enum { max_length = 1024 }; + char data_[max_length]; +}; + +class server +{ +public: + server(boost::asio::io_context& io_context, short port) + : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) + { + do_accept(); + } + +private: + void do_accept() + { + acceptor_.async_accept( + [this](boost::system::error_code ec, tcp::socket socket) + { + if (!ec) + { + std::make_shared<session>(std::move(socket))->start(); + } + + do_accept(); + }); + } + + tcp::acceptor acceptor_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: async_tcp_echo_server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + server s(io_context, std::atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/handler_tracking/custom_tracking.hpp b/src/boost/libs/asio/example/cpp11/handler_tracking/custom_tracking.hpp new file mode 100644 index 000000000..1bb5b6ad9 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/handler_tracking/custom_tracking.hpp @@ -0,0 +1,201 @@ +// +// custom_tracking.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef CUSTOM_TRACKING_HPP +#define CUSTOM_TRACKING_HPP + +#include <cinttypes> +#include <cstdint> +#include <cstdio> + +# define BOOST_ASIO_INHERIT_TRACKED_HANDLER \ + : public ::custom_tracking::tracked_handler + +# define BOOST_ASIO_ALSO_INHERIT_TRACKED_HANDLER \ + , public ::custom_tracking::tracked_handler + +# define BOOST_ASIO_HANDLER_TRACKING_INIT \ + ::custom_tracking::init() + +# define BOOST_ASIO_HANDLER_CREATION(args) \ + ::custom_tracking::creation args + +# define BOOST_ASIO_HANDLER_COMPLETION(args) \ + ::custom_tracking::completion tracked_completion args + +# define BOOST_ASIO_HANDLER_INVOCATION_BEGIN(args) \ + tracked_completion.invocation_begin args + +# define BOOST_ASIO_HANDLER_INVOCATION_END \ + tracked_completion.invocation_end() + +# define BOOST_ASIO_HANDLER_OPERATION(args) \ + ::custom_tracking::operation args + +# define BOOST_ASIO_HANDLER_REACTOR_REGISTRATION(args) \ + ::custom_tracking::reactor_registration args + +# define BOOST_ASIO_HANDLER_REACTOR_DEREGISTRATION(args) \ + ::custom_tracking::reactor_deregistration args + +# define BOOST_ASIO_HANDLER_REACTOR_READ_EVENT 1 +# define BOOST_ASIO_HANDLER_REACTOR_WRITE_EVENT 2 +# define BOOST_ASIO_HANDLER_REACTOR_ERROR_EVENT 4 + +# define BOOST_ASIO_HANDLER_REACTOR_EVENTS(args) \ + ::custom_tracking::reactor_events args + +# define BOOST_ASIO_HANDLER_REACTOR_OPERATION(args) \ + ::custom_tracking::reactor_operation args + +struct custom_tracking +{ + // Base class for objects containing tracked handlers. + struct tracked_handler + { + std::uintmax_t handler_id_ = 0; // To uniquely identify a handler. + std::uintmax_t tree_id_ = 0; // To identify related handlers. + const char* object_type_; // The object type associated with the handler. + std::uintmax_t native_handle_; // Native handle, if any. + }; + + // Initialise the tracking system. + static void init() + { + } + + // Record the creation of a tracked handler. + static void creation(boost::asio::execution_context& /*ctx*/, + tracked_handler& h, const char* object_type, void* /*object*/, + std::uintmax_t native_handle, const char* op_name) + { + // Generate a unique id for the new handler. + static std::atomic<std::uintmax_t> next_handler_id{1}; + h.handler_id_ = next_handler_id++; + + // Copy the tree identifier forward from the current handler. + if (*current_completion()) + h.tree_id_ = (*current_completion())->handler_.tree_id_; + + // Store various attributes of the operation to use in later output. + h.object_type_ = object_type; + h.native_handle_ = native_handle; + + std::printf( + "Starting operation %s.%s for native_handle = %" PRIuMAX + ", handler = %" PRIuMAX ", tree = %" PRIuMAX "\n", + object_type, op_name, h.native_handle_, h.handler_id_, h.tree_id_); + } + + struct completion + { + explicit completion(const tracked_handler& h) + : handler_(h), + next_(*current_completion()) + { + *current_completion() = this; + } + + completion(const completion&) = delete; + completion& operator=(const completion&) = delete; + + // Destructor records only when an exception is thrown from the handler, or + // if the memory is being freed without the handler having been invoked. + ~completion() + { + *current_completion() = next_; + } + + // Records that handler is to be invoked with the specified arguments. + template <class... Args> + void invocation_begin(Args&&... /*args*/) + { + std::printf("Entering handler %" PRIuMAX " in tree %" PRIuMAX "\n", + handler_.handler_id_, handler_.tree_id_); + } + + // Record that handler invocation has ended. + void invocation_end() + { + std::printf("Leaving handler %" PRIuMAX " in tree %" PRIuMAX "\n", + handler_.handler_id_, handler_.tree_id_); + } + + tracked_handler handler_; + + // Completions may nest. Here we stash a pointer to the outer completion. + completion* next_; + }; + + static completion** current_completion() + { + static BOOST_ASIO_THREAD_KEYWORD completion* current = nullptr; + return ¤t; + } + + // Record an operation that is not directly associated with a handler. + static void operation(boost::asio::execution_context& /*ctx*/, + const char* /*object_type*/, void* /*object*/, + std::uintmax_t /*native_handle*/, const char* /*op_name*/) + { + } + + // Record that a descriptor has been registered with the reactor. + static void reactor_registration(boost::asio::execution_context& context, + uintmax_t native_handle, uintmax_t registration) + { + std::printf("Adding to reactor native_handle = %" PRIuMAX + ", registration = %" PRIuMAX "\n", native_handle, registration); + } + + // Record that a descriptor has been deregistered from the reactor. + static void reactor_deregistration(boost::asio::execution_context& context, + uintmax_t native_handle, uintmax_t registration) + { + std::printf("Removing from reactor native_handle = %" PRIuMAX + ", registration = %" PRIuMAX "\n", native_handle, registration); + } + + // Record reactor-based readiness events associated with a descriptor. + static void reactor_events(boost::asio::execution_context& context, + uintmax_t registration, unsigned events) + { + std::printf( + "Reactor readiness for registration = %" PRIuMAX ", events =%s%s%s\n", + registration, + (events & BOOST_ASIO_HANDLER_REACTOR_READ_EVENT) ? " read" : "", + (events & BOOST_ASIO_HANDLER_REACTOR_WRITE_EVENT) ? " write" : "", + (events & BOOST_ASIO_HANDLER_REACTOR_ERROR_EVENT) ? " error" : ""); + } + + // Record a reactor-based operation that is associated with a handler. + static void reactor_operation(const tracked_handler& h, + const char* op_name, const boost::system::error_code& ec) + { + std::printf( + "Performed operation %s.%s for native_handle = %" PRIuMAX + ", ec = %s:%d\n", h.object_type_, op_name, h.native_handle_, + ec.category().name(), ec.value()); + } + + // Record a reactor-based operation that is associated with a handler. + static void reactor_operation(const tracked_handler& h, + const char* op_name, const boost::system::error_code& ec, + std::size_t bytes_transferred) + { + std::printf( + "Performed operation %s.%s for native_handle = %" PRIuMAX + ", ec = %s:%d, n = %" PRIuMAX "\n", h.object_type_, op_name, + h.native_handle_, ec.category().name(), ec.value(), + static_cast<uintmax_t>(bytes_transferred)); + } +}; + +#endif // CUSTOM_TRACKING_HPP diff --git a/src/boost/libs/asio/example/cpp11/http/server/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/http/server/Jamfile.v2 new file mode 100644 index 000000000..084b7b472 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/Jamfile.v2 @@ -0,0 +1,37 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe server + : connection.cpp + connection_manager.cpp + main.cpp + mime_types.cpp + reply.cpp + request_handler.cpp + request_parser.cpp + server.cpp + /boost/system//boost_system + /boost/thread//boost_thread + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp11/http/server/connection.cpp b/src/boost/libs/asio/example/cpp11/http/server/connection.cpp new file mode 100644 index 000000000..cdf31cc24 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/connection.cpp @@ -0,0 +1,94 @@ +// +// connection.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "connection.hpp" +#include <utility> +#include <vector> +#include "connection_manager.hpp" +#include "request_handler.hpp" + +namespace http { +namespace server { + +connection::connection(boost::asio::ip::tcp::socket socket, + connection_manager& manager, request_handler& handler) + : socket_(std::move(socket)), + connection_manager_(manager), + request_handler_(handler) +{ +} + +void connection::start() +{ + do_read(); +} + +void connection::stop() +{ + socket_.close(); +} + +void connection::do_read() +{ + auto self(shared_from_this()); + socket_.async_read_some(boost::asio::buffer(buffer_), + [this, self](boost::system::error_code ec, std::size_t bytes_transferred) + { + if (!ec) + { + request_parser::result_type result; + std::tie(result, std::ignore) = request_parser_.parse( + request_, buffer_.data(), buffer_.data() + bytes_transferred); + + if (result == request_parser::good) + { + request_handler_.handle_request(request_, reply_); + do_write(); + } + else if (result == request_parser::bad) + { + reply_ = reply::stock_reply(reply::bad_request); + do_write(); + } + else + { + do_read(); + } + } + else if (ec != boost::asio::error::operation_aborted) + { + connection_manager_.stop(shared_from_this()); + } + }); +} + +void connection::do_write() +{ + auto self(shared_from_this()); + boost::asio::async_write(socket_, reply_.to_buffers(), + [this, self](boost::system::error_code ec, std::size_t) + { + if (!ec) + { + // Initiate graceful connection closure. + boost::system::error_code ignored_ec; + socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, + ignored_ec); + } + + if (ec != boost::asio::error::operation_aborted) + { + connection_manager_.stop(shared_from_this()); + } + }); +} + +} // namespace server +} // namespace http diff --git a/src/boost/libs/asio/example/cpp11/http/server/connection.hpp b/src/boost/libs/asio/example/cpp11/http/server/connection.hpp new file mode 100644 index 000000000..83fcc0408 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/connection.hpp @@ -0,0 +1,79 @@ +// +// connection.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_CONNECTION_HPP +#define HTTP_CONNECTION_HPP + +#include <array> +#include <memory> +#include <boost/asio.hpp> +#include "reply.hpp" +#include "request.hpp" +#include "request_handler.hpp" +#include "request_parser.hpp" + +namespace http { +namespace server { + +class connection_manager; + +/// Represents a single connection from a client. +class connection + : public std::enable_shared_from_this<connection> +{ +public: + connection(const connection&) = delete; + connection& operator=(const connection&) = delete; + + /// Construct a connection with the given socket. + explicit connection(boost::asio::ip::tcp::socket socket, + connection_manager& manager, request_handler& handler); + + /// Start the first asynchronous operation for the connection. + void start(); + + /// Stop all asynchronous operations associated with the connection. + void stop(); + +private: + /// Perform an asynchronous read operation. + void do_read(); + + /// Perform an asynchronous write operation. + void do_write(); + + /// Socket for the connection. + boost::asio::ip::tcp::socket socket_; + + /// The manager for this connection. + connection_manager& connection_manager_; + + /// The handler used to process the incoming request. + request_handler& request_handler_; + + /// Buffer for incoming data. + std::array<char, 8192> buffer_; + + /// The incoming request. + request request_; + + /// The parser for the incoming request. + request_parser request_parser_; + + /// The reply to be sent back to the client. + reply reply_; +}; + +typedef std::shared_ptr<connection> connection_ptr; + +} // namespace server +} // namespace http + +#endif // HTTP_CONNECTION_HPP diff --git a/src/boost/libs/asio/example/cpp11/http/server/connection_manager.cpp b/src/boost/libs/asio/example/cpp11/http/server/connection_manager.cpp new file mode 100644 index 000000000..c07642189 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/connection_manager.cpp @@ -0,0 +1,40 @@ +// +// connection_manager.cpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "connection_manager.hpp" + +namespace http { +namespace server { + +connection_manager::connection_manager() +{ +} + +void connection_manager::start(connection_ptr c) +{ + connections_.insert(c); + c->start(); +} + +void connection_manager::stop(connection_ptr c) +{ + connections_.erase(c); + c->stop(); +} + +void connection_manager::stop_all() +{ + for (auto c: connections_) + c->stop(); + connections_.clear(); +} + +} // namespace server +} // namespace http diff --git a/src/boost/libs/asio/example/cpp11/http/server/connection_manager.hpp b/src/boost/libs/asio/example/cpp11/http/server/connection_manager.hpp new file mode 100644 index 000000000..aac32c6f0 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/connection_manager.hpp @@ -0,0 +1,48 @@ +// +// connection_manager.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_CONNECTION_MANAGER_HPP +#define HTTP_CONNECTION_MANAGER_HPP + +#include <set> +#include "connection.hpp" + +namespace http { +namespace server { + +/// Manages open connections so that they may be cleanly stopped when the server +/// needs to shut down. +class connection_manager +{ +public: + connection_manager(const connection_manager&) = delete; + connection_manager& operator=(const connection_manager&) = delete; + + /// Construct a connection manager. + connection_manager(); + + /// Add the specified connection to the manager and start it. + void start(connection_ptr c); + + /// Stop the specified connection. + void stop(connection_ptr c); + + /// Stop all connections. + void stop_all(); + +private: + /// The managed connections. + std::set<connection_ptr> connections_; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_CONNECTION_MANAGER_HPP diff --git a/src/boost/libs/asio/example/cpp11/http/server/header.hpp b/src/boost/libs/asio/example/cpp11/http/server/header.hpp new file mode 100644 index 000000000..7e176c3b0 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/header.hpp @@ -0,0 +1,28 @@ +// +// header.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_HEADER_HPP +#define HTTP_HEADER_HPP + +#include <string> + +namespace http { +namespace server { + +struct header +{ + std::string name; + std::string value; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_HEADER_HPP diff --git a/src/boost/libs/asio/example/cpp11/http/server/main.cpp b/src/boost/libs/asio/example/cpp11/http/server/main.cpp new file mode 100644 index 000000000..8236fa6b7 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/main.cpp @@ -0,0 +1,43 @@ +// +// main.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <string> +#include <boost/asio.hpp> +#include "server.hpp" + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 4) + { + std::cerr << "Usage: http_server <address> <port> <doc_root>\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " receiver 0.0.0.0 80 .\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " receiver 0::0 80 .\n"; + return 1; + } + + // Initialise the server. + http::server::server s(argv[1], argv[2], argv[3]); + + // Run the server until stopped. + s.run(); + } + catch (std::exception& e) + { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/http/server/mime_types.cpp b/src/boost/libs/asio/example/cpp11/http/server/mime_types.cpp new file mode 100644 index 000000000..806c58f9d --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/mime_types.cpp @@ -0,0 +1,45 @@ +// +// mime_types.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "mime_types.hpp" + +namespace http { +namespace server { +namespace mime_types { + +struct mapping +{ + const char* extension; + const char* mime_type; +} mappings[] = +{ + { "gif", "image/gif" }, + { "htm", "text/html" }, + { "html", "text/html" }, + { "jpg", "image/jpeg" }, + { "png", "image/png" } +}; + +std::string extension_to_type(const std::string& extension) +{ + for (mapping m: mappings) + { + if (m.extension == extension) + { + return m.mime_type; + } + } + + return "text/plain"; +} + +} // namespace mime_types +} // namespace server +} // namespace http diff --git a/src/boost/libs/asio/example/cpp11/http/server/mime_types.hpp b/src/boost/libs/asio/example/cpp11/http/server/mime_types.hpp new file mode 100644 index 000000000..e9bb88268 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/mime_types.hpp @@ -0,0 +1,27 @@ +// +// mime_types.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_MIME_TYPES_HPP +#define HTTP_MIME_TYPES_HPP + +#include <string> + +namespace http { +namespace server { +namespace mime_types { + +/// Convert a file extension into a MIME type. +std::string extension_to_type(const std::string& extension); + +} // namespace mime_types +} // namespace server +} // namespace http + +#endif // HTTP_MIME_TYPES_HPP diff --git a/src/boost/libs/asio/example/cpp11/http/server/reply.cpp b/src/boost/libs/asio/example/cpp11/http/server/reply.cpp new file mode 100644 index 000000000..1ff869f2d --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/reply.cpp @@ -0,0 +1,255 @@ +// +// reply.cpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "reply.hpp" +#include <string> + +namespace http { +namespace server { + +namespace status_strings { + +const std::string ok = + "HTTP/1.0 200 OK\r\n"; +const std::string created = + "HTTP/1.0 201 Created\r\n"; +const std::string accepted = + "HTTP/1.0 202 Accepted\r\n"; +const std::string no_content = + "HTTP/1.0 204 No Content\r\n"; +const std::string multiple_choices = + "HTTP/1.0 300 Multiple Choices\r\n"; +const std::string moved_permanently = + "HTTP/1.0 301 Moved Permanently\r\n"; +const std::string moved_temporarily = + "HTTP/1.0 302 Moved Temporarily\r\n"; +const std::string not_modified = + "HTTP/1.0 304 Not Modified\r\n"; +const std::string bad_request = + "HTTP/1.0 400 Bad Request\r\n"; +const std::string unauthorized = + "HTTP/1.0 401 Unauthorized\r\n"; +const std::string forbidden = + "HTTP/1.0 403 Forbidden\r\n"; +const std::string not_found = + "HTTP/1.0 404 Not Found\r\n"; +const std::string internal_server_error = + "HTTP/1.0 500 Internal Server Error\r\n"; +const std::string not_implemented = + "HTTP/1.0 501 Not Implemented\r\n"; +const std::string bad_gateway = + "HTTP/1.0 502 Bad Gateway\r\n"; +const std::string service_unavailable = + "HTTP/1.0 503 Service Unavailable\r\n"; + +boost::asio::const_buffer to_buffer(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return boost::asio::buffer(ok); + case reply::created: + return boost::asio::buffer(created); + case reply::accepted: + return boost::asio::buffer(accepted); + case reply::no_content: + return boost::asio::buffer(no_content); + case reply::multiple_choices: + return boost::asio::buffer(multiple_choices); + case reply::moved_permanently: + return boost::asio::buffer(moved_permanently); + case reply::moved_temporarily: + return boost::asio::buffer(moved_temporarily); + case reply::not_modified: + return boost::asio::buffer(not_modified); + case reply::bad_request: + return boost::asio::buffer(bad_request); + case reply::unauthorized: + return boost::asio::buffer(unauthorized); + case reply::forbidden: + return boost::asio::buffer(forbidden); + case reply::not_found: + return boost::asio::buffer(not_found); + case reply::internal_server_error: + return boost::asio::buffer(internal_server_error); + case reply::not_implemented: + return boost::asio::buffer(not_implemented); + case reply::bad_gateway: + return boost::asio::buffer(bad_gateway); + case reply::service_unavailable: + return boost::asio::buffer(service_unavailable); + default: + return boost::asio::buffer(internal_server_error); + } +} + +} // namespace status_strings + +namespace misc_strings { + +const char name_value_separator[] = { ':', ' ' }; +const char crlf[] = { '\r', '\n' }; + +} // namespace misc_strings + +std::vector<boost::asio::const_buffer> reply::to_buffers() +{ + std::vector<boost::asio::const_buffer> buffers; + buffers.push_back(status_strings::to_buffer(status)); + for (std::size_t i = 0; i < headers.size(); ++i) + { + header& h = headers[i]; + buffers.push_back(boost::asio::buffer(h.name)); + buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator)); + buffers.push_back(boost::asio::buffer(h.value)); + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + } + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + buffers.push_back(boost::asio::buffer(content)); + return buffers; +} + +namespace stock_replies { + +const char ok[] = ""; +const char created[] = + "<html>" + "<head><title>Created</title></head>" + "<body><h1>201 Created</h1></body>" + "</html>"; +const char accepted[] = + "<html>" + "<head><title>Accepted</title></head>" + "<body><h1>202 Accepted</h1></body>" + "</html>"; +const char no_content[] = + "<html>" + "<head><title>No Content</title></head>" + "<body><h1>204 Content</h1></body>" + "</html>"; +const char multiple_choices[] = + "<html>" + "<head><title>Multiple Choices</title></head>" + "<body><h1>300 Multiple Choices</h1></body>" + "</html>"; +const char moved_permanently[] = + "<html>" + "<head><title>Moved Permanently</title></head>" + "<body><h1>301 Moved Permanently</h1></body>" + "</html>"; +const char moved_temporarily[] = + "<html>" + "<head><title>Moved Temporarily</title></head>" + "<body><h1>302 Moved Temporarily</h1></body>" + "</html>"; +const char not_modified[] = + "<html>" + "<head><title>Not Modified</title></head>" + "<body><h1>304 Not Modified</h1></body>" + "</html>"; +const char bad_request[] = + "<html>" + "<head><title>Bad Request</title></head>" + "<body><h1>400 Bad Request</h1></body>" + "</html>"; +const char unauthorized[] = + "<html>" + "<head><title>Unauthorized</title></head>" + "<body><h1>401 Unauthorized</h1></body>" + "</html>"; +const char forbidden[] = + "<html>" + "<head><title>Forbidden</title></head>" + "<body><h1>403 Forbidden</h1></body>" + "</html>"; +const char not_found[] = + "<html>" + "<head><title>Not Found</title></head>" + "<body><h1>404 Not Found</h1></body>" + "</html>"; +const char internal_server_error[] = + "<html>" + "<head><title>Internal Server Error</title></head>" + "<body><h1>500 Internal Server Error</h1></body>" + "</html>"; +const char not_implemented[] = + "<html>" + "<head><title>Not Implemented</title></head>" + "<body><h1>501 Not Implemented</h1></body>" + "</html>"; +const char bad_gateway[] = + "<html>" + "<head><title>Bad Gateway</title></head>" + "<body><h1>502 Bad Gateway</h1></body>" + "</html>"; +const char service_unavailable[] = + "<html>" + "<head><title>Service Unavailable</title></head>" + "<body><h1>503 Service Unavailable</h1></body>" + "</html>"; + +std::string to_string(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return ok; + case reply::created: + return created; + case reply::accepted: + return accepted; + case reply::no_content: + return no_content; + case reply::multiple_choices: + return multiple_choices; + case reply::moved_permanently: + return moved_permanently; + case reply::moved_temporarily: + return moved_temporarily; + case reply::not_modified: + return not_modified; + case reply::bad_request: + return bad_request; + case reply::unauthorized: + return unauthorized; + case reply::forbidden: + return forbidden; + case reply::not_found: + return not_found; + case reply::internal_server_error: + return internal_server_error; + case reply::not_implemented: + return not_implemented; + case reply::bad_gateway: + return bad_gateway; + case reply::service_unavailable: + return service_unavailable; + default: + return internal_server_error; + } +} + +} // namespace stock_replies + +reply reply::stock_reply(reply::status_type status) +{ + reply rep; + rep.status = status; + rep.content = stock_replies::to_string(status); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = std::to_string(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = "text/html"; + return rep; +} + +} // namespace server +} // namespace http diff --git a/src/boost/libs/asio/example/cpp11/http/server/reply.hpp b/src/boost/libs/asio/example/cpp11/http/server/reply.hpp new file mode 100644 index 000000000..6a3240e2d --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/reply.hpp @@ -0,0 +1,64 @@ +// +// reply.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_REPLY_HPP +#define HTTP_REPLY_HPP + +#include <string> +#include <vector> +#include <boost/asio.hpp> +#include "header.hpp" + +namespace http { +namespace server { + +/// A reply to be sent to a client. +struct reply +{ + /// The status of the reply. + enum status_type + { + ok = 200, + created = 201, + accepted = 202, + no_content = 204, + multiple_choices = 300, + moved_permanently = 301, + moved_temporarily = 302, + not_modified = 304, + bad_request = 400, + unauthorized = 401, + forbidden = 403, + not_found = 404, + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503 + } status; + + /// The headers to be included in the reply. + std::vector<header> headers; + + /// The content to be sent in the reply. + std::string content; + + /// Convert the reply into a vector of buffers. The buffers do not own the + /// underlying memory blocks, therefore the reply object must remain valid and + /// not be changed until the write operation has completed. + std::vector<boost::asio::const_buffer> to_buffers(); + + /// Get a stock reply. + static reply stock_reply(status_type status); +}; + +} // namespace server +} // namespace http + +#endif // HTTP_REPLY_HPP diff --git a/src/boost/libs/asio/example/cpp11/http/server/request.hpp b/src/boost/libs/asio/example/cpp11/http/server/request.hpp new file mode 100644 index 000000000..358133512 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/request.hpp @@ -0,0 +1,34 @@ +// +// request.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_REQUEST_HPP +#define HTTP_REQUEST_HPP + +#include <string> +#include <vector> +#include "header.hpp" + +namespace http { +namespace server { + +/// A request received from a client. +struct request +{ + std::string method; + std::string uri; + int http_version_major; + int http_version_minor; + std::vector<header> headers; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_REQUEST_HPP diff --git a/src/boost/libs/asio/example/cpp11/http/server/request_handler.cpp b/src/boost/libs/asio/example/cpp11/http/server/request_handler.cpp new file mode 100644 index 000000000..c0ee6139c --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/request_handler.cpp @@ -0,0 +1,121 @@ +// +// request_handler.cpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "request_handler.hpp" +#include <fstream> +#include <sstream> +#include <string> +#include "mime_types.hpp" +#include "reply.hpp" +#include "request.hpp" + +namespace http { +namespace server { + +request_handler::request_handler(const std::string& doc_root) + : doc_root_(doc_root) +{ +} + +void request_handler::handle_request(const request& req, reply& rep) +{ + // Decode url to path. + std::string request_path; + if (!url_decode(req.uri, request_path)) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // Request path must be absolute and not contain "..". + if (request_path.empty() || request_path[0] != '/' + || request_path.find("..") != std::string::npos) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // If path ends in slash (i.e. is a directory) then add "index.html". + if (request_path[request_path.size() - 1] == '/') + { + request_path += "index.html"; + } + + // Determine the file extension. + std::size_t last_slash_pos = request_path.find_last_of("/"); + std::size_t last_dot_pos = request_path.find_last_of("."); + std::string extension; + if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos) + { + extension = request_path.substr(last_dot_pos + 1); + } + + // Open the file to send back. + std::string full_path = doc_root_ + request_path; + std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary); + if (!is) + { + rep = reply::stock_reply(reply::not_found); + return; + } + + // Fill out the reply to be sent to the client. + rep.status = reply::ok; + char buf[512]; + while (is.read(buf, sizeof(buf)).gcount() > 0) + rep.content.append(buf, is.gcount()); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = std::to_string(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = mime_types::extension_to_type(extension); +} + +bool request_handler::url_decode(const std::string& in, std::string& out) +{ + out.clear(); + out.reserve(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) + { + if (in[i] == '%') + { + if (i + 3 <= in.size()) + { + int value = 0; + std::istringstream is(in.substr(i + 1, 2)); + if (is >> std::hex >> value) + { + out += static_cast<char>(value); + i += 2; + } + else + { + return false; + } + } + else + { + return false; + } + } + else if (in[i] == '+') + { + out += ' '; + } + else + { + out += in[i]; + } + } + return true; +} + +} // namespace server +} // namespace http diff --git a/src/boost/libs/asio/example/cpp11/http/server/request_handler.hpp b/src/boost/libs/asio/example/cpp11/http/server/request_handler.hpp new file mode 100644 index 000000000..b1670c468 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/request_handler.hpp @@ -0,0 +1,47 @@ +// +// request_handler.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_REQUEST_HANDLER_HPP +#define HTTP_REQUEST_HANDLER_HPP + +#include <string> + +namespace http { +namespace server { + +struct reply; +struct request; + +/// The common handler for all incoming requests. +class request_handler +{ +public: + request_handler(const request_handler&) = delete; + request_handler& operator=(const request_handler&) = delete; + + /// Construct with a directory containing files to be served. + explicit request_handler(const std::string& doc_root); + + /// Handle a request and produce a reply. + void handle_request(const request& req, reply& rep); + +private: + /// The directory containing the files to be served. + std::string doc_root_; + + /// Perform URL-decoding on a string. Returns false if the encoding was + /// invalid. + static bool url_decode(const std::string& in, std::string& out); +}; + +} // namespace server +} // namespace http + +#endif // HTTP_REQUEST_HANDLER_HPP diff --git a/src/boost/libs/asio/example/cpp11/http/server/request_parser.cpp b/src/boost/libs/asio/example/cpp11/http/server/request_parser.cpp new file mode 100644 index 000000000..27a253201 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/request_parser.cpp @@ -0,0 +1,315 @@ +// +// request_parser.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "request_parser.hpp" +#include "request.hpp" + +namespace http { +namespace server { + +request_parser::request_parser() + : state_(method_start) +{ +} + +void request_parser::reset() +{ + state_ = method_start; +} + +request_parser::result_type request_parser::consume(request& req, char input) +{ + switch (state_) + { + case method_start: + if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return bad; + } + else + { + state_ = method; + req.method.push_back(input); + return indeterminate; + } + case method: + if (input == ' ') + { + state_ = uri; + return indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return bad; + } + else + { + req.method.push_back(input); + return indeterminate; + } + case uri: + if (input == ' ') + { + state_ = http_version_h; + return indeterminate; + } + else if (is_ctl(input)) + { + return bad; + } + else + { + req.uri.push_back(input); + return indeterminate; + } + case http_version_h: + if (input == 'H') + { + state_ = http_version_t_1; + return indeterminate; + } + else + { + return bad; + } + case http_version_t_1: + if (input == 'T') + { + state_ = http_version_t_2; + return indeterminate; + } + else + { + return bad; + } + case http_version_t_2: + if (input == 'T') + { + state_ = http_version_p; + return indeterminate; + } + else + { + return bad; + } + case http_version_p: + if (input == 'P') + { + state_ = http_version_slash; + return indeterminate; + } + else + { + return bad; + } + case http_version_slash: + if (input == '/') + { + req.http_version_major = 0; + req.http_version_minor = 0; + state_ = http_version_major_start; + return indeterminate; + } + else + { + return bad; + } + case http_version_major_start: + if (is_digit(input)) + { + req.http_version_major = req.http_version_major * 10 + input - '0'; + state_ = http_version_major; + return indeterminate; + } + else + { + return bad; + } + case http_version_major: + if (input == '.') + { + state_ = http_version_minor_start; + return indeterminate; + } + else if (is_digit(input)) + { + req.http_version_major = req.http_version_major * 10 + input - '0'; + return indeterminate; + } + else + { + return bad; + } + case http_version_minor_start: + if (is_digit(input)) + { + req.http_version_minor = req.http_version_minor * 10 + input - '0'; + state_ = http_version_minor; + return indeterminate; + } + else + { + return bad; + } + case http_version_minor: + if (input == '\r') + { + state_ = expecting_newline_1; + return indeterminate; + } + else if (is_digit(input)) + { + req.http_version_minor = req.http_version_minor * 10 + input - '0'; + return indeterminate; + } + else + { + return bad; + } + case expecting_newline_1: + if (input == '\n') + { + state_ = header_line_start; + return indeterminate; + } + else + { + return bad; + } + case header_line_start: + if (input == '\r') + { + state_ = expecting_newline_3; + return indeterminate; + } + else if (!req.headers.empty() && (input == ' ' || input == '\t')) + { + state_ = header_lws; + return indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return bad; + } + else + { + req.headers.push_back(header()); + req.headers.back().name.push_back(input); + state_ = header_name; + return indeterminate; + } + case header_lws: + if (input == '\r') + { + state_ = expecting_newline_2; + return indeterminate; + } + else if (input == ' ' || input == '\t') + { + return indeterminate; + } + else if (is_ctl(input)) + { + return bad; + } + else + { + state_ = header_value; + req.headers.back().value.push_back(input); + return indeterminate; + } + case header_name: + if (input == ':') + { + state_ = space_before_header_value; + return indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return bad; + } + else + { + req.headers.back().name.push_back(input); + return indeterminate; + } + case space_before_header_value: + if (input == ' ') + { + state_ = header_value; + return indeterminate; + } + else + { + return bad; + } + case header_value: + if (input == '\r') + { + state_ = expecting_newline_2; + return indeterminate; + } + else if (is_ctl(input)) + { + return bad; + } + else + { + req.headers.back().value.push_back(input); + return indeterminate; + } + case expecting_newline_2: + if (input == '\n') + { + state_ = header_line_start; + return indeterminate; + } + else + { + return bad; + } + case expecting_newline_3: + return (input == '\n') ? good : bad; + default: + return bad; + } +} + +bool request_parser::is_char(int c) +{ + return c >= 0 && c <= 127; +} + +bool request_parser::is_ctl(int c) +{ + return (c >= 0 && c <= 31) || (c == 127); +} + +bool request_parser::is_tspecial(int c) +{ + switch (c) + { + case '(': case ')': case '<': case '>': case '@': + case ',': case ';': case ':': case '\\': case '"': + case '/': case '[': case ']': case '?': case '=': + case '{': case '}': case ' ': case '\t': + return true; + default: + return false; + } +} + +bool request_parser::is_digit(int c) +{ + return c >= '0' && c <= '9'; +} + +} // namespace server +} // namespace http diff --git a/src/boost/libs/asio/example/cpp11/http/server/request_parser.hpp b/src/boost/libs/asio/example/cpp11/http/server/request_parser.hpp new file mode 100644 index 000000000..131b468de --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/request_parser.hpp @@ -0,0 +1,96 @@ +// +// request_parser.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_REQUEST_PARSER_HPP +#define HTTP_REQUEST_PARSER_HPP + +#include <tuple> + +namespace http { +namespace server { + +struct request; + +/// Parser for incoming requests. +class request_parser +{ +public: + /// Construct ready to parse the request method. + request_parser(); + + /// Reset to initial parser state. + void reset(); + + /// Result of parse. + enum result_type { good, bad, indeterminate }; + + /// Parse some data. The enum return value is good when a complete request has + /// been parsed, bad if the data is invalid, indeterminate when more data is + /// required. The InputIterator return value indicates how much of the input + /// has been consumed. + template <typename InputIterator> + std::tuple<result_type, InputIterator> parse(request& req, + InputIterator begin, InputIterator end) + { + while (begin != end) + { + result_type result = consume(req, *begin++); + if (result == good || result == bad) + return std::make_tuple(result, begin); + } + return std::make_tuple(indeterminate, begin); + } + +private: + /// Handle the next character of input. + result_type consume(request& req, char input); + + /// Check if a byte is an HTTP character. + static bool is_char(int c); + + /// Check if a byte is an HTTP control character. + static bool is_ctl(int c); + + /// Check if a byte is defined as an HTTP tspecial character. + static bool is_tspecial(int c); + + /// Check if a byte is a digit. + static bool is_digit(int c); + + /// The current state of the parser. + enum state + { + method_start, + method, + uri, + http_version_h, + http_version_t_1, + http_version_t_2, + http_version_p, + http_version_slash, + http_version_major_start, + http_version_major, + http_version_minor_start, + http_version_minor, + expecting_newline_1, + header_line_start, + header_lws, + header_name, + space_before_header_value, + header_value, + expecting_newline_2, + expecting_newline_3 + } state_; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_REQUEST_PARSER_HPP diff --git a/src/boost/libs/asio/example/cpp11/http/server/server.cpp b/src/boost/libs/asio/example/cpp11/http/server/server.cpp new file mode 100644 index 000000000..3e5ca4e1f --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/server.cpp @@ -0,0 +1,94 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include "server.hpp" +#include <signal.h> +#include <utility> + +namespace http { +namespace server { + +server::server(const std::string& address, const std::string& port, + const std::string& doc_root) + : io_context_(1), + signals_(io_context_), + acceptor_(io_context_), + connection_manager_(), + request_handler_(doc_root) +{ + // Register to handle the signals that indicate when the server should exit. + // It is safe to register for the same signal multiple times in a program, + // provided all registration for the specified signal is made through Asio. + signals_.add(SIGINT); + signals_.add(SIGTERM); +#if defined(SIGQUIT) + signals_.add(SIGQUIT); +#endif // defined(SIGQUIT) + + do_await_stop(); + + // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). + boost::asio::ip::tcp::resolver resolver(io_context_); + boost::asio::ip::tcp::endpoint endpoint = + *resolver.resolve(address, port).begin(); + acceptor_.open(endpoint.protocol()); + acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + acceptor_.bind(endpoint); + acceptor_.listen(); + + do_accept(); +} + +void server::run() +{ + // The io_context::run() call will block until all asynchronous operations + // have finished. While the server is running, there is always at least one + // asynchronous operation outstanding: the asynchronous accept call waiting + // for new incoming connections. + io_context_.run(); +} + +void server::do_accept() +{ + acceptor_.async_accept( + [this](boost::system::error_code ec, boost::asio::ip::tcp::socket socket) + { + // Check whether the server was stopped by a signal before this + // completion handler had a chance to run. + if (!acceptor_.is_open()) + { + return; + } + + if (!ec) + { + connection_manager_.start(std::make_shared<connection>( + std::move(socket), connection_manager_, request_handler_)); + } + + do_accept(); + }); +} + +void server::do_await_stop() +{ + signals_.async_wait( + [this](boost::system::error_code /*ec*/, int /*signo*/) + { + // The server is stopped by cancelling all outstanding asynchronous + // operations. Once all operations have finished the io_context::run() + // call will exit. + acceptor_.close(); + connection_manager_.stop_all(); + }); +} + +} // namespace server +} // namespace http diff --git a/src/boost/libs/asio/example/cpp11/http/server/server.hpp b/src/boost/libs/asio/example/cpp11/http/server/server.hpp new file mode 100644 index 000000000..9d72a340f --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/http/server/server.hpp @@ -0,0 +1,64 @@ +// +// server.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef HTTP_SERVER_HPP +#define HTTP_SERVER_HPP + +#include <boost/asio.hpp> +#include <string> +#include "connection.hpp" +#include "connection_manager.hpp" +#include "request_handler.hpp" + +namespace http { +namespace server { + +/// The top-level class of the HTTP server. +class server +{ +public: + server(const server&) = delete; + server& operator=(const server&) = delete; + + /// Construct the server to listen on the specified TCP address and port, and + /// serve up files from the given directory. + explicit server(const std::string& address, const std::string& port, + const std::string& doc_root); + + /// Run the server's io_context loop. + void run(); + +private: + /// Perform an asynchronous accept operation. + void do_accept(); + + /// Wait for a request to stop the server. + void do_await_stop(); + + /// The io_context used to perform asynchronous operations. + boost::asio::io_context io_context_; + + /// The signal_set is used to register for process termination notifications. + boost::asio::signal_set signals_; + + /// Acceptor used to listen for incoming connections. + boost::asio::ip::tcp::acceptor acceptor_; + + /// The connection manager which owns all live connections. + connection_manager connection_manager_; + + /// The handler for all incoming requests. + request_handler request_handler_; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_SERVER_HPP diff --git a/src/boost/libs/asio/example/cpp11/invocation/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/invocation/Jamfile.v2 new file mode 100644 index 000000000..d348adce7 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/invocation/Jamfile.v2 @@ -0,0 +1,29 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe prioritised_handlers + : prioritised_handlers.cpp + /boost/system//boost_system + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp11/invocation/prioritised_handlers.cpp b/src/boost/libs/asio/example/cpp11/invocation/prioritised_handlers.cpp new file mode 100644 index 000000000..9bf9c26a3 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/invocation/prioritised_handlers.cpp @@ -0,0 +1,202 @@ +// +// prioritised_handlers.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio.hpp> +#include <iostream> +#include <memory> +#include <queue> + +using boost::asio::ip::tcp; + +class handler_priority_queue : public boost::asio::execution_context +{ +public: + template <typename Function> + void add(int priority, Function function) + { + std::unique_ptr<queued_handler_base> handler( + new queued_handler<Function>( + priority, std::move(function))); + + handlers_.push(std::move(handler)); + } + + void execute_all() + { + while (!handlers_.empty()) + { + handlers_.top()->execute(); + handlers_.pop(); + } + } + + class executor + { + public: + executor(handler_priority_queue& q, int p) + : context_(q), priority_(p) + { + } + + handler_priority_queue& context() const noexcept + { + return context_; + } + + template <typename Function, typename Allocator> + void dispatch(Function f, const Allocator&) const + { + context_.add(priority_, std::move(f)); + } + + template <typename Function, typename Allocator> + void post(Function f, const Allocator&) const + { + context_.add(priority_, std::move(f)); + } + + template <typename Function, typename Allocator> + void defer(Function f, const Allocator&) const + { + context_.add(priority_, std::move(f)); + } + + void on_work_started() const noexcept {} + void on_work_finished() const noexcept {} + + bool operator==(const executor& other) const noexcept + { + return &context_ == &other.context_ && priority_ == other.priority_; + } + + bool operator!=(const executor& other) const noexcept + { + return !operator==(other); + } + + private: + handler_priority_queue& context_; + int priority_; + }; + + template <typename Handler> + boost::asio::executor_binder<Handler, executor> + wrap(int priority, Handler handler) + { + return boost::asio::bind_executor( + executor(*this, priority), std::move(handler)); + } + +private: + class queued_handler_base + { + public: + queued_handler_base(int p) + : priority_(p) + { + } + + virtual ~queued_handler_base() + { + } + + virtual void execute() = 0; + + friend bool operator<(const std::unique_ptr<queued_handler_base>& a, + const std::unique_ptr<queued_handler_base>& b) noexcept + { + return a->priority_ < b->priority_; + } + + private: + int priority_; + }; + + template <typename Function> + class queued_handler : public queued_handler_base + { + public: + queued_handler(int p, Function f) + : queued_handler_base(p), function_(std::move(f)) + { + } + + void execute() override + { + function_(); + } + + private: + Function function_; + }; + + std::priority_queue<std::unique_ptr<queued_handler_base>> handlers_; +}; + +//---------------------------------------------------------------------- + +void high_priority_handler(const boost::system::error_code& /*ec*/, + tcp::socket /*socket*/) +{ + std::cout << "High priority handler\n"; +} + +void middle_priority_handler(const boost::system::error_code& /*ec*/) +{ + std::cout << "Middle priority handler\n"; +} + +struct low_priority_handler +{ + // Make the handler a move-only type. + low_priority_handler() = default; + low_priority_handler(const low_priority_handler&) = delete; + low_priority_handler(low_priority_handler&&) = default; + + void operator()() + { + std::cout << "Low priority handler\n"; + } +}; + +int main() +{ + boost::asio::io_context io_context; + + handler_priority_queue pri_queue; + + // Post a completion handler to be run immediately. + boost::asio::post(io_context, pri_queue.wrap(0, low_priority_handler())); + + // Start an asynchronous accept that will complete immediately. + tcp::endpoint endpoint(boost::asio::ip::address_v4::loopback(), 0); + tcp::acceptor acceptor(io_context, endpoint); + tcp::socket server_socket(io_context); + acceptor.async_accept(pri_queue.wrap(100, high_priority_handler)); + tcp::socket client_socket(io_context); + client_socket.connect(acceptor.local_endpoint()); + + // Set a deadline timer to expire immediately. + boost::asio::steady_timer timer(io_context); + timer.expires_at(boost::asio::steady_timer::clock_type::time_point::min()); + timer.async_wait(pri_queue.wrap(42, middle_priority_handler)); + + while (io_context.run_one()) + { + // The custom invocation hook adds the handlers to the priority queue + // rather than executing them from within the poll_one() call. + while (io_context.poll_one()) + ; + + pri_queue.execute_all(); + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/iostreams/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/iostreams/Jamfile.v2 new file mode 100644 index 000000000..a201dfd80 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/iostreams/Jamfile.v2 @@ -0,0 +1,31 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe http_client : http_client.cpp ; diff --git a/src/boost/libs/asio/example/cpp11/iostreams/http_client.cpp b/src/boost/libs/asio/example/cpp11/iostreams/http_client.cpp new file mode 100644 index 000000000..97d5f728c --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/iostreams/http_client.cpp @@ -0,0 +1,91 @@ +// +// http_client.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <istream> +#include <ostream> +#include <string> +#include <boost/asio/ip/tcp.hpp> + +using boost::asio::ip::tcp; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cout << "Usage: http_client <server> <path>\n"; + std::cout << "Example:\n"; + std::cout << " http_client www.boost.org /LICENSE_1_0.txt\n"; + return 1; + } + + boost::asio::ip::tcp::iostream s; + + // The entire sequence of I/O operations must complete within 60 seconds. + // If an expiry occurs, the socket is automatically closed and the stream + // becomes bad. + s.expires_after(std::chrono::seconds(60)); + + // Establish a connection to the server. + s.connect(argv[1], "http"); + if (!s) + { + std::cout << "Unable to connect: " << s.error().message() << "\n"; + return 1; + } + + // Send the request. We specify the "Connection: close" header so that the + // server will close the socket after transmitting the response. This will + // allow us to treat all data up until the EOF as the content. + s << "GET " << argv[2] << " HTTP/1.0\r\n"; + s << "Host: " << argv[1] << "\r\n"; + s << "Accept: */*\r\n"; + s << "Connection: close\r\n\r\n"; + + // By default, the stream is tied with itself. This means that the stream + // automatically flush the buffered output before attempting a read. It is + // not necessary not explicitly flush the stream at this point. + + // Check that response is OK. + std::string http_version; + s >> http_version; + unsigned int status_code; + s >> status_code; + std::string status_message; + std::getline(s, status_message); + if (!s || http_version.substr(0, 5) != "HTTP/") + { + std::cout << "Invalid response\n"; + return 1; + } + if (status_code != 200) + { + std::cout << "Response returned with status code " << status_code << "\n"; + return 1; + } + + // Process the response headers, which are terminated by a blank line. + std::string header; + while (std::getline(s, header) && header != "\r") + std::cout << header << "\n"; + std::cout << "\n"; + + // Write the remaining data to output. + std::cout << s.rdbuf(); + } + catch (std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/local/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/local/Jamfile.v2 new file mode 100644 index 000000000..e231001fa --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/local/Jamfile.v2 @@ -0,0 +1,78 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe connect_pair + : connect_pair.cpp + /boost/system//boost_system + /boost/thread//boost_thread + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe iostream_client + : iostream_client.cpp + /boost/system//boost_system + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe stream_client + : stream_client.cpp + /boost/system//boost_system + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe stream_server + : stream_server.cpp + /boost/system//boost_system + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp11/local/connect_pair.cpp b/src/boost/libs/asio/example/cpp11/local/connect_pair.cpp new file mode 100644 index 000000000..1e60848ec --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/local/connect_pair.cpp @@ -0,0 +1,130 @@ +// +// connect_pair.cpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <array> +#include <iostream> +#include <string> +#include <cctype> +#include <boost/asio.hpp> +#include <boost/thread/thread.hpp> + +#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) + +using boost::asio::local::stream_protocol; + +class uppercase_filter +{ +public: + uppercase_filter(stream_protocol::socket sock) + : socket_(std::move(sock)) + { + read(); + } + +private: + void read() + { + socket_.async_read_some(boost::asio::buffer(data_), + [this](boost::system::error_code ec, std::size_t size) + { + if (!ec) + { + // Compute result. + for (std::size_t i = 0; i < size; ++i) + data_[i] = std::toupper(data_[i]); + + // Send result. + write(size); + } + else + { + throw boost::system::system_error(ec); + } + }); + } + + void write(std::size_t size) + { + boost::asio::async_write(socket_, boost::asio::buffer(data_, size), + [this](boost::system::error_code ec, std::size_t /*size*/) + { + if (!ec) + { + // Wait for request. + read(); + } + else + { + throw boost::system::system_error(ec); + } + }); + } + + stream_protocol::socket socket_; + std::array<char, 512> data_; +}; + +int main() +{ + try + { + boost::asio::io_context io_context; + + // Create a connected pair and pass one end to a filter. + stream_protocol::socket socket(io_context); + stream_protocol::socket filter_socket(io_context); + boost::asio::local::connect_pair(socket, filter_socket); + uppercase_filter filter(std::move(filter_socket)); + + // The io_context runs in a background thread to perform filtering. + boost::thread thread( + [&io_context]() + { + try + { + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception in thread: " << e.what() << "\n"; + std::exit(1); + } + }); + + for (;;) + { + // Collect request from user. + std::cout << "Enter a string: "; + std::string request; + std::getline(std::cin, request); + + // Send request to filter. + boost::asio::write(socket, boost::asio::buffer(request)); + + // Wait for reply from filter. + std::vector<char> reply(request.size()); + boost::asio::read(socket, boost::asio::buffer(reply)); + + // Show reply to user. + std::cout << "Result: "; + std::cout.write(&reply[0], request.size()); + std::cout << std::endl; + } + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + std::exit(1); + } +} + +#else // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) +# error Local sockets not available on this platform. +#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) diff --git a/src/boost/libs/asio/example/cpp11/local/iostream_client.cpp b/src/boost/libs/asio/example/cpp11/local/iostream_client.cpp new file mode 100644 index 000000000..2bc79e81b --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/local/iostream_client.cpp @@ -0,0 +1,61 @@ +// +// stream_client.cpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstring> +#include <iostream> +#include <boost/asio.hpp> + +#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) + +using boost::asio::local::stream_protocol; + +constexpr std::size_t max_length = 1024; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: iostream_client <file>\n"; + return 1; + } + + stream_protocol::endpoint ep(argv[1]); + stream_protocol::iostream s(ep); + if (!s) + { + std::cerr << "Unable to connect: " << s.error().message() << std::endl; + return 1; + } + + std::cout << "Enter message: "; + char request[max_length]; + std::cin.getline(request, max_length); + size_t length = std::strlen(request); + s << request; + + char reply[max_length]; + s.read(reply, length); + std::cout << "Reply is: "; + std::cout.write(reply, length); + std::cout << "\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} + +#else // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) +# error Local sockets not available on this platform. +#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) diff --git a/src/boost/libs/asio/example/cpp11/local/stream_client.cpp b/src/boost/libs/asio/example/cpp11/local/stream_client.cpp new file mode 100644 index 000000000..5d03adb50 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/local/stream_client.cpp @@ -0,0 +1,60 @@ +// +// stream_client.cpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <boost/asio.hpp> + +#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) + +using boost::asio::local::stream_protocol; + +constexpr std::size_t max_length = 1024; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: stream_client <file>\n"; + return 1; + } + + boost::asio::io_context io_context; + + stream_protocol::socket s(io_context); + s.connect(stream_protocol::endpoint(argv[1])); + + std::cout << "Enter message: "; + char request[max_length]; + std::cin.getline(request, max_length); + size_t request_length = std::strlen(request); + boost::asio::write(s, boost::asio::buffer(request, request_length)); + + char reply[max_length]; + size_t reply_length = boost::asio::read(s, + boost::asio::buffer(reply, request_length)); + std::cout << "Reply is: "; + std::cout.write(reply, reply_length); + std::cout << "\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} + +#else // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) +# error Local sockets not available on this platform. +#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) diff --git a/src/boost/libs/asio/example/cpp11/local/stream_server.cpp b/src/boost/libs/asio/example/cpp11/local/stream_server.cpp new file mode 100644 index 000000000..6b915029e --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/local/stream_server.cpp @@ -0,0 +1,121 @@ +// +// stream_server.cpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <array> +#include <cstdio> +#include <iostream> +#include <memory> +#include <boost/asio.hpp> + +#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) + +using boost::asio::local::stream_protocol; + +class session + : public std::enable_shared_from_this<session> +{ +public: + session(stream_protocol::socket sock) + : socket_(std::move(sock)) + { + } + + void start() + { + do_read(); + } + +private: + void do_read() + { + auto self(shared_from_this()); + socket_.async_read_some(boost::asio::buffer(data_), + [this, self](boost::system::error_code ec, std::size_t length) + { + if (!ec) + do_write(length); + }); + } + + void do_write(std::size_t length) + { + auto self(shared_from_this()); + boost::asio::async_write(socket_, + boost::asio::buffer(data_, length), + [this, self](boost::system::error_code ec, std::size_t /*length*/) + { + if (!ec) + do_read(); + }); + } + + // The socket used to communicate with the client. + stream_protocol::socket socket_; + + // Buffer used to store data received from the client. + std::array<char, 1024> data_; +}; + +class server +{ +public: + server(boost::asio::io_context& io_context, const std::string& file) + : acceptor_(io_context, stream_protocol::endpoint(file)) + { + do_accept(); + } + +private: + void do_accept() + { + acceptor_.async_accept( + [this](boost::system::error_code ec, stream_protocol::socket socket) + { + if (!ec) + { + std::make_shared<session>(std::move(socket))->start(); + } + + do_accept(); + }); + } + + stream_protocol::acceptor acceptor_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: stream_server <file>\n"; + std::cerr << "*** WARNING: existing file is removed ***\n"; + return 1; + } + + boost::asio::io_context io_context; + + std::remove(argv[1]); + server s(io_context, argv[1]); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} + +#else // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) +# error Local sockets not available on this platform. +#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) diff --git a/src/boost/libs/asio/example/cpp11/multicast/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/multicast/Jamfile.v2 new file mode 100644 index 000000000..dbfe4f47b --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/multicast/Jamfile.v2 @@ -0,0 +1,32 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe receiver : receiver.cpp ; +exe sender : sender.cpp ; diff --git a/src/boost/libs/asio/example/cpp11/multicast/receiver.cpp b/src/boost/libs/asio/example/cpp11/multicast/receiver.cpp new file mode 100644 index 000000000..7148288b8 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/multicast/receiver.cpp @@ -0,0 +1,88 @@ +// +// receiver.cpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <array> +#include <iostream> +#include <string> +#include <boost/asio.hpp> + +constexpr short multicast_port = 30001; + +class receiver +{ +public: + receiver(boost::asio::io_context& io_context, + const boost::asio::ip::address& listen_address, + const boost::asio::ip::address& multicast_address) + : socket_(io_context) + { + // Create the socket so that multiple may be bound to the same address. + boost::asio::ip::udp::endpoint listen_endpoint( + listen_address, multicast_port); + socket_.open(listen_endpoint.protocol()); + socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true)); + socket_.bind(listen_endpoint); + + // Join the multicast group. + socket_.set_option( + boost::asio::ip::multicast::join_group(multicast_address)); + + do_receive(); + } + +private: + void do_receive() + { + socket_.async_receive_from( + boost::asio::buffer(data_), sender_endpoint_, + [this](boost::system::error_code ec, std::size_t length) + { + if (!ec) + { + std::cout.write(data_.data(), length); + std::cout << std::endl; + + do_receive(); + } + }); + } + + boost::asio::ip::udp::socket socket_; + boost::asio::ip::udp::endpoint sender_endpoint_; + std::array<char, 1024> data_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: receiver <listen_address> <multicast_address>\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " receiver 0.0.0.0 239.255.0.1\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " receiver 0::0 ff31::8000:1234\n"; + return 1; + } + + boost::asio::io_context io_context; + receiver r(io_context, + boost::asio::ip::make_address(argv[1]), + boost::asio::ip::make_address(argv[2])); + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/multicast/sender.cpp b/src/boost/libs/asio/example/cpp11/multicast/sender.cpp new file mode 100644 index 000000000..92312a71a --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/multicast/sender.cpp @@ -0,0 +1,91 @@ +// +// sender.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <iostream> +#include <sstream> +#include <string> +#include <boost/asio.hpp> + +constexpr short multicast_port = 30001; +constexpr int max_message_count = 10; + +class sender +{ +public: + sender(boost::asio::io_context& io_context, + const boost::asio::ip::address& multicast_address) + : endpoint_(multicast_address, multicast_port), + socket_(io_context, endpoint_.protocol()), + timer_(io_context), + message_count_(0) + { + do_send(); + } + +private: + void do_send() + { + std::ostringstream os; + os << "Message " << message_count_++; + message_ = os.str(); + + socket_.async_send_to( + boost::asio::buffer(message_), endpoint_, + [this](boost::system::error_code ec, std::size_t /*length*/) + { + if (!ec && message_count_ < max_message_count) + do_timeout(); + }); + } + + void do_timeout() + { + timer_.expires_after(std::chrono::seconds(1)); + timer_.async_wait( + [this](boost::system::error_code ec) + { + if (!ec) + do_send(); + }); + } + +private: + boost::asio::ip::udp::endpoint endpoint_; + boost::asio::ip::udp::socket socket_; + boost::asio::steady_timer timer_; + int message_count_; + std::string message_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: sender <multicast_address>\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " sender 239.255.0.1\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " sender ff31::8000:1234\n"; + return 1; + } + + boost::asio::io_context io_context; + sender s(io_context, boost::asio::ip::make_address(argv[1])); + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/nonblocking/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/nonblocking/Jamfile.v2 new file mode 100644 index 000000000..1ef265c4f --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/nonblocking/Jamfile.v2 @@ -0,0 +1,29 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe third_party_lib + : third_party_lib.cpp + /boost/system//boost_system + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp11/nonblocking/third_party_lib.cpp b/src/boost/libs/asio/example/cpp11/nonblocking/third_party_lib.cpp new file mode 100644 index 000000000..ceea0e61e --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/nonblocking/third_party_lib.cpp @@ -0,0 +1,212 @@ +// +// third_party_lib.cpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio.hpp> +#include <array> +#include <iostream> +#include <memory> + +using boost::asio::ip::tcp; + +namespace third_party_lib { + +// Simulation of a third party library that wants to perform read and write +// operations directly on a socket. It needs to be polled to determine whether +// it requires a read or write operation, and notified when the socket is ready +// for reading or writing. +class session +{ +public: + session(tcp::socket& socket) + : socket_(socket) + { + } + + // Returns true if the third party library wants to be notified when the + // socket is ready for reading. + bool want_read() const + { + return state_ == reading; + } + + // Notify that third party library that it should perform its read operation. + void do_read(boost::system::error_code& ec) + { + if (std::size_t len = socket_.read_some(boost::asio::buffer(data_), ec)) + { + write_buffer_ = boost::asio::buffer(data_, len); + state_ = writing; + } + } + + // Returns true if the third party library wants to be notified when the + // socket is ready for writing. + bool want_write() const + { + return state_ == writing; + } + + // Notify that third party library that it should perform its write operation. + void do_write(boost::system::error_code& ec) + { + if (std::size_t len = socket_.write_some( + boost::asio::buffer(write_buffer_), ec)) + { + write_buffer_ = write_buffer_ + len; + state_ = boost::asio::buffer_size(write_buffer_) > 0 ? writing : reading; + } + } + +private: + tcp::socket& socket_; + enum { reading, writing } state_ = reading; + std::array<char, 128> data_; + boost::asio::const_buffer write_buffer_; +}; + +} // namespace third_party_lib + +// The glue between asio's sockets and the third party library. +class connection + : public std::enable_shared_from_this<connection> +{ +public: + connection(tcp::socket socket) + : socket_(std::move(socket)) + { + } + + void start() + { + // Put the socket into non-blocking mode. + socket_.non_blocking(true); + + do_operations(); + } + +private: + void do_operations() + { + auto self(shared_from_this()); + + // Start a read operation if the third party library wants one. + if (session_impl_.want_read() && !read_in_progress_) + { + read_in_progress_ = true; + socket_.async_wait(tcp::socket::wait_read, + [this, self](boost::system::error_code ec) + { + read_in_progress_ = false; + + // Notify third party library that it can perform a read. + if (!ec) + session_impl_.do_read(ec); + + // The third party library successfully performed a read on the + // socket. Start new read or write operations based on what it now + // wants. + if (!ec || ec == boost::asio::error::would_block) + do_operations(); + + // Otherwise, an error occurred. Closing the socket cancels any + // outstanding asynchronous read or write operations. The + // connection object will be destroyed automatically once those + // outstanding operations complete. + else + socket_.close(); + }); + } + + // Start a write operation if the third party library wants one. + if (session_impl_.want_write() && !write_in_progress_) + { + write_in_progress_ = true; + socket_.async_wait(tcp::socket::wait_write, + [this, self](boost::system::error_code ec) + { + write_in_progress_ = false; + + // Notify third party library that it can perform a write. + if (!ec) + session_impl_.do_write(ec); + + // The third party library successfully performed a write on the + // socket. Start new read or write operations based on what it now + // wants. + if (!ec || ec == boost::asio::error::would_block) + do_operations(); + + // Otherwise, an error occurred. Closing the socket cancels any + // outstanding asynchronous read or write operations. The + // connection object will be destroyed automatically once those + // outstanding operations complete. + else + socket_.close(); + }); + } + } + +private: + tcp::socket socket_; + third_party_lib::session session_impl_{socket_}; + bool read_in_progress_ = false; + bool write_in_progress_ = false; +}; + +class server +{ +public: + server(boost::asio::io_context& io_context, unsigned short port) + : acceptor_(io_context, {tcp::v4(), port}) + { + do_accept(); + } + +private: + void do_accept() + { + acceptor_.async_accept( + [this](boost::system::error_code ec, tcp::socket socket) + { + if (!ec) + { + std::make_shared<connection>(std::move(socket))->start(); + } + + do_accept(); + }); + } + + tcp::acceptor acceptor_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: third_party_lib <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + server s(io_context, std::atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/operations/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/operations/Jamfile.v2 new file mode 100644 index 000000000..a01b42a3a --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/operations/Jamfile.v2 @@ -0,0 +1,39 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/chrono//boost_chrono + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe composed_1 : composed_1.cpp ; +exe composed_2 : composed_2.cpp ; +exe composed_3 : composed_3.cpp ; +exe composed_4 : composed_4.cpp ; +exe composed_5 : composed_5.cpp ; +exe composed_6 : composed_6.cpp ; +exe composed_7 : composed_7.cpp ; +exe composed_8 : composed_8.cpp ; diff --git a/src/boost/libs/asio/example/cpp11/operations/composed_1.cpp b/src/boost/libs/asio/example/cpp11/operations/composed_1.cpp new file mode 100644 index 000000000..31906d006 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/operations/composed_1.cpp @@ -0,0 +1,113 @@ +// +// composed_1.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <cstring> +#include <iostream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +//------------------------------------------------------------------------------ + +// This is the simplest example of a composed asynchronous operation, where we +// simply repackage an existing operation. The asynchronous operation +// requirements are met by delegating responsibility to the underlying +// operation. + +template <typename CompletionToken> +auto async_write_message(tcp::socket& socket, + const char* message, CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is void. However, + // when the completion token is boost::asio::yield_context (used for stackful + // coroutines) the return type would be std::size_t, and when the completion + // token is boost::asio::use_future it would be std::future<std::size_t>. + -> typename boost::asio::async_result< + typename std::decay<CompletionToken>::type, + void(boost::system::error_code, std::size_t)>::return_type +{ + // When delegating to the underlying operation we must take care to perfectly + // forward the completion token. This ensures that our operation works + // correctly with move-only function objects as callbacks, as well as other + // completion token types. + return boost::asio::async_write(socket, + boost::asio::buffer(message, std::strlen(message)), + std::forward<CompletionToken>(token)); +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_message(socket, "Testing callback\r\n", + [](const boost::system::error_code& error, std::size_t n) + { + if (!error) + { + std::cout << n << " bytes transferred\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<std::size_t> f = async_write_message( + socket, "Testing future\r\n", boost::asio::use_future); + + io_context.run(); + + try + { + // Get the result of the operation. + std::size_t n = f.get(); + std::cout << n << " bytes transferred\n"; + } + catch (const std::exception& e) + { + std::cout << "Error: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp11/operations/composed_2.cpp b/src/boost/libs/asio/example/cpp11/operations/composed_2.cpp new file mode 100644 index 000000000..1d0656725 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/operations/composed_2.cpp @@ -0,0 +1,131 @@ +// +// composed_2.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <cstring> +#include <iostream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +//------------------------------------------------------------------------------ + +// This next simplest example of a composed asynchronous operation involves +// repackaging multiple operations but choosing to invoke just one of them. All +// of these underlying operations have the same completion signature. The +// asynchronous operation requirements are met by delegating responsibility to +// the underlying operations. + +template <typename CompletionToken> +auto async_write_message(tcp::socket& socket, + const char* message, bool allow_partial_write, + CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is void. However, + // when the completion token is boost::asio::yield_context (used for stackful + // coroutines) the return type would be std::size_t, and when the completion + // token is boost::asio::use_future it would be std::future<std::size_t>. + -> typename boost::asio::async_result< + typename std::decay<CompletionToken>::type, + void(boost::system::error_code, std::size_t)>::return_type +{ + // As the return type of the initiating function is deduced solely from the + // CompletionToken and completion signature, we know that two different + // asynchronous operations having the same completion signature will produce + // the same return type, when passed the same CompletionToken. This allows us + // to trivially delegate to alternate implementations. + if (allow_partial_write) + { + // When delegating to an underlying operation we must take care to + // perfectly forward the completion token. This ensures that our operation + // works correctly with move-only function objects as callbacks, as well as + // other completion token types. + return socket.async_write_some( + boost::asio::buffer(message, std::strlen(message)), + std::forward<CompletionToken>(token)); + } + else + { + // As above, we must perfectly forward the completion token when calling + // the alternate underlying operation. + return boost::asio::async_write(socket, + boost::asio::buffer(message, std::strlen(message)), + std::forward<CompletionToken>(token)); + } +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_message(socket, "Testing callback\r\n", false, + [](const boost::system::error_code& error, std::size_t n) + { + if (!error) + { + std::cout << n << " bytes transferred\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<std::size_t> f = async_write_message( + socket, "Testing future\r\n", false, boost::asio::use_future); + + io_context.run(); + + try + { + // Get the result of the operation. + std::size_t n = f.get(); + std::cout << n << " bytes transferred\n"; + } + catch (const std::exception& e) + { + std::cout << "Error: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp11/operations/composed_3.cpp b/src/boost/libs/asio/example/cpp11/operations/composed_3.cpp new file mode 100644 index 000000000..563db94fa --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/operations/composed_3.cpp @@ -0,0 +1,192 @@ +// +// composed_3.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/bind_executor.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <cstring> +#include <functional> +#include <iostream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +// NOTE: This example requires the new boost::asio::async_initiate function. For +// an example that works with the Networking TS style of completion tokens, +// please see an older version of asio. + +//------------------------------------------------------------------------------ + +// In this composed operation we repackage an existing operation, but with a +// different completion handler signature. The asynchronous operation +// requirements are met by delegating responsibility to the underlying +// operation. + +// In addition to determining the mechanism by which an asynchronous operation +// delivers its result, a completion token also determines the time when the +// operation commences. For example, when the completion token is a simple +// callback the operation commences before the initiating function returns. +// However, if the completion token's delivery mechanism uses a future, we +// might instead want to defer initiation of the operation until the returned +// future object is waited upon. +// +// To enable this, when implementing an asynchronous operation we must package +// the initiation step as a function object. +struct async_write_message_initiation +{ + // The initiation function object's call operator is passed the concrete + // completion handler produced by the completion token. This completion + // handler matches the asynchronous operation's completion handler signature, + // which in this example is: + // + // void(boost::system::error_code error) + // + // The initiation function object also receives any additional arguments + // required to start the operation. (Note: We could have instead passed these + // arguments as members in the initiaton function object. However, we should + // prefer to propagate them as function call arguments as this allows the + // completion token to optimise how they are passed. For example, a lazy + // future which defers initiation would need to make a decay-copy of the + // arguments, but when using a simple callback the arguments can be trivially + // forwarded straight through.) + template <typename CompletionHandler> + void operator()(CompletionHandler&& completion_handler, + tcp::socket& socket, const char* message) const + { + // The async_write operation has a completion handler signature of: + // + // void(boost::system::error_code error, std::size n) + // + // This differs from our operation's signature in that it is also passed + // the number of bytes transferred as an argument of type std::size_t. We + // will adapt our completion handler to async_write's completion handler + // signature by using std::bind, which drops the additional argument. + // + // However, it is essential to the correctness of our composed operation + // that we preserve the executor of the user-supplied completion handler. + // The std::bind function will not do this for us, so we must do this by + // first obtaining the completion handler's associated executor (defaulting + // to the I/O executor - in this case the executor of the socket - if the + // completion handler does not have its own) ... + auto executor = boost::asio::get_associated_executor( + completion_handler, socket.get_executor()); + + // ... and then binding this executor to our adapted completion handler + // using the boost::asio::bind_executor function. + boost::asio::async_write(socket, + boost::asio::buffer(message, std::strlen(message)), + boost::asio::bind_executor(executor, + std::bind(std::forward<CompletionHandler>( + completion_handler), std::placeholders::_1))); + } +}; + +template <typename CompletionToken> +auto async_write_message(tcp::socket& socket, + const char* message, CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is always void. + // In this example, when the completion token is boost::asio::yield_context + // (used for stackful coroutines) the return type would be also be void, as + // there is no non-error argument to the completion handler. When the + // completion token is boost::asio::use_future it would be std::future<void>. + -> typename boost::asio::async_result< + typename std::decay<CompletionToken>::type, + void(boost::system::error_code)>::return_type +{ + // The boost::asio::async_initiate function takes: + // + // - our initiation function object, + // - the completion token, + // - the completion handler signature, and + // - any additional arguments we need to initiate the operation. + // + // It then asks the completion token to create a completion handler (i.e. a + // callback) with the specified signature, and invoke the initiation function + // object with this completion handler as well as the additional arguments. + // The return value of async_initiate is the result of our operation's + // initiating function. + // + // Note that we wrap non-const reference arguments in std::reference_wrapper + // to prevent incorrect decay-copies of these objects. + return boost::asio::async_initiate< + CompletionToken, void(boost::system::error_code)>( + async_write_message_initiation(), + token, std::ref(socket), message); +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_message(socket, "Testing callback\r\n", + [](const boost::system::error_code& error) + { + if (!error) + { + std::cout << "Message sent\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<void> f = async_write_message( + socket, "Testing future\r\n", boost::asio::use_future); + + io_context.run(); + + // Get the result of the operation. + try + { + // Get the result of the operation. + f.get(); + std::cout << "Message sent\n"; + } + catch (const std::exception& e) + { + std::cout << "Error: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp11/operations/composed_4.cpp b/src/boost/libs/asio/example/cpp11/operations/composed_4.cpp new file mode 100644 index 000000000..cd70684ff --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/operations/composed_4.cpp @@ -0,0 +1,207 @@ +// +// composed_4.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/bind_executor.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <cstring> +#include <functional> +#include <iostream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +// NOTE: This example requires the new boost::asio::async_initiate function. For +// an example that works with the Networking TS style of completion tokens, +// please see an older version of asio. + +//------------------------------------------------------------------------------ + +// In this composed operation we repackage an existing operation, but with a +// different completion handler signature. We will also intercept an empty +// message as an invalid argument, and propagate the corresponding error to the +// user. The asynchronous operation requirements are met by delegating +// responsibility to the underlying operation. + +// In addition to determining the mechanism by which an asynchronous operation +// delivers its result, a completion token also determines the time when the +// operation commences. For example, when the completion token is a simple +// callback the operation commences before the initiating function returns. +// However, if the completion token's delivery mechanism uses a future, we +// might instead want to defer initiation of the operation until the returned +// future object is waited upon. +// +// To enable this, when implementing an asynchronous operation we must package +// the initiation step as a function object. +struct async_write_message_initiation +{ + // The initiation function object's call operator is passed the concrete + // completion handler produced by the completion token. This completion + // handler matches the asynchronous operation's completion handler signature, + // which in this example is: + // + // void(boost::system::error_code error) + // + // The initiation function object also receives any additional arguments + // required to start the operation. (Note: We could have instead passed these + // arguments as members in the initiaton function object. However, we should + // prefer to propagate them as function call arguments as this allows the + // completion token to optimise how they are passed. For example, a lazy + // future which defers initiation would need to make a decay-copy of the + // arguments, but when using a simple callback the arguments can be trivially + // forwarded straight through.) + template <typename CompletionHandler> + void operator()(CompletionHandler&& completion_handler, + tcp::socket& socket, const char* message) const + { + // The post operation has a completion handler signature of: + // + // void() + // + // and the async_write operation has a completion handler signature of: + // + // void(boost::system::error_code error, std::size n) + // + // Both of these operations' completion handler signatures differ from our + // operation's completion handler signature. We will adapt our completion + // handler to these signatures by using std::bind, which drops the + // additional arguments. + // + // However, it is essential to the correctness of our composed operation + // that we preserve the executor of the user-supplied completion handler. + // The std::bind function will not do this for us, so we must do this by + // first obtaining the completion handler's associated executor (defaulting + // to the I/O executor - in this case the executor of the socket - if the + // completion handler does not have its own) ... + auto executor = boost::asio::get_associated_executor( + completion_handler, socket.get_executor()); + + // ... and then binding this executor to our adapted completion handler + // using the boost::asio::bind_executor function. + std::size_t length = std::strlen(message); + if (length == 0) + { + boost::asio::post( + boost::asio::bind_executor(executor, + std::bind(std::forward<CompletionHandler>(completion_handler), + boost::asio::error::invalid_argument))); + } + else + { + boost::asio::async_write(socket, + boost::asio::buffer(message, length), + boost::asio::bind_executor(executor, + std::bind(std::forward<CompletionHandler>(completion_handler), + std::placeholders::_1))); + } + } +}; + +template <typename CompletionToken> +auto async_write_message(tcp::socket& socket, + const char* message, CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is always void. + // In this example, when the completion token is boost::asio::yield_context + // (used for stackful coroutines) the return type would be also be void, as + // there is no non-error argument to the completion handler. When the + // completion token is boost::asio::use_future it would be std::future<void>. + -> typename boost::asio::async_result< + typename std::decay<CompletionToken>::type, + void(boost::system::error_code)>::return_type +{ + // The boost::asio::async_initiate function takes: + // + // - our initiation function object, + // - the completion token, + // - the completion handler signature, and + // - any additional arguments we need to initiate the operation. + // + // It then asks the completion token to create a completion handler (i.e. a + // callback) with the specified signature, and invoke the initiation function + // object with this completion handler as well as the additional arguments. + // The return value of async_initiate is the result of our operation's + // initiating function. + // + // Note that we wrap non-const reference arguments in std::reference_wrapper + // to prevent incorrect decay-copies of these objects. + return boost::asio::async_initiate< + CompletionToken, void(boost::system::error_code)>( + async_write_message_initiation(), + token, std::ref(socket), message); +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_message(socket, "", + [](const boost::system::error_code& error) + { + if (!error) + { + std::cout << "Message sent\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<void> f = async_write_message( + socket, "", boost::asio::use_future); + + io_context.run(); + + try + { + // Get the result of the operation. + f.get(); + std::cout << "Message sent\n"; + } + catch (const std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp11/operations/composed_5.cpp b/src/boost/libs/asio/example/cpp11/operations/composed_5.cpp new file mode 100644 index 000000000..2f5f823a3 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/operations/composed_5.cpp @@ -0,0 +1,243 @@ +// +// composed_5.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <functional> +#include <iostream> +#include <memory> +#include <sstream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +// NOTE: This example requires the new boost::asio::async_initiate function. For +// an example that works with the Networking TS style of completion tokens, +// please see an older version of asio. + +//------------------------------------------------------------------------------ + +// This composed operation automatically serialises a message, using its I/O +// streams insertion operator, before sending it on the socket. To do this, it +// must allocate a buffer for the encoded message and ensure this buffer's +// validity until the underlying async_write operation completes. + +// In addition to determining the mechanism by which an asynchronous operation +// delivers its result, a completion token also determines the time when the +// operation commences. For example, when the completion token is a simple +// callback the operation commences before the initiating function returns. +// However, if the completion token's delivery mechanism uses a future, we +// might instead want to defer initiation of the operation until the returned +// future object is waited upon. +// +// To enable this, when implementing an asynchronous operation we must package +// the initiation step as a function object. +struct async_write_message_initiation +{ + // The initiation function object's call operator is passed the concrete + // completion handler produced by the completion token. This completion + // handler matches the asynchronous operation's completion handler signature, + // which in this example is: + // + // void(boost::system::error_code error) + // + // The initiation function object also receives any additional arguments + // required to start the operation. (Note: We could have instead passed these + // arguments as members in the initiaton function object. However, we should + // prefer to propagate them as function call arguments as this allows the + // completion token to optimise how they are passed. For example, a lazy + // future which defers initiation would need to make a decay-copy of the + // arguments, but when using a simple callback the arguments can be trivially + // forwarded straight through.) + template <typename CompletionHandler> + void operator()(CompletionHandler&& completion_handler, + tcp::socket& socket, std::unique_ptr<std::string> encoded_message) const + { + // In this example, the composed operation's intermediate completion + // handler is implemented as a hand-crafted function object, rather than + // using a lambda or std::bind. + struct intermediate_completion_handler + { + // The intermediate completion handler holds a reference to the socket so + // that it can obtain the I/O executor (see get_executor below). + tcp::socket& socket_; + + // The allocated buffer for the encoded message. The std::unique_ptr + // smart pointer is move-only, and as a consequence our intermediate + // completion handler is also move-only. + std::unique_ptr<std::string> encoded_message_; + + // The user-supplied completion handler. + typename std::decay<CompletionHandler>::type handler_; + + // The function call operator matches the completion signature of the + // async_write operation. + void operator()(const boost::system::error_code& error, std::size_t /*n*/) + { + // Deallocate the encoded message before calling the user-supplied + // completion handler. + encoded_message_.reset(); + + // Call the user-supplied handler with the result of the operation. + // The arguments must match the completion signature of our composed + // operation. + handler_(error); + } + + // It is essential to the correctness of our composed operation that we + // preserve the executor of the user-supplied completion handler. With a + // hand-crafted function object we can do this by defining a nested type + // executor_type and member function get_executor. These obtain the + // completion handler's associated executor, and default to the I/O + // executor - in this case the executor of the socket - if the completion + // handler does not have its own. + using executor_type = boost::asio::associated_executor_t< + typename std::decay<CompletionHandler>::type, + tcp::socket::executor_type>; + + executor_type get_executor() const noexcept + { + return boost::asio::get_associated_executor( + handler_, socket_.get_executor()); + } + + // Although not necessary for correctness, we may also preserve the + // allocator of the user-supplied completion handler. This is achieved by + // defining a nested type allocator_type and member function + // get_allocator. These obtain the completion handler's associated + // allocator, and default to std::allocator<void> if the completion + // handler does not have its own. + using allocator_type = boost::asio::associated_allocator_t< + typename std::decay<CompletionHandler>::type, + std::allocator<void>>; + + allocator_type get_allocator() const noexcept + { + return boost::asio::get_associated_allocator( + handler_, std::allocator<void>{}); + } + }; + + // Initiate the underlying async_write operation using our intermediate + // completion handler. + auto encoded_message_buffer = boost::asio::buffer(*encoded_message); + boost::asio::async_write(socket, encoded_message_buffer, + intermediate_completion_handler{socket, std::move(encoded_message), + std::forward<CompletionHandler>(completion_handler)}); + } +}; + +template <typename T, typename CompletionToken> +auto async_write_message(tcp::socket& socket, + const T& message, CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is always void. + // In this example, when the completion token is boost::asio::yield_context + // (used for stackful coroutines) the return type would be also be void, as + // there is no non-error argument to the completion handler. When the + // completion token is boost::asio::use_future it would be std::future<void>. + -> typename boost::asio::async_result< + typename std::decay<CompletionToken>::type, + void(boost::system::error_code)>::return_type +{ + // Encode the message and copy it into an allocated buffer. The buffer will + // be maintained for the lifetime of the asynchronous operation. + std::ostringstream os; + os << message; + std::unique_ptr<std::string> encoded_message(new std::string(os.str())); + + // The boost::asio::async_initiate function takes: + // + // - our initiation function object, + // - the completion token, + // - the completion handler signature, and + // - any additional arguments we need to initiate the operation. + // + // It then asks the completion token to create a completion handler (i.e. a + // callback) with the specified signature, and invoke the initiation function + // object with this completion handler as well as the additional arguments. + // The return value of async_initiate is the result of our operation's + // initiating function. + // + // Note that we wrap non-const reference arguments in std::reference_wrapper + // to prevent incorrect decay-copies of these objects. + return boost::asio::async_initiate< + CompletionToken, void(boost::system::error_code)>( + async_write_message_initiation(), token, + std::ref(socket), std::move(encoded_message)); +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_message(socket, 123456, + [](const boost::system::error_code& error) + { + if (!error) + { + std::cout << "Message sent\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<void> f = async_write_message( + socket, 654.321, boost::asio::use_future); + + io_context.run(); + + try + { + // Get the result of the operation. + f.get(); + std::cout << "Message sent\n"; + } + catch (const std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp11/operations/composed_6.cpp b/src/boost/libs/asio/example/cpp11/operations/composed_6.cpp new file mode 100644 index 000000000..d3a51fc78 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/operations/composed_6.cpp @@ -0,0 +1,302 @@ +// +// composed_6.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/executor_work_guard.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <functional> +#include <iostream> +#include <memory> +#include <sstream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +// NOTE: This example requires the new boost::asio::async_initiate function. For +// an example that works with the Networking TS style of completion tokens, +// please see an older version of asio. + +//------------------------------------------------------------------------------ + +// This composed operation shows composition of multiple underlying operations. +// It automatically serialises a message, using its I/O streams insertion +// operator, before sending it N times on the socket. To do this, it must +// allocate a buffer for the encoded message and ensure this buffer's validity +// until all underlying async_write operation complete. A one second delay is +// inserted prior to each write operation, using a steady_timer. + +// In addition to determining the mechanism by which an asynchronous operation +// delivers its result, a completion token also determines the time when the +// operation commences. For example, when the completion token is a simple +// callback the operation commences before the initiating function returns. +// However, if the completion token's delivery mechanism uses a future, we +// might instead want to defer initiation of the operation until the returned +// future object is waited upon. +// +// To enable this, when implementing an asynchronous operation we must package +// the initiation step as a function object. +struct async_write_message_initiation +{ + // The initiation function object's call operator is passed the concrete + // completion handler produced by the completion token. This completion + // handler matches the asynchronous operation's completion handler signature, + // which in this example is: + // + // void(boost::system::error_code error) + // + // The initiation function object also receives any additional arguments + // required to start the operation. (Note: We could have instead passed these + // arguments as members in the initiaton function object. However, we should + // prefer to propagate them as function call arguments as this allows the + // completion token to optimise how they are passed. For example, a lazy + // future which defers initiation would need to make a decay-copy of the + // arguments, but when using a simple callback the arguments can be trivially + // forwarded straight through.) + template <typename CompletionHandler> + void operator()(CompletionHandler&& completion_handler, tcp::socket& socket, + std::unique_ptr<std::string> encoded_message, std::size_t repeat_count, + std::unique_ptr<boost::asio::steady_timer> delay_timer) const + { + // In this example, the composed operation's intermediate completion + // handler is implemented as a hand-crafted function object. + struct intermediate_completion_handler + { + // The intermediate completion handler holds a reference to the socket as + // it is used for multiple async_write operations, as well as for + // obtaining the I/O executor (see get_executor below). + tcp::socket& socket_; + + // The allocated buffer for the encoded message. The std::unique_ptr + // smart pointer is move-only, and as a consequence our intermediate + // completion handler is also move-only. + std::unique_ptr<std::string> encoded_message_; + + // The repeat count remaining. + std::size_t repeat_count_; + + // A steady timer used for introducing a delay. + std::unique_ptr<boost::asio::steady_timer> delay_timer_; + + // To manage the cycle between the multiple underlying asychronous + // operations, our intermediate completion handler is implemented as a + // state machine. + enum { starting, waiting, writing } state_; + + // As our composed operation performs multiple underlying I/O operations, + // we should maintain a work object against the I/O executor. This tells + // the I/O executor that there is still more work to come in the future. + boost::asio::executor_work_guard<tcp::socket::executor_type> io_work_; + + // The user-supplied completion handler, called once only on completion + // of the entire composed operation. + typename std::decay<CompletionHandler>::type handler_; + + // By having a default value for the second argument, this function call + // operator matches the completion signature of both the async_write and + // steady_timer::async_wait operations. + void operator()(const boost::system::error_code& error, std::size_t = 0) + { + if (!error) + { + switch (state_) + { + case starting: + case writing: + if (repeat_count_ > 0) + { + --repeat_count_; + state_ = waiting; + delay_timer_->expires_after(std::chrono::seconds(1)); + delay_timer_->async_wait(std::move(*this)); + return; // Composed operation not yet complete. + } + break; // Composed operation complete, continue below. + case waiting: + state_ = writing; + boost::asio::async_write(socket_, + boost::asio::buffer(*encoded_message_), std::move(*this)); + return; // Composed operation not yet complete. + } + } + + // This point is reached only on completion of the entire composed + // operation. + + // We no longer have any future work coming for the I/O executor. + io_work_.reset(); + + // Deallocate the encoded message before calling the user-supplied + // completion handler. + encoded_message_.reset(); + + // Call the user-supplied handler with the result of the operation. + handler_(error); + } + + // It is essential to the correctness of our composed operation that we + // preserve the executor of the user-supplied completion handler. With a + // hand-crafted function object we can do this by defining a nested type + // executor_type and member function get_executor. These obtain the + // completion handler's associated executor, and default to the I/O + // executor - in this case the executor of the socket - if the completion + // handler does not have its own. + using executor_type = boost::asio::associated_executor_t< + typename std::decay<CompletionHandler>::type, + tcp::socket::executor_type>; + + executor_type get_executor() const noexcept + { + return boost::asio::get_associated_executor( + handler_, socket_.get_executor()); + } + + // Although not necessary for correctness, we may also preserve the + // allocator of the user-supplied completion handler. This is achieved by + // defining a nested type allocator_type and member function + // get_allocator. These obtain the completion handler's associated + // allocator, and default to std::allocator<void> if the completion + // handler does not have its own. + using allocator_type = boost::asio::associated_allocator_t< + typename std::decay<CompletionHandler>::type, + std::allocator<void>>; + + allocator_type get_allocator() const noexcept + { + return boost::asio::get_associated_allocator( + handler_, std::allocator<void>{}); + } + }; + + // Initiate the underlying async_write operation using our intermediate + // completion handler. + auto encoded_message_buffer = boost::asio::buffer(*encoded_message); + boost::asio::async_write(socket, encoded_message_buffer, + intermediate_completion_handler{ + socket, std::move(encoded_message), + repeat_count, std::move(delay_timer), + intermediate_completion_handler::starting, + boost::asio::make_work_guard(socket.get_executor()), + std::forward<CompletionHandler>(completion_handler)}); + } +}; + +template <typename T, typename CompletionToken> +auto async_write_messages(tcp::socket& socket, + const T& message, std::size_t repeat_count, + CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is always void. + // In this example, when the completion token is boost::asio::yield_context + // (used for stackful coroutines) the return type would be also be void, as + // there is no non-error argument to the completion handler. When the + // completion token is boost::asio::use_future it would be std::future<void>. + -> typename boost::asio::async_result< + typename std::decay<CompletionToken>::type, + void(boost::system::error_code)>::return_type +{ + // Encode the message and copy it into an allocated buffer. The buffer will + // be maintained for the lifetime of the composed asynchronous operation. + std::ostringstream os; + os << message; + std::unique_ptr<std::string> encoded_message(new std::string(os.str())); + + // Create a steady_timer to be used for the delay between messages. + std::unique_ptr<boost::asio::steady_timer> delay_timer( + new boost::asio::steady_timer(socket.get_executor())); + + // The boost::asio::async_initiate function takes: + // + // - our initiation function object, + // - the completion token, + // - the completion handler signature, and + // - any additional arguments we need to initiate the operation. + // + // It then asks the completion token to create a completion handler (i.e. a + // callback) with the specified signature, and invoke the initiation function + // object with this completion handler as well as the additional arguments. + // The return value of async_initiate is the result of our operation's + // initiating function. + // + // Note that we wrap non-const reference arguments in std::reference_wrapper + // to prevent incorrect decay-copies of these objects. + return boost::asio::async_initiate< + CompletionToken, void(boost::system::error_code)>( + async_write_message_initiation(), token, std::ref(socket), + std::move(encoded_message), repeat_count, std::move(delay_timer)); +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_messages(socket, "Testing callback\r\n", 5, + [](const boost::system::error_code& error) + { + if (!error) + { + std::cout << "Messages sent\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<void> f = async_write_messages( + socket, "Testing future\r\n", 5, boost::asio::use_future); + + io_context.run(); + + try + { + // Get the result of the operation. + f.get(); + std::cout << "Messages sent\n"; + } + catch (const std::exception& e) + { + std::cout << "Error: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp11/operations/composed_7.cpp b/src/boost/libs/asio/example/cpp11/operations/composed_7.cpp new file mode 100644 index 000000000..1139c2d39 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/operations/composed_7.cpp @@ -0,0 +1,222 @@ +// +// composed_7.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/compose.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <functional> +#include <iostream> +#include <memory> +#include <sstream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +// NOTE: This example requires the new boost::asio::async_compose function. For +// an example that works with the Networking TS style of completion tokens, +// please see an older version of asio. + +//------------------------------------------------------------------------------ + +// This composed operation shows composition of multiple underlying operations. +// It automatically serialises a message, using its I/O streams insertion +// operator, before sending it N times on the socket. To do this, it must +// allocate a buffer for the encoded message and ensure this buffer's validity +// until all underlying async_write operation complete. A one second delay is +// inserted prior to each write operation, using a steady_timer. + +// In this example, the composed operation's logic is implemented as a state +// machine within a hand-crafted function object. +struct async_write_messages_implementation +{ + // The implementation holds a reference to the socket as it is used for + // multiple async_write operations. + tcp::socket& socket_; + + // The allocated buffer for the encoded message. The std::unique_ptr smart + // pointer is move-only, and as a consequence our implementation is also + // move-only. + std::unique_ptr<std::string> encoded_message_; + + // The repeat count remaining. + std::size_t repeat_count_; + + // A steady timer used for introducing a delay. + std::unique_ptr<boost::asio::steady_timer> delay_timer_; + + // To manage the cycle between the multiple underlying asychronous + // operations, our implementation is a state machine. + enum { starting, waiting, writing } state_; + + // The first argument to our function object's call operator is a reference + // to the enclosing intermediate completion handler. This intermediate + // completion handler is provided for us by the boost::asio::async_compose + // function, and takes care of all the details required to implement a + // conforming asynchronous operation. When calling an underlying asynchronous + // operation, we pass it this enclosing intermediate completion handler + // as the completion token. + // + // All arguments after the first must be defaulted to allow the state machine + // to be started, as well as to allow the completion handler to match the + // completion signature of both the async_write and steady_timer::async_wait + // operations. + template <typename Self> + void operator()(Self& self, + const boost::system::error_code& error = boost::system::error_code(), + std::size_t = 0) + { + if (!error) + { + switch (state_) + { + case starting: + case writing: + if (repeat_count_ > 0) + { + --repeat_count_; + state_ = waiting; + delay_timer_->expires_after(std::chrono::seconds(1)); + delay_timer_->async_wait(std::move(self)); + return; // Composed operation not yet complete. + } + break; // Composed operation complete, continue below. + case waiting: + state_ = writing; + boost::asio::async_write(socket_, + boost::asio::buffer(*encoded_message_), std::move(self)); + return; // Composed operation not yet complete. + } + } + + // This point is reached only on completion of the entire composed + // operation. + + // Deallocate the encoded message and delay timer before calling the + // user-supplied completion handler. + encoded_message_.reset(); + delay_timer_.reset(); + + // Call the user-supplied handler with the result of the operation. + self.complete(error); + } +}; + +template <typename T, typename CompletionToken> +auto async_write_messages(tcp::socket& socket, + const T& message, std::size_t repeat_count, + CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is always void. + // In this example, when the completion token is boost::asio::yield_context + // (used for stackful coroutines) the return type would be also be void, as + // there is no non-error argument to the completion handler. When the + // completion token is boost::asio::use_future it would be std::future<void>. + -> typename boost::asio::async_result< + typename std::decay<CompletionToken>::type, + void(boost::system::error_code)>::return_type +{ + // Encode the message and copy it into an allocated buffer. The buffer will + // be maintained for the lifetime of the composed asynchronous operation. + std::ostringstream os; + os << message; + std::unique_ptr<std::string> encoded_message(new std::string(os.str())); + + // Create a steady_timer to be used for the delay between messages. + std::unique_ptr<boost::asio::steady_timer> delay_timer( + new boost::asio::steady_timer(socket.get_executor())); + + // The boost::asio::async_compose function takes: + // + // - our asynchronous operation implementation, + // - the completion token, + // - the completion handler signature, and + // - any I/O objects (or executors) used by the operation + // + // It then wraps our implementation in an intermediate completion handler + // that meets the requirements of a conforming asynchronous operation. This + // includes tracking outstanding work against the I/O executors associated + // with the operation (in this example, this is the socket's executor). + return boost::asio::async_compose< + CompletionToken, void(boost::system::error_code)>( + async_write_messages_implementation{ + socket, std::move(encoded_message), + repeat_count, std::move(delay_timer), + async_write_messages_implementation::starting}, + token, socket); +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_messages(socket, "Testing callback\r\n", 5, + [](const boost::system::error_code& error) + { + if (!error) + { + std::cout << "Messages sent\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<void> f = async_write_messages( + socket, "Testing future\r\n", 5, boost::asio::use_future); + + io_context.run(); + + try + { + // Get the result of the operation. + f.get(); + std::cout << "Messages sent\n"; + } + catch (const std::exception& e) + { + std::cout << "Error: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp11/operations/composed_8.cpp b/src/boost/libs/asio/example/cpp11/operations/composed_8.cpp new file mode 100644 index 000000000..4fb684656 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/operations/composed_8.cpp @@ -0,0 +1,217 @@ +// +// composed_8.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/compose.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <functional> +#include <iostream> +#include <memory> +#include <sstream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +// NOTE: This example requires the new boost::asio::async_compose function. For +// an example that works with the Networking TS style of completion tokens, +// please see an older version of asio. + +//------------------------------------------------------------------------------ + +// This composed operation shows composition of multiple underlying operations, +// using asio's stackless coroutines support to express the flow of control. It +// automatically serialises a message, using its I/O streams insertion +// operator, before sending it N times on the socket. To do this, it must +// allocate a buffer for the encoded message and ensure this buffer's validity +// until all underlying async_write operation complete. A one second delay is +// inserted prior to each write operation, using a steady_timer. + +#include <boost/asio/yield.hpp> + +// In this example, the composed operation's logic is implemented as a state +// machine within a hand-crafted function object. +struct async_write_messages_implementation +{ + // The implementation holds a reference to the socket as it is used for + // multiple async_write operations. + tcp::socket& socket_; + + // The allocated buffer for the encoded message. The std::unique_ptr smart + // pointer is move-only, and as a consequence our implementation is also + // move-only. + std::unique_ptr<std::string> encoded_message_; + + // The repeat count remaining. + std::size_t repeat_count_; + + // A steady timer used for introducing a delay. + std::unique_ptr<boost::asio::steady_timer> delay_timer_; + + // The coroutine state. + boost::asio::coroutine coro_; + + // The first argument to our function object's call operator is a reference + // to the enclosing intermediate completion handler. This intermediate + // completion handler is provided for us by the boost::asio::async_compose + // function, and takes care of all the details required to implement a + // conforming asynchronous operation. When calling an underlying asynchronous + // operation, we pass it this enclosing intermediate completion handler + // as the completion token. + // + // All arguments after the first must be defaulted to allow the state machine + // to be started, as well as to allow the completion handler to match the + // completion signature of both the async_write and steady_timer::async_wait + // operations. + template <typename Self> + void operator()(Self& self, + const boost::system::error_code& error = boost::system::error_code(), + std::size_t = 0) + { + reenter (coro_) + { + while (repeat_count_ > 0) + { + --repeat_count_; + + delay_timer_->expires_after(std::chrono::seconds(1)); + yield delay_timer_->async_wait(std::move(self)); + if (error) + break; + + yield boost::asio::async_write(socket_, + boost::asio::buffer(*encoded_message_), std::move(self)); + if (error) + break; + } + + // Deallocate the encoded message and delay timer before calling the + // user-supplied completion handler. + encoded_message_.reset(); + delay_timer_.reset(); + + // Call the user-supplied handler with the result of the operation. + self.complete(error); + } + } +}; + +#include <boost/asio/unyield.hpp> + +template <typename T, typename CompletionToken> +auto async_write_messages(tcp::socket& socket, + const T& message, std::size_t repeat_count, + CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is always void. + // In this example, when the completion token is boost::asio::yield_context + // (used for stackful coroutines) the return type would be also be void, as + // there is no non-error argument to the completion handler. When the + // completion token is boost::asio::use_future it would be std::future<void>. + -> typename boost::asio::async_result< + typename std::decay<CompletionToken>::type, + void(boost::system::error_code)>::return_type +{ + // Encode the message and copy it into an allocated buffer. The buffer will + // be maintained for the lifetime of the composed asynchronous operation. + std::ostringstream os; + os << message; + std::unique_ptr<std::string> encoded_message(new std::string(os.str())); + + // Create a steady_timer to be used for the delay between messages. + std::unique_ptr<boost::asio::steady_timer> delay_timer( + new boost::asio::steady_timer(socket.get_executor())); + + // The boost::asio::async_compose function takes: + // + // - our asynchronous operation implementation, + // - the completion token, + // - the completion handler signature, and + // - any I/O objects (or executors) used by the operation + // + // It then wraps our implementation in an intermediate completion handler + // that meets the requirements of a conforming asynchronous operation. This + // includes tracking outstanding work against the I/O executors associated + // with the operation (in this example, this is the socket's executor). + return boost::asio::async_compose< + CompletionToken, void(boost::system::error_code)>( + async_write_messages_implementation{socket, + std::move(encoded_message), repeat_count, + std::move(delay_timer), boost::asio::coroutine()}, + token, socket); +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_messages(socket, "Testing callback\r\n", 5, + [](const boost::system::error_code& error) + { + if (!error) + { + std::cout << "Messages sent\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<void> f = async_write_messages( + socket, "Testing future\r\n", 5, boost::asio::use_future); + + io_context.run(); + + try + { + // Get the result of the operation. + f.get(); + std::cout << "Messages sent\n"; + } + catch (const std::exception& e) + { + std::cout << "Error: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp11/socks4/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/socks4/Jamfile.v2 new file mode 100644 index 000000000..aa1492bad --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/socks4/Jamfile.v2 @@ -0,0 +1,30 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe server + : sync_client.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp11/socks4/socks4.hpp b/src/boost/libs/asio/example/cpp11/socks4/socks4.hpp new file mode 100644 index 000000000..e9d37fbb0 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/socks4/socks4.hpp @@ -0,0 +1,143 @@ +// +// socks4.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#ifndef SOCKS4_HPP +#define SOCKS4_HPP + +#include <array> +#include <string> +#include <boost/asio/buffer.hpp> +#include <boost/asio/ip/tcp.hpp> + +namespace socks4 { + +const unsigned char version = 0x04; + +class request +{ +public: + enum command_type + { + connect = 0x01, + bind = 0x02 + }; + + request(command_type cmd, const boost::asio::ip::tcp::endpoint& endpoint, + const std::string& user_id) + : version_(version), + command_(cmd), + user_id_(user_id), + null_byte_(0) + { + // Only IPv4 is supported by the SOCKS 4 protocol. + if (endpoint.protocol() != boost::asio::ip::tcp::v4()) + { + throw boost::system::system_error( + boost::asio::error::address_family_not_supported); + } + + // Convert port number to network byte order. + unsigned short port = endpoint.port(); + port_high_byte_ = (port >> 8) & 0xff; + port_low_byte_ = port & 0xff; + + // Save IP address in network byte order. + address_ = endpoint.address().to_v4().to_bytes(); + } + + std::array<boost::asio::const_buffer, 7> buffers() const + { + return + { + { + boost::asio::buffer(&version_, 1), + boost::asio::buffer(&command_, 1), + boost::asio::buffer(&port_high_byte_, 1), + boost::asio::buffer(&port_low_byte_, 1), + boost::asio::buffer(address_), + boost::asio::buffer(user_id_), + boost::asio::buffer(&null_byte_, 1) + } + }; + } + +private: + unsigned char version_; + unsigned char command_; + unsigned char port_high_byte_; + unsigned char port_low_byte_; + boost::asio::ip::address_v4::bytes_type address_; + std::string user_id_; + unsigned char null_byte_; +}; + +class reply +{ +public: + enum status_type + { + request_granted = 0x5a, + request_failed = 0x5b, + request_failed_no_identd = 0x5c, + request_failed_bad_user_id = 0x5d + }; + + reply() + : null_byte_(0), + status_() + { + } + + std::array<boost::asio::mutable_buffer, 5> buffers() + { + return + { + { + boost::asio::buffer(&null_byte_, 1), + boost::asio::buffer(&status_, 1), + boost::asio::buffer(&port_high_byte_, 1), + boost::asio::buffer(&port_low_byte_, 1), + boost::asio::buffer(address_) + } + }; + } + + bool success() const + { + return null_byte_ == 0 && status_ == request_granted; + } + + unsigned char status() const + { + return status_; + } + + boost::asio::ip::tcp::endpoint endpoint() const + { + unsigned short port = port_high_byte_; + port = (port << 8) & 0xff00; + port = port | port_low_byte_; + + boost::asio::ip::address_v4 address(address_); + + return boost::asio::ip::tcp::endpoint(address, port); + } + +private: + unsigned char null_byte_; + unsigned char status_; + unsigned char port_high_byte_; + unsigned char port_low_byte_; + boost::asio::ip::address_v4::bytes_type address_; +}; + +} // namespace socks4 + +#endif // SOCKS4_HPP diff --git a/src/boost/libs/asio/example/cpp11/socks4/sync_client.cpp b/src/boost/libs/asio/example/cpp11/socks4/sync_client.cpp new file mode 100644 index 000000000..1e8e6b713 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/socks4/sync_client.cpp @@ -0,0 +1,93 @@ +// +// sync_client.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <array> +#include <iostream> +#include <iomanip> +#include <ostream> +#include <string> +#include <boost/asio.hpp> +#include "socks4.hpp" + +using boost::asio::ip::tcp; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 4) + { + std::cout << "Usage: sync_client <socks4server> <socks4port> <user>\n"; + std::cout << "Examples:\n"; + std::cout << " sync_client 127.0.0.1 1080 chris\n"; + std::cout << " sync_client localhost socks chris\n"; + return 1; + } + + boost::asio::io_context io_context; + + // Get a list of endpoints corresponding to the SOCKS 4 server name. + tcp::resolver resolver(io_context); + auto endpoints = resolver.resolve(argv[1], argv[2]); + + // Try each endpoint until we successfully establish a connection to the + // SOCKS 4 server. + tcp::socket socket(io_context); + boost::asio::connect(socket, endpoints); + + // Get an endpoint for the Boost website. This will be passed to the SOCKS + // 4 server. Explicitly specify IPv4 since SOCKS 4 does not support IPv6. + auto http_endpoint = *resolver.resolve(tcp::v4(), "www.boost.org", "http"); + + // Send the request to the SOCKS 4 server. + socks4::request socks_request( + socks4::request::connect, http_endpoint, argv[3]); + boost::asio::write(socket, socks_request.buffers()); + + // Receive a response from the SOCKS 4 server. + socks4::reply socks_reply; + boost::asio::read(socket, socks_reply.buffers()); + + // Check whether we successfully negotiated with the SOCKS 4 server. + if (!socks_reply.success()) + { + std::cout << "Connection failed.\n"; + std::cout << "status = 0x" << std::hex << socks_reply.status(); + return 1; + } + + // Form the HTTP request. We specify the "Connection: close" header so that + // the server will close the socket after transmitting the response. This + // will allow us to treat all data up until the EOF as the response. + std::string request = + "GET / HTTP/1.0\r\n" + "Host: www.boost.org\r\n" + "Accept: */*\r\n" + "Connection: close\r\n\r\n"; + + // Send the HTTP request. + boost::asio::write(socket, boost::asio::buffer(request)); + + // Read until EOF, writing data to output as we go. + std::array<char, 512> response; + boost::system::error_code error; + while (std::size_t s = socket.read_some( + boost::asio::buffer(response), error)) + std::cout.write(response.data(), s); + if (error != boost::asio::error::eof) + throw std::system_error(error); + } + catch (std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/spawn/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/spawn/Jamfile.v2 new file mode 100644 index 000000000..573fb124e --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/spawn/Jamfile.v2 @@ -0,0 +1,31 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe server + : echo_server.cpp + /boost/context//boost_context + /boost/coroutine//boost_coroutine + /boost/system//boost_system + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp11/spawn/echo_server.cpp b/src/boost/libs/asio/example/cpp11/spawn/echo_server.cpp new file mode 100644 index 000000000..5fb6bbbfe --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/spawn/echo_server.cpp @@ -0,0 +1,111 @@ +// +// echo_server.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/spawn.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/asio/write.hpp> +#include <iostream> +#include <memory> + +using boost::asio::ip::tcp; + +class session : public std::enable_shared_from_this<session> +{ +public: + explicit session(boost::asio::io_context& io_context, tcp::socket socket) + : socket_(std::move(socket)), + timer_(io_context), + strand_(io_context.get_executor()) + { + } + + void go() + { + auto self(shared_from_this()); + boost::asio::spawn(strand_, + [this, self](boost::asio::yield_context yield) + { + try + { + char data[128]; + for (;;) + { + timer_.expires_from_now(std::chrono::seconds(10)); + std::size_t n = socket_.async_read_some(boost::asio::buffer(data), yield); + boost::asio::async_write(socket_, boost::asio::buffer(data, n), yield); + } + } + catch (std::exception& e) + { + socket_.close(); + timer_.cancel(); + } + }); + + boost::asio::spawn(strand_, + [this, self](boost::asio::yield_context yield) + { + while (socket_.is_open()) + { + boost::system::error_code ignored_ec; + timer_.async_wait(yield[ignored_ec]); + if (timer_.expires_from_now() <= std::chrono::seconds(0)) + socket_.close(); + } + }); + } + +private: + tcp::socket socket_; + boost::asio::steady_timer timer_; + boost::asio::strand<boost::asio::io_context::executor_type> strand_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: echo_server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + boost::asio::spawn(io_context, + [&](boost::asio::yield_context yield) + { + tcp::acceptor acceptor(io_context, + tcp::endpoint(tcp::v4(), std::atoi(argv[1]))); + + for (;;) + { + boost::system::error_code ec; + tcp::socket socket(io_context); + acceptor.async_accept(socket, yield[ec]); + if (!ec) + { + std::make_shared<session>(io_context, std::move(socket))->go(); + } + } + }); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/spawn/parallel_grep.cpp b/src/boost/libs/asio/example/cpp11/spawn/parallel_grep.cpp new file mode 100644 index 000000000..2c17131a5 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/spawn/parallel_grep.cpp @@ -0,0 +1,84 @@ +// +// parallel_grep.cpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/dispatch.hpp> +#include <boost/asio/post.hpp> +#include <boost/asio/spawn.hpp> +#include <boost/asio/strand.hpp> +#include <boost/asio/thread_pool.hpp> +#include <fstream> +#include <iostream> +#include <string> + +using boost::asio::dispatch; +using boost::asio::spawn; +using boost::asio::strand; +using boost::asio::thread_pool; +using boost::asio::yield_context; + +int main(int argc, char* argv[]) +{ + try + { + if (argc < 2) + { + std::cerr << "Usage: parallel_grep <string> <files...>\n"; + return 1; + } + + // We use a fixed size pool of threads for reading the input files. The + // number of threads is automatically determined based on the number of + // CPUs available in the system. + thread_pool pool; + + // To prevent the output from being garbled, we use a strand to synchronise + // printing. + strand<thread_pool::executor_type> output_strand(pool.get_executor()); + + // Spawn a new coroutine for each file specified on the command line. + std::string search_string = argv[1]; + for (int argn = 2; argn < argc; ++argn) + { + std::string input_file = argv[argn]; + spawn(pool, + [=](yield_context yield) + { + std::ifstream is(input_file.c_str()); + std::string line; + std::size_t line_num = 0; + while (std::getline(is, line)) + { + // If we find a match, send a message to the output. + if (line.find(search_string) != std::string::npos) + { + dispatch(output_strand, + [=] + { + std::cout << input_file << ':' << line << std::endl; + }); + } + + // Every so often we yield control to another coroutine. + if (++line_num % 10 == 0) + post(yield); + } + }); + } + + // Join the thread pool to wait for all the spawned tasks to complete. + pool.join(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/ssl/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/ssl/Jamfile.v2 new file mode 100644 index 000000000..a7805abb8 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/ssl/Jamfile.v2 @@ -0,0 +1,48 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +import os ; + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +if [ os.name ] = NT +{ + lib ssl : : <name>ssleay32 ; + lib crypto : : <name>libeay32 ; +} +else +{ + lib ssl ; + lib crypto ; +} + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/chrono//boost_chrono + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + <library>ssl + <library>crypto + ; + +exe client : client.cpp ; +exe server : server.cpp ; diff --git a/src/boost/libs/asio/example/cpp11/ssl/README b/src/boost/libs/asio/example/cpp11/ssl/README new file mode 100644 index 000000000..7dea8cc9b --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/ssl/README @@ -0,0 +1,8 @@ +The passphrase for both the CA and server private keys is "test". + + +------------------------------------------------------------------------------- +Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) diff --git a/src/boost/libs/asio/example/cpp11/ssl/ca.pem b/src/boost/libs/asio/example/cpp11/ssl/ca.pem new file mode 100644 index 000000000..1ee5f2ca4 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/ssl/ca.pem @@ -0,0 +1,49 @@ +-----BEGIN CERTIFICATE----- +MIIDlzCCAn+gAwIBAgIJAMJYU3U6A0IRMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNV +BAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChME +YXNpbzAeFw0xNTExMTgyMjMzNDhaFw0yMDExMTYyMjMzNDhaMDsxCzAJBgNVBAYT +AkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChMEYXNp +bzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMcRJocHdVMdLUJ/pypY +QVSTC0t3IIgjwjazrK3kAaoIMvzPmDFxEXWcDx+nyz8kQ/E38Ir/ef2BCNGci5hu +wkfMSuMoW9l2N4hx3QCcF46tTDEZztFxWAH7QbE2wYMlMgKZSxWimNfq0YjxEEXb +QM0lGPLFh7Xoko29H0F3LKaaQV9u/vop3Hs0h12HeWlY4PiLp7QQTNGqbWcXycA0 +NZ/fyismireyEvPAgo6L8iXuAi7g0TVKVNlrticGGjMcMq6IMvxzEpSMkuMQ5rWj +pZjWOoBjSYBuXdblcBRvXhOr2Ws8jJLMZfehKq9q1reQfoGV6xMnbwmumSXbWRWT +0vkCAwEAAaOBnTCBmjAdBgNVHQ4EFgQUK/Zv/AVtfIeucJw8VEtux1dhI1YwawYD +VR0jBGQwYoAUK/Zv/AVtfIeucJw8VEtux1dhI1ahP6Q9MDsxCzAJBgNVBAYTAkFV +MQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChMEYXNpb4IJ +AMJYU3U6A0IRMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBABLYXimq +v/HLyIJi7Xn8AJUsICj8LKF/J24nwwiF+ibf7UkoChJURs4nN78bod/lpDVPTEVl +gTBdV/vBJs416sCEFfsGjqB9OBYj4gb0VaJDsQd0+NMvXp0faKv2y9wgScxG9/cg +aM7eRmyfMn1qjb6tpNxVOPpe/nFi8Vx/1orejBRaZr4zF5TkoPepfwLWQeXDUIdE ++QHZ60jZAkR5RXTVU4u3kOKcJs839pmJYyxM4H2VxpR18vy4/YdIVWkREIUM2OgT +5iznIQIIgR56QRGP85uef+I6n0BHzrBk6du69bkQFxrFjLVGlal4bIQqSg4KGWgx +dEdymMWzmMxpO9s= +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIIEpgIBAAKCAQEAxxEmhwd1Ux0tQn+nKlhBVJMLS3cgiCPCNrOsreQBqggy/M+Y +MXERdZwPH6fLPyRD8Tfwiv95/YEI0ZyLmG7CR8xK4yhb2XY3iHHdAJwXjq1MMRnO +0XFYAftBsTbBgyUyAplLFaKY1+rRiPEQRdtAzSUY8sWHteiSjb0fQXcspppBX27+ ++incezSHXYd5aVjg+IuntBBM0aptZxfJwDQ1n9/KKyaKt7IS88CCjovyJe4CLuDR +NUpU2Wu2JwYaMxwyrogy/HMSlIyS4xDmtaOlmNY6gGNJgG5d1uVwFG9eE6vZazyM +ksxl96Eqr2rWt5B+gZXrEydvCa6ZJdtZFZPS+QIDAQABAoIBAQCOma+SvPoDzvvU +DiPOxqgOEMPfjHfGbm86xl0luBalGfiEd6WbjVanfGKtF4MWOUFec+chez+FJMEP +fufVC0qrKiJfNVMOpYvEd2SMgkSx1VymM8me6WXVDYsSipn2+1cm228ZEYAR9Emj +oqQ4loaGLlP/3RaJbhBF7ruMJvXaZZQ4fZy74Z4tyRaaE1B659ua7Rjne7eNhQE8 +cR7cQDkxsNNN3LTbfLRwEc/gcDXWgLe5JlR/K4ZrdKc3lyivm+Uew3ubKs+fgkyY +kHmuI3RJGIjpnsZW0/So+pHm3b/fo6lmlhTXtNNd+tkkKn2K9ttbXT3Sc13Pc+4w +c4MLyUpdAoGBAOxTtGDpeF6U4s+GPuOCzHCwKQyzfOyCL/UTZv1UJX7Kn1FYycJH +eOjtBRtS661cGkGd1MPfjdX2VV84AmBGDUmRqJ2KfTI1NjLAEJ115ANTpmSTm3lF +UYncgbzl6aflLpjE1mgY+JTJykYeN5jhhO0r2bsdY7S+zaMCSI5NLuznAoGBANej +aMtqLg2qKoq+fUkNBHHLXelR5dBXFnKgSrTj++H4yeW9pYbl8bK3gTF3I5+dSjHW +DdC4+X09iPqY7p8vm8Gq/vgO8Bu+EnKNVr80PJSj7AzFGd6mk/CVrAzoY2XJWbAp +YFwpo1WfHjS5wBfQzBlXY7kWVB7fj32kk14PYmUfAoGBAJXfd7NGHPoOfdCSGGv8 +VV7ZuQ6+/WiYH4XS6iuaI7VHFsZmAn3dCcbeGbD8Y04r7NLUH0yhB7g7YmTihk87 +3c1cPIy8eS1QJbEFsQPK8fFSKWH7YkwEM/O0DesX+5hodaaYnkiiHXNujYLuQuAH +lV87wfcyajsEDjFkj1L/i9TdAoGBAKYfRUQv8HqmdU+doHb+iEYCHb75UMpHzQtR +YTwpxoo3V5Kdnz9lNeYwaF7rIY59ZgMunEYHumw5U6V625nW228/hF0lZOR6cUu+ +hu2WGHWKMvdDgMJ+IcpeA8WN4cUwcN+9gHZ/vUzg4CxOTSYLvLBpGnIkOXnvUGPC +vaTgxTSRAoGBAOHcuZ9hcUrPuVI1HVkjQQLu5mLZ3tz6linEbe/RCdJMK8JrRX4w +ubB7gFclMYGbLlDNAJVYkydJaCy/2NAI3rfsOda+VmDqGx6z4BbSGceHhomyU1Oo +1H7YaXsuzDkzl23HRsyp0pKJpTdghZdbVsGF8vAB8ygK3ehM233neSln +-----END RSA PRIVATE KEY----- diff --git a/src/boost/libs/asio/example/cpp11/ssl/client.cpp b/src/boost/libs/asio/example/cpp11/ssl/client.cpp new file mode 100644 index 000000000..175c85156 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/ssl/client.cpp @@ -0,0 +1,165 @@ +// +// client.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <cstring> +#include <functional> +#include <iostream> +#include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> + +using boost::asio::ip::tcp; +using std::placeholders::_1; +using std::placeholders::_2; + +enum { max_length = 1024 }; + +class client +{ +public: + client(boost::asio::io_context& io_context, + boost::asio::ssl::context& context, + const tcp::resolver::results_type& endpoints) + : socket_(io_context, context) + { + socket_.set_verify_mode(boost::asio::ssl::verify_peer); + socket_.set_verify_callback( + std::bind(&client::verify_certificate, this, _1, _2)); + + connect(endpoints); + } + +private: + bool verify_certificate(bool preverified, + boost::asio::ssl::verify_context& ctx) + { + // The verify callback can be used to check whether the certificate that is + // being presented is valid for the peer. For example, RFC 2818 describes + // the steps involved in doing this for HTTPS. Consult the OpenSSL + // documentation for more details. Note that the callback is called once + // for each certificate in the certificate chain, starting from the root + // certificate authority. + + // In this example we will simply print the certificate's subject name. + char subject_name[256]; + X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); + X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256); + std::cout << "Verifying " << subject_name << "\n"; + + return preverified; + } + + void connect(const tcp::resolver::results_type& endpoints) + { + boost::asio::async_connect(socket_.lowest_layer(), endpoints, + [this](const boost::system::error_code& error, + const tcp::endpoint& /*endpoint*/) + { + if (!error) + { + handshake(); + } + else + { + std::cout << "Connect failed: " << error.message() << "\n"; + } + }); + } + + void handshake() + { + socket_.async_handshake(boost::asio::ssl::stream_base::client, + [this](const boost::system::error_code& error) + { + if (!error) + { + send_request(); + } + else + { + std::cout << "Handshake failed: " << error.message() << "\n"; + } + }); + } + + void send_request() + { + std::cout << "Enter message: "; + std::cin.getline(request_, max_length); + size_t request_length = std::strlen(request_); + + boost::asio::async_write(socket_, + boost::asio::buffer(request_, request_length), + [this](const boost::system::error_code& error, std::size_t length) + { + if (!error) + { + receive_response(length); + } + else + { + std::cout << "Write failed: " << error.message() << "\n"; + } + }); + } + + void receive_response(std::size_t length) + { + boost::asio::async_read(socket_, + boost::asio::buffer(reply_, length), + [this](const boost::system::error_code& error, std::size_t length) + { + if (!error) + { + std::cout << "Reply: "; + std::cout.write(reply_, length); + std::cout << "\n"; + } + else + { + std::cout << "Read failed: " << error.message() << "\n"; + } + }); + } + + boost::asio::ssl::stream<tcp::socket> socket_; + char request_[max_length]; + char reply_[max_length]; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: client <host> <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + tcp::resolver resolver(io_context); + auto endpoints = resolver.resolve(argv[1], argv[2]); + + boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); + ctx.load_verify_file("ca.pem"); + + client c(io_context, ctx, endpoints); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/ssl/dh2048.pem b/src/boost/libs/asio/example/cpp11/ssl/dh2048.pem new file mode 100644 index 000000000..07250cca6 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/ssl/dh2048.pem @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEAyNnxZSYc6J89mDNnqOH8bnwBiAJxcaUS3PkIEcwW8D9o2BlNq6EO +XKMIbdfwPFZi80GMpNu3YP2A2B42sAHmb7w7ZA92QDv3JjqzR0QuS/CkMv4CEjha +QBFwBDDWnnHBSj4w/t54ii0SH34mWcjBItI2eMtnM9J6fnvNiWqJxdt4iA4mZjZD +qZTjIRyjgKAevzkqAlBqQRoVUUgu+9Cf29wXjVl3bE+0VU5CdFeyT+Y9yunz88mq +rGyx1uPt+zbIfxuNLH+coY67y1ht7iZEL5WLd3wGCycRT+lYy2AL/rxGBPxStFIT +2bOkQao6sAfb4UdGEUlwHUXZrAV51oM30wIBAg== +-----END DH PARAMETERS----- diff --git a/src/boost/libs/asio/example/cpp11/ssl/server.cpp b/src/boost/libs/asio/example/cpp11/ssl/server.cpp new file mode 100644 index 000000000..32936a674 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/ssl/server.cpp @@ -0,0 +1,143 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <functional> +#include <iostream> +#include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> + +using boost::asio::ip::tcp; + +class session : public std::enable_shared_from_this<session> +{ +public: + session(tcp::socket socket, boost::asio::ssl::context& context) + : socket_(std::move(socket), context) + { + } + + void start() + { + do_handshake(); + } + +private: + void do_handshake() + { + auto self(shared_from_this()); + socket_.async_handshake(boost::asio::ssl::stream_base::server, + [this, self](const boost::system::error_code& error) + { + if (!error) + { + do_read(); + } + }); + } + + void do_read() + { + auto self(shared_from_this()); + socket_.async_read_some(boost::asio::buffer(data_), + [this, self](const boost::system::error_code& ec, std::size_t length) + { + if (!ec) + { + do_write(length); + } + }); + } + + void do_write(std::size_t length) + { + auto self(shared_from_this()); + boost::asio::async_write(socket_, boost::asio::buffer(data_, length), + [this, self](const boost::system::error_code& ec, + std::size_t /*length*/) + { + if (!ec) + { + do_read(); + } + }); + } + + boost::asio::ssl::stream<tcp::socket> socket_; + char data_[1024]; +}; + +class server +{ +public: + server(boost::asio::io_context& io_context, unsigned short port) + : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)), + context_(boost::asio::ssl::context::sslv23) + { + context_.set_options( + boost::asio::ssl::context::default_workarounds + | boost::asio::ssl::context::no_sslv2 + | boost::asio::ssl::context::single_dh_use); + context_.set_password_callback(std::bind(&server::get_password, this)); + context_.use_certificate_chain_file("server.pem"); + context_.use_private_key_file("server.pem", boost::asio::ssl::context::pem); + context_.use_tmp_dh_file("dh2048.pem"); + + do_accept(); + } + +private: + std::string get_password() const + { + return "test"; + } + + void do_accept() + { + acceptor_.async_accept( + [this](const boost::system::error_code& error, tcp::socket socket) + { + if (!error) + { + std::make_shared<session>(std::move(socket), context_)->start(); + } + + do_accept(); + }); + } + + tcp::acceptor acceptor_; + boost::asio::ssl::context context_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: server <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + using namespace std; // For atoi. + server s(io_context, atoi(argv[1])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/ssl/server.pem b/src/boost/libs/asio/example/cpp11/ssl/server.pem new file mode 100644 index 000000000..37ea6e267 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/ssl/server.pem @@ -0,0 +1,71 @@ +-----BEGIN CERTIFICATE----- +MIIDAzCCAesCCQD9QcRiWk0y9TANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJB +VTEMMAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZTeWRuZXkxDTALBgNVBAoTBGFzaW8w +HhcNMTUxMTE4MjIzNzMxWhcNMjAxMTE2MjIzNzMxWjBMMQswCQYDVQQGEwJBVTEM +MAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZTeWRuZXkxDTALBgNVBAoTBGFzaW8xDzAN +BgNVBAsTBnNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALr0 ++NXSklsGJR7HYHP/H4V5+KpYrmFKva/K7iiqi+XyWEjGnj+/iImJW26phhg9GouN +JJxdrP7/0LwpMsEC/9v09dMNAEewtYhPgD4kiUH/E/79wVmayMZZZGrpF9Rw+wWv +q58y3L1wKge3qilX6slVDdNhqU3vBiMKEJfsjE4PKcEVjPCjVJG2562eHK9FxyjQ +DykyH61lQKBQOiElilPQKzAO7U36yTvs+chWuUfK47B8EC+PJ5KcLEppli4ljlwE +w01HnGxwvjDLobKm2jL6CWi3aYGWudyTsNAd7YC5C7psktBypQLBcfp7uUrrR5Bb +PEjFHJUWIlyoYvm2OjMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAtceVW6tixFsB +ZRhjL5aRCcbx2iMwEXd54lcP6BWe1qOcDPHoSYI1zvvGzohbEvBfqUv78S9MtzaT +gMe5rIU9M1ZM09PyaM6ZutGpKHE8L4qcOslTt41GQFsSqPFdcbgSV20MvBzjGayR +AI/WV0avW3oasdetJPZCR7bRbCbMbWTgclUfv5F25ENcR+BhNuilfL15owL0s4sS +Wb4jOOHhXV9iXeS2dH0snFqv4BmQ9ZoA7zbM9lG3EU5DuxHESYkCnzJyEqqY3vWv +PFRViCxLp5LQLmkTQ3dglVQA4x6ZaonaewdPtdhjkLUuIqDvQx5+kIaOELbSws+c +bREYlnGrFw== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,D459676347D389E9135496D8AAFA7953 + +wbrjxr9NHur8kgxDsgXOY9qFGKpONIQLxkuahUrDD/H+s/l7ugsLWOPsOXbjNL/7 +QYUBAx85HKm9D8BQ5g78Y82qfArap3/3IIuysDfQDh4fQodhVtmGTFiCOvudlGEp +lq1niQRLThlxeRoFphH8KKiOTO9a/d8tdL7zRmiFwnVnhK4014mgVmgcSefA1AF5 +RbJAeMclUKddG6ltQK00ptg84CDXiMWQXFBGGmQ1av2lyFzC+xLP+qDqZAYTM9lZ +NFRo2oEZP1ozfOVNSbXTanJgZ0DSSmhGE1PcVrHSeE/v+k1kPh3oVKi9GV51kIDC +Zd9f/XltuDOzy1Ybn6gRy4nzNpzcwjSCIHEdSD5nxU5JfHfQ3OtnsEab7qf989iP +s2LbCSp5uGTMvfesMIkixIZAQp2FeahZTAgU2Vx+wi5Kks68rOqeywEfzACL/Um5 +7XZu8gDs4MgRRWnxK1BbJDPifICLvSJZvgB9FKX/hk4FHFF+MtcrkalehCuLooDV +3rfHNvRSbg7J97XQ3QC+k9ZDaumpy6n+LhaVv7BIJRBnBBtZ5Eg3DmPg6flqaHAU +Y/8d82wb/pCmbvR3B1/Ebgs84DPJ+uZnY9M5Iwx19oqlVSR2ts/Tx619LGAm+BiQ +7YDoC4CFmpAA8Uw0xnUbNgx94NdNmlnLeLtS50b0XlWpHKbVzmVbNYEjY6NHMlLt +aqxWHTYTa7g/c1bg2/nxF1Lbfu5VSTROGBUuer1c3yzVuyBrjcX92Jp4BJH78qOp +N6lY6MnH4HYRXHjzlt/S0ZzO0faPPe18Q8SWvnDVuE3fYzzL772B56d2t8eodc+/ +t6M3qJ60eXdsmgYOaPRLRUovN2xT2UUr0+biuguHyqfaVfcEU/adw+b9oUVE+5Nw +nZHI5qhPnhLxChyZqbBl68zMUyKlfff4OyLvRGpfcHwBw6DTGjduB+DDsqqkcIB9 +2VL6nps7ZVCwMPI18siUd6cttEOf6ZXrVqHg9wfDvJOlh2NNKNLxSAFubHc90Jlj +KejrWenXo2w6YkSUeTV4t4cWu7U8rXIkTJXDl1S6NO8DWqNDo5KjgJ2SK5NlSOJ7 +jgECn390ooneJOxxytPVQO2xppXQZZS65RHrvhB+ss5xUknly9q+ICyt6xTR9nqA +PKkeSE6qVY0J4JgFXpkgQxgwMnjSED3LKr3jlz28pr5cC6tsc5SSlekHjT2fcSrX +uccaVahaJRigf+q+4XzmJtdwbZU+YWGZRVMlQLA5yzPHQHDYkPpOeYU4WReND8S4 +TZRkPHaxOZ2lKQwJB93V8Vbt2MvwRy392452a33S4TcQLaWzoOljXjmZjrp2rvRz +prBaNe8LnO4V8Oliv+H+E0UWiWFDuI+HBy4X4O9plsbw/gk64Phl9qLiBwaX/AIR +66FXvC/czABo9oSt2jekcMtJofYr8Gr2bsJlt5ZX+GEOxz4jMv7xvz5/L3W7jVav +pHGIv4xfN9FrXzL47O7UuUF9xZg4Rp/fxwpgEDNZmX/3DnP0ewZQUcgUX0pdqNGQ +YVqJXcRF7KqG2NSQFuwPESZQnxU0WzSgRyUae7xg1WKfSuN8NVAzKhOgeqlD2IAo +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIDlzCCAn+gAwIBAgIJAMJYU3U6A0IRMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNV +BAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChME +YXNpbzAeFw0xNTExMTgyMjMzNDhaFw0yMDExMTYyMjMzNDhaMDsxCzAJBgNVBAYT +AkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChMEYXNp +bzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMcRJocHdVMdLUJ/pypY +QVSTC0t3IIgjwjazrK3kAaoIMvzPmDFxEXWcDx+nyz8kQ/E38Ir/ef2BCNGci5hu +wkfMSuMoW9l2N4hx3QCcF46tTDEZztFxWAH7QbE2wYMlMgKZSxWimNfq0YjxEEXb +QM0lGPLFh7Xoko29H0F3LKaaQV9u/vop3Hs0h12HeWlY4PiLp7QQTNGqbWcXycA0 +NZ/fyismireyEvPAgo6L8iXuAi7g0TVKVNlrticGGjMcMq6IMvxzEpSMkuMQ5rWj +pZjWOoBjSYBuXdblcBRvXhOr2Ws8jJLMZfehKq9q1reQfoGV6xMnbwmumSXbWRWT +0vkCAwEAAaOBnTCBmjAdBgNVHQ4EFgQUK/Zv/AVtfIeucJw8VEtux1dhI1YwawYD +VR0jBGQwYoAUK/Zv/AVtfIeucJw8VEtux1dhI1ahP6Q9MDsxCzAJBgNVBAYTAkFV +MQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChMEYXNpb4IJ +AMJYU3U6A0IRMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBABLYXimq +v/HLyIJi7Xn8AJUsICj8LKF/J24nwwiF+ibf7UkoChJURs4nN78bod/lpDVPTEVl +gTBdV/vBJs416sCEFfsGjqB9OBYj4gb0VaJDsQd0+NMvXp0faKv2y9wgScxG9/cg +aM7eRmyfMn1qjb6tpNxVOPpe/nFi8Vx/1orejBRaZr4zF5TkoPepfwLWQeXDUIdE ++QHZ60jZAkR5RXTVU4u3kOKcJs839pmJYyxM4H2VxpR18vy4/YdIVWkREIUM2OgT +5iznIQIIgR56QRGP85uef+I6n0BHzrBk6du69bkQFxrFjLVGlal4bIQqSg4KGWgx +dEdymMWzmMxpO9s= +-----END CERTIFICATE----- diff --git a/src/boost/libs/asio/example/cpp11/timeouts/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/timeouts/Jamfile.v2 new file mode 100644 index 000000000..dcc83c435 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/timeouts/Jamfile.v2 @@ -0,0 +1,36 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/chrono//boost_chrono + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe async_tcp_client : async_tcp_client.cpp ; +exe blocking_tcp_client : blocking_tcp_client.cpp ; +exe blocking_token_tcp_client : blocking_token_tcp_client.cpp ; +exe blocking_udp_client : blocking_udp_client.cpp ; +exe server : server.cpp ; diff --git a/src/boost/libs/asio/example/cpp11/timeouts/async_tcp_client.cpp b/src/boost/libs/asio/example/cpp11/timeouts/async_tcp_client.cpp new file mode 100644 index 000000000..9c6719060 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/timeouts/async_tcp_client.cpp @@ -0,0 +1,311 @@ +// +// async_tcp_client.cpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/buffer.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/read_until.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/asio/write.hpp> +#include <functional> +#include <iostream> +#include <string> + +using boost::asio::steady_timer; +using boost::asio::ip::tcp; +using std::placeholders::_1; +using std::placeholders::_2; + +// +// This class manages socket timeouts by applying the concept of a deadline. +// Some asynchronous operations are given deadlines by which they must complete. +// Deadlines are enforced by an "actor" that persists for the lifetime of the +// client object: +// +// +----------------+ +// | | +// | check_deadline |<---+ +// | | | +// +----------------+ | async_wait() +// | | +// +---------+ +// +// If the deadline actor determines that the deadline has expired, the socket +// is closed and any outstanding operations are consequently cancelled. +// +// Connection establishment involves trying each endpoint in turn until a +// connection is successful, or the available endpoints are exhausted. If the +// deadline actor closes the socket, the connect actor is woken up and moves to +// the next endpoint. +// +// +---------------+ +// | | +// | start_connect |<---+ +// | | | +// +---------------+ | +// | | +// async_- | +----------------+ +// connect() | | | +// +--->| handle_connect | +// | | +// +----------------+ +// : +// Once a connection is : +// made, the connect : +// actor forks in two - : +// : +// an actor for reading : and an actor for +// inbound messages: : sending heartbeats: +// : +// +------------+ : +-------------+ +// | |<- - - - -+- - - - ->| | +// | start_read | | start_write |<---+ +// | |<---+ | | | +// +------------+ | +-------------+ | async_wait() +// | | | | +// async_- | +-------------+ async_- | +--------------+ +// read_- | | | write() | | | +// until() +--->| handle_read | +--->| handle_write | +// | | | | +// +-------------+ +--------------+ +// +// The input actor reads messages from the socket, where messages are delimited +// by the newline character. The deadline for a complete message is 30 seconds. +// +// The heartbeat actor sends a heartbeat (a message that consists of a single +// newline character) every 10 seconds. In this example, no deadline is applied +// to message sending. +// +class client +{ +public: + client(boost::asio::io_context& io_context) + : socket_(io_context), + deadline_(io_context), + heartbeat_timer_(io_context) + { + } + + // Called by the user of the client class to initiate the connection process. + // The endpoints will have been obtained using a tcp::resolver. + void start(tcp::resolver::results_type endpoints) + { + // Start the connect actor. + endpoints_ = endpoints; + start_connect(endpoints_.begin()); + + // Start the deadline actor. You will note that we're not setting any + // particular deadline here. Instead, the connect and input actors will + // update the deadline prior to each asynchronous operation. + deadline_.async_wait(std::bind(&client::check_deadline, this)); + } + + // This function terminates all the actors to shut down the connection. It + // may be called by the user of the client class, or by the class itself in + // response to graceful termination or an unrecoverable error. + void stop() + { + stopped_ = true; + boost::system::error_code ignored_error; + socket_.close(ignored_error); + deadline_.cancel(); + heartbeat_timer_.cancel(); + } + +private: + void start_connect(tcp::resolver::results_type::iterator endpoint_iter) + { + if (endpoint_iter != endpoints_.end()) + { + std::cout << "Trying " << endpoint_iter->endpoint() << "...\n"; + + // Set a deadline for the connect operation. + deadline_.expires_after(std::chrono::seconds(60)); + + // Start the asynchronous connect operation. + socket_.async_connect(endpoint_iter->endpoint(), + std::bind(&client::handle_connect, + this, _1, endpoint_iter)); + } + else + { + // There are no more endpoints to try. Shut down the client. + stop(); + } + } + + void handle_connect(const boost::system::error_code& error, + tcp::resolver::results_type::iterator endpoint_iter) + { + if (stopped_) + return; + + // The async_connect() function automatically opens the socket at the start + // of the asynchronous operation. If the socket is closed at this time then + // the timeout handler must have run first. + if (!socket_.is_open()) + { + std::cout << "Connect timed out\n"; + + // Try the next available endpoint. + start_connect(++endpoint_iter); + } + + // Check if the connect operation failed before the deadline expired. + else if (error) + { + std::cout << "Connect error: " << error.message() << "\n"; + + // We need to close the socket used in the previous connection attempt + // before starting a new one. + socket_.close(); + + // Try the next available endpoint. + start_connect(++endpoint_iter); + } + + // Otherwise we have successfully established a connection. + else + { + std::cout << "Connected to " << endpoint_iter->endpoint() << "\n"; + + // Start the input actor. + start_read(); + + // Start the heartbeat actor. + start_write(); + } + } + + void start_read() + { + // Set a deadline for the read operation. + deadline_.expires_after(std::chrono::seconds(30)); + + // Start an asynchronous operation to read a newline-delimited message. + boost::asio::async_read_until(socket_, + boost::asio::dynamic_buffer(input_buffer_), '\n', + std::bind(&client::handle_read, this, _1, _2)); + } + + void handle_read(const boost::system::error_code& error, std::size_t n) + { + if (stopped_) + return; + + if (!error) + { + // Extract the newline-delimited message from the buffer. + std::string line(input_buffer_.substr(0, n - 1)); + input_buffer_.erase(0, n); + + // Empty messages are heartbeats and so ignored. + if (!line.empty()) + { + std::cout << "Received: " << line << "\n"; + } + + start_read(); + } + else + { + std::cout << "Error on receive: " << error.message() << "\n"; + + stop(); + } + } + + void start_write() + { + if (stopped_) + return; + + // Start an asynchronous operation to send a heartbeat message. + boost::asio::async_write(socket_, boost::asio::buffer("\n", 1), + std::bind(&client::handle_write, this, _1)); + } + + void handle_write(const boost::system::error_code& error) + { + if (stopped_) + return; + + if (!error) + { + // Wait 10 seconds before sending the next heartbeat. + heartbeat_timer_.expires_after(std::chrono::seconds(10)); + heartbeat_timer_.async_wait(std::bind(&client::start_write, this)); + } + else + { + std::cout << "Error on heartbeat: " << error.message() << "\n"; + + stop(); + } + } + + void check_deadline() + { + if (stopped_) + return; + + // Check whether the deadline has passed. We compare the deadline against + // the current time since a new asynchronous operation may have moved the + // deadline before this actor had a chance to run. + if (deadline_.expiry() <= steady_timer::clock_type::now()) + { + // The deadline has passed. The socket is closed so that any outstanding + // asynchronous operations are cancelled. + socket_.close(); + + // There is no longer an active deadline. The expiry is set to the + // maximum time point so that the actor takes no action until a new + // deadline is set. + deadline_.expires_at(steady_timer::time_point::max()); + } + + // Put the actor back to sleep. + deadline_.async_wait(std::bind(&client::check_deadline, this)); + } + +private: + bool stopped_ = false; + tcp::resolver::results_type endpoints_; + tcp::socket socket_; + std::string input_buffer_; + steady_timer deadline_; + steady_timer heartbeat_timer_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: client <host> <port>\n"; + return 1; + } + + boost::asio::io_context io_context; + tcp::resolver r(io_context); + client c(io_context); + + c.start(r.resolve(argv[1], argv[2])); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/timeouts/blocking_tcp_client.cpp b/src/boost/libs/asio/example/cpp11/timeouts/blocking_tcp_client.cpp new file mode 100644 index 000000000..f64ba66a2 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/timeouts/blocking_tcp_client.cpp @@ -0,0 +1,192 @@ +// +// blocking_tcp_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/buffer.hpp> +#include <boost/asio/connect.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/read_until.hpp> +#include <boost/system/system_error.hpp> +#include <boost/asio/write.hpp> +#include <cstdlib> +#include <iostream> +#include <string> + +using boost::asio::ip::tcp; + +//---------------------------------------------------------------------- + +// +// This class manages socket timeouts by running the io_context using the timed +// io_context::run_for() member function. Each asynchronous operation is given +// a timeout within which it must complete. The socket operations themselves +// use lambdas as completion handlers. For a given socket operation, the client +// object runs the io_context to block thread execution until the operation +// completes or the timeout is reached. If the io_context::run_for() function +// times out, the socket is closed and the outstanding asynchronous operation +// is cancelled. +// +class client +{ +public: + void connect(const std::string& host, const std::string& service, + std::chrono::steady_clock::duration timeout) + { + // Resolve the host name and service to a list of endpoints. + auto endpoints = tcp::resolver(io_context_).resolve(host, service); + + // Start the asynchronous operation itself. The lambda that is used as a + // callback will update the error variable when the operation completes. + // The blocking_udp_client.cpp example shows how you can use std::bind + // rather than a lambda. + boost::system::error_code error; + boost::asio::async_connect(socket_, endpoints, + [&](const boost::system::error_code& result_error, + const tcp::endpoint& /*result_endpoint*/) + { + error = result_error; + }); + + // Run the operation until it completes, or until the timeout. + run(timeout); + + // Determine whether a connection was successfully established. + if (error) + throw std::system_error(error); + } + + std::string read_line(std::chrono::steady_clock::duration timeout) + { + // Start the asynchronous operation. The lambda that is used as a callback + // will update the error and n variables when the operation completes. The + // blocking_udp_client.cpp example shows how you can use std::bind rather + // than a lambda. + boost::system::error_code error; + std::size_t n = 0; + boost::asio::async_read_until(socket_, + boost::asio::dynamic_buffer(input_buffer_), '\n', + [&](const boost::system::error_code& result_error, + std::size_t result_n) + { + error = result_error; + n = result_n; + }); + + // Run the operation until it completes, or until the timeout. + run(timeout); + + // Determine whether the read completed successfully. + if (error) + throw std::system_error(error); + + std::string line(input_buffer_.substr(0, n - 1)); + input_buffer_.erase(0, n); + return line; + } + + void write_line(const std::string& line, + std::chrono::steady_clock::duration timeout) + { + std::string data = line + "\n"; + + // Start the asynchronous operation itself. The lambda that is used as a + // callback will update the error variable when the operation completes. + // The blocking_udp_client.cpp example shows how you can use std::bind + // rather than a lambda. + boost::system::error_code error; + boost::asio::async_write(socket_, boost::asio::buffer(data), + [&](const boost::system::error_code& result_error, + std::size_t /*result_n*/) + { + error = result_error; + }); + + // Run the operation until it completes, or until the timeout. + run(timeout); + + // Determine whether the read completed successfully. + if (error) + throw std::system_error(error); + } + +private: + void run(std::chrono::steady_clock::duration timeout) + { + // Restart the io_context, as it may have been left in the "stopped" state + // by a previous operation. + io_context_.restart(); + + // Block until the asynchronous operation has completed, or timed out. If + // the pending asynchronous operation is a composed operation, the deadline + // applies to the entire operation, rather than individual operations on + // the socket. + io_context_.run_for(timeout); + + // If the asynchronous operation completed successfully then the io_context + // would have been stopped due to running out of work. If it was not + // stopped, then the io_context::run_for call must have timed out. + if (!io_context_.stopped()) + { + // Close the socket to cancel the outstanding asynchronous operation. + socket_.close(); + + // Run the io_context again until the operation completes. + io_context_.run(); + } + } + + boost::asio::io_context io_context_; + tcp::socket socket_{io_context_}; + std::string input_buffer_; +}; + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 4) + { + std::cerr << "Usage: blocking_tcp_client <host> <port> <message>\n"; + return 1; + } + + client c; + c.connect(argv[1], argv[2], std::chrono::seconds(10)); + + auto time_sent = std::chrono::steady_clock::now(); + + c.write_line(argv[3], std::chrono::seconds(10)); + + for (;;) + { + std::string line = c.read_line(std::chrono::seconds(10)); + + // Keep going until we get back the line that was sent. + if (line == argv[3]) + break; + } + + auto time_received = std::chrono::steady_clock::now(); + + std::cout << "Round trip time: "; + std::cout << std::chrono::duration_cast< + std::chrono::microseconds>( + time_received - time_sent).count(); + std::cout << " microseconds\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/timeouts/blocking_token_tcp_client.cpp b/src/boost/libs/asio/example/cpp11/timeouts/blocking_token_tcp_client.cpp new file mode 100644 index 000000000..031275f73 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/timeouts/blocking_token_tcp_client.cpp @@ -0,0 +1,199 @@ +// +// blocking_token_tcp_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/connect.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/read_until.hpp> +#include <boost/asio/streambuf.hpp> +#include <boost/system/system_error.hpp> +#include <boost/asio/write.hpp> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <string> + +using boost::asio::ip::tcp; + +// We will use our sockets only with an io_context. +using tcp_socket = boost::asio::basic_stream_socket< + tcp, boost::asio::io_context::executor_type>; + +//---------------------------------------------------------------------- + +// A custom completion token that makes asynchronous operations behave as +// though they are blocking calls with a timeout. +struct close_after +{ + close_after(std::chrono::steady_clock::duration t, tcp_socket& s) + : timeout_(t), socket_(s) + { + } + + // The maximum time to wait for an asynchronous operation to complete. + std::chrono::steady_clock::duration timeout_; + + // The socket to be closed if the operation does not complete in time. + tcp_socket& socket_; +}; + +namespace boost { +namespace asio { + +// The async_result template is specialised to allow the close_after token to +// be used with asynchronous operations that have a completion signature of +// void(error_code, T). Generalising this for all completion signature forms is +// left as an exercise for the reader. +template <typename T> +class async_result<close_after, void(boost::system::error_code, T)> +{ +public: + // An asynchronous operation's initiating function automatically creates an + // completion_handler_type object from the token. This function object is + // then called on completion of the asynchronous operation. + class completion_handler_type + { + public: + completion_handler_type(const close_after& token) + : token_(token) + { + } + + void operator()(const boost::system::error_code& error, T t) + { + *error_ = error; + *t_ = t; + } + + private: + friend class async_result; + close_after token_; + boost::system::error_code* error_; + T* t_; + }; + + // The async_result constructor associates the completion handler object with + // the result of the initiating function. + explicit async_result(completion_handler_type& h) + : timeout_(h.token_.timeout_), + socket_(h.token_.socket_) + { + h.error_ = &error_; + h.t_ = &t_; + } + + // The return_type typedef determines the result type of the asynchronous + // operation's initiating function. + typedef T return_type; + + // The get() function is used to obtain the result of the asynchronous + // operation's initiating function. For the close_after completion token, we + // use this function to run the io_context until the operation is complete. + return_type get() + { + boost::asio::io_context& io_context = socket_.get_executor().context(); + + // Restart the io_context, as it may have been left in the "stopped" state + // by a previous operation. + io_context.restart(); + + // Block until the asynchronous operation has completed, or timed out. If + // the pending asynchronous operation is a composed operation, the deadline + // applies to the entire operation, rather than individual operations on + // the socket. + io_context.run_for(timeout_); + + // If the asynchronous operation completed successfully then the io_context + // would have been stopped due to running out of work. If it was not + // stopped, then the io_context::run_for call must have timed out and the + // operation is still incomplete. + if (!io_context.stopped()) + { + // Close the socket to cancel the outstanding asynchronous operation. + socket_.close(); + + // Run the io_context again until the operation completes. + io_context.run(); + } + + // If the operation failed, throw an exception. Otherwise return the result. + return error_ ? throw std::system_error(error_) : t_; + } + +private: + std::chrono::steady_clock::duration timeout_; + tcp_socket& socket_; + boost::system::error_code error_; + T t_; +}; + +} // namespace asio +} // namespace boost + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 4) + { + std::cerr << "Usage: blocking_tcp_client <host> <port> <message>\n"; + return 1; + } + + boost::asio::io_context io_context; + + // Resolve the host name and service to a list of endpoints. + auto endpoints = tcp::resolver(io_context).resolve(argv[1], argv[2]); + + tcp_socket socket(io_context); + + // Run an asynchronous connect operation with a timeout. + boost::asio::async_connect(socket, endpoints, + close_after(std::chrono::seconds(10), socket)); + + auto time_sent = std::chrono::steady_clock::now(); + + // Run an asynchronous write operation with a timeout. + std::string msg = argv[3] + std::string("\n"); + boost::asio::async_write(socket, boost::asio::buffer(msg), + close_after(std::chrono::seconds(10), socket)); + + for (std::string input_buffer;;) + { + // Run an asynchronous read operation with a timeout. + std::size_t n = boost::asio::async_read_until(socket, + boost::asio::dynamic_buffer(input_buffer), '\n', + close_after(std::chrono::seconds(10), socket)); + + std::string line(input_buffer.substr(0, n - 1)); + input_buffer.erase(0, n); + + // Keep going until we get back the line that was sent. + if (line == argv[3]) + break; + } + + auto time_received = std::chrono::steady_clock::now(); + + std::cout << "Round trip time: "; + std::cout << std::chrono::duration_cast< + std::chrono::microseconds>( + time_received - time_sent).count(); + std::cout << " microseconds\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/timeouts/blocking_udp_client.cpp b/src/boost/libs/asio/example/cpp11/timeouts/blocking_udp_client.cpp new file mode 100644 index 000000000..c769e3e7b --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/timeouts/blocking_udp_client.cpp @@ -0,0 +1,155 @@ +// +// blocking_udp_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/buffer.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/udp.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> + +using boost::asio::ip::udp; +using std::placeholders::_1; +using std::placeholders::_2; + +//---------------------------------------------------------------------- + +// +// This class manages socket timeouts by running the io_context using the timed +// io_context::run_for() member function. Each asynchronous operation is given +// a timeout within which it must complete. The socket operations themselves +// use std::bind to specify the completion handler: +// +// +---------------+ +// | | +// | receive | +// | | +// +---------------+ +// | +// async_- | +----------------+ +// receive() | | | +// +--->| handle_receive | +// | | +// +----------------+ +// +// For a given socket operation, the client object runs the io_context to block +// thread execution until the operation completes or the timeout is reached. If +// the io_context::run_for() function times out, the socket is closed and the +// outstanding asynchronous operation is cancelled. +// +class client +{ +public: + client(const udp::endpoint& listen_endpoint) + : socket_(io_context_, listen_endpoint) + { + } + + std::size_t receive(const boost::asio::mutable_buffer& buffer, + std::chrono::steady_clock::duration timeout, + boost::system::error_code& error) + { + // Start the asynchronous operation. The handle_receive function used as a + // callback will update the error and length variables. + std::size_t length = 0; + socket_.async_receive(boost::asio::buffer(buffer), + std::bind(&client::handle_receive, _1, _2, &error, &length)); + + // Run the operation until it completes, or until the timeout. + run(timeout); + + return length; + } + +private: + void run(std::chrono::steady_clock::duration timeout) + { + // Restart the io_context, as it may have been left in the "stopped" state + // by a previous operation. + io_context_.restart(); + + // Block until the asynchronous operation has completed, or timed out. If + // the pending asynchronous operation is a composed operation, the deadline + // applies to the entire operation, rather than individual operations on + // the socket. + io_context_.run_for(timeout); + + // If the asynchronous operation completed successfully then the io_context + // would have been stopped due to running out of work. If it was not + // stopped, then the io_context::run_for call must have timed out. + if (!io_context_.stopped()) + { + // Cancel the outstanding asynchronous operation. + socket_.cancel(); + + // Run the io_context again until the operation completes. + io_context_.run(); + } + } + + static void handle_receive( + const boost::system::error_code& error, std::size_t length, + boost::system::error_code* out_error, std::size_t* out_length) + { + *out_error = error; + *out_length = length; + } + +private: + boost::asio::io_context io_context_; + udp::socket socket_; +}; + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + try + { + using namespace std; // For atoi. + + if (argc != 3) + { + std::cerr << "Usage: blocking_udp_client <listen_addr> <listen_port>\n"; + return 1; + } + + udp::endpoint listen_endpoint( + boost::asio::ip::make_address(argv[1]), + std::atoi(argv[2])); + + client c(listen_endpoint); + + for (;;) + { + char data[1024]; + boost::system::error_code error; + std::size_t n = c.receive(boost::asio::buffer(data), + std::chrono::seconds(10), error); + + if (error) + { + std::cout << "Receive error: " << error.message() << "\n"; + } + else + { + std::cout << "Received: "; + std::cout.write(data, n); + std::cout << "\n"; + } + } + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/timeouts/server.cpp b/src/boost/libs/asio/example/cpp11/timeouts/server.cpp new file mode 100644 index 000000000..e693791b4 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/timeouts/server.cpp @@ -0,0 +1,433 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <algorithm> +#include <cstdlib> +#include <deque> +#include <iostream> +#include <memory> +#include <set> +#include <string> +#include <boost/asio/buffer.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/ip/udp.hpp> +#include <boost/asio/read_until.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/asio/write.hpp> + +using boost::asio::steady_timer; +using boost::asio::ip::tcp; +using boost::asio::ip::udp; + +//---------------------------------------------------------------------- + +class subscriber +{ +public: + virtual ~subscriber() = default; + virtual void deliver(const std::string& msg) = 0; +}; + +typedef std::shared_ptr<subscriber> subscriber_ptr; + +//---------------------------------------------------------------------- + +class channel +{ +public: + void join(subscriber_ptr subscriber) + { + subscribers_.insert(subscriber); + } + + void leave(subscriber_ptr subscriber) + { + subscribers_.erase(subscriber); + } + + void deliver(const std::string& msg) + { + for (const auto& s : subscribers_) + { + s->deliver(msg); + } + } + +private: + std::set<subscriber_ptr> subscribers_; +}; + +//---------------------------------------------------------------------- + +// +// This class manages socket timeouts by applying the concept of a deadline. +// Some asynchronous operations are given deadlines by which they must complete. +// Deadlines are enforced by two "actors" that persist for the lifetime of the +// session object, one for input and one for output: +// +// +----------------+ +----------------+ +// | | | | +// | check_deadline |<-------+ | check_deadline |<-------+ +// | | | | | | +// +----------------+ | +----------------+ | +// | | | | +// async_wait() | +----------------+ async_wait() | +----------------+ +// on input | | lambda | on output | | lambda | +// deadline +--->| in | deadline +--->| in | +// | check_deadline | | check_deadline | +// +----------------+ +----------------+ +// +// If either deadline actor determines that the corresponding deadline has +// expired, the socket is closed and any outstanding operations are cancelled. +// +// The input actor reads messages from the socket, where messages are delimited +// by the newline character: +// +// +-------------+ +// | | +// | read_line |<----+ +// | | | +// +-------------+ | +// | | +// async_- | +-------------+ +// read_- | | lambda | +// until() +--->| in | +// | read_line | +// +-------------+ +// +// The deadline for receiving a complete message is 30 seconds. If a non-empty +// message is received, it is delivered to all subscribers. If a heartbeat (a +// message that consists of a single newline character) is received, a heartbeat +// is enqueued for the client, provided there are no other messages waiting to +// be sent. +// +// The output actor is responsible for sending messages to the client: +// +// +----------------+ +// | |<---------------------+ +// | await_output | | +// | |<-------+ | +// +----------------+ | | +// | | | | +// | async_- | +----------------+ | +// | wait() | | lambda | | +// | +->| in | | +// | | await_output | | +// | +----------------+ | +// V | +// +--------------+ +--------------+ +// | | async_write() | lambda | +// | write_line |-------------->| in | +// | | | write_line | +// +--------------+ +--------------+ +// +// The output actor first waits for an output message to be enqueued. It does +// this by using a steady_timer as an asynchronous condition variable. The +// steady_timer will be signalled whenever the output queue is non-empty. +// +// Once a message is available, it is sent to the client. The deadline for +// sending a complete message is 30 seconds. After the message is successfully +// sent, the output actor again waits for the output queue to become non-empty. +// +class tcp_session + : public subscriber, + public std::enable_shared_from_this<tcp_session> +{ +public: + tcp_session(tcp::socket socket, channel& ch) + : channel_(ch), + socket_(std::move(socket)) + { + input_deadline_.expires_at(steady_timer::time_point::max()); + output_deadline_.expires_at(steady_timer::time_point::max()); + + // The non_empty_output_queue_ steady_timer is set to the maximum time + // point whenever the output queue is empty. This ensures that the output + // actor stays asleep until a message is put into the queue. + non_empty_output_queue_.expires_at(steady_timer::time_point::max()); + } + + // Called by the server object to initiate the four actors. + void start() + { + channel_.join(shared_from_this()); + + read_line(); + check_deadline(input_deadline_); + + await_output(); + check_deadline(output_deadline_); + } + +private: + void stop() + { + channel_.leave(shared_from_this()); + + boost::system::error_code ignored_error; + socket_.close(ignored_error); + input_deadline_.cancel(); + non_empty_output_queue_.cancel(); + output_deadline_.cancel(); + } + + bool stopped() const + { + return !socket_.is_open(); + } + + void deliver(const std::string& msg) override + { + output_queue_.push_back(msg + "\n"); + + // Signal that the output queue contains messages. Modifying the expiry + // will wake the output actor, if it is waiting on the timer. + non_empty_output_queue_.expires_at(steady_timer::time_point::min()); + } + + void read_line() + { + // Set a deadline for the read operation. + input_deadline_.expires_after(std::chrono::seconds(30)); + + // Start an asynchronous operation to read a newline-delimited message. + auto self(shared_from_this()); + boost::asio::async_read_until(socket_, + boost::asio::dynamic_buffer(input_buffer_), '\n', + [this, self](const boost::system::error_code& error, std::size_t n) + { + // Check if the session was stopped while the operation was pending. + if (stopped()) + return; + + if (!error) + { + // Extract the newline-delimited message from the buffer. + std::string msg(input_buffer_.substr(0, n - 1)); + input_buffer_.erase(0, n); + + if (!msg.empty()) + { + channel_.deliver(msg); + } + else + { + + // We received a heartbeat message from the client. If there's + // nothing else being sent or ready to be sent, send a heartbeat + // right back. + if (output_queue_.empty()) + { + output_queue_.push_back("\n"); + + // Signal that the output queue contains messages. Modifying + // the expiry will wake the output actor, if it is waiting on + // the timer. + non_empty_output_queue_.expires_at( + steady_timer::time_point::min()); + } + } + + read_line(); + } + else + { + stop(); + } + }); + } + + void await_output() + { + auto self(shared_from_this()); + non_empty_output_queue_.async_wait( + [this, self](const boost::system::error_code& /*error*/) + { + // Check if the session was stopped while the operation was pending. + if (stopped()) + return; + + if (output_queue_.empty()) + { + // There are no messages that are ready to be sent. The actor goes + // to sleep by waiting on the non_empty_output_queue_ timer. When a + // new message is added, the timer will be modified and the actor + // will wake. + non_empty_output_queue_.expires_at(steady_timer::time_point::max()); + await_output(); + } + else + { + write_line(); + } + }); + } + + void write_line() + { + // Set a deadline for the write operation. + output_deadline_.expires_after(std::chrono::seconds(30)); + + // Start an asynchronous operation to send a message. + auto self(shared_from_this()); + boost::asio::async_write(socket_, + boost::asio::buffer(output_queue_.front()), + [this, self](const boost::system::error_code& error, std::size_t /*n*/) + { + // Check if the session was stopped while the operation was pending. + if (stopped()) + return; + + if (!error) + { + output_queue_.pop_front(); + + await_output(); + } + else + { + stop(); + } + }); + } + + void check_deadline(steady_timer& deadline) + { + auto self(shared_from_this()); + deadline.async_wait( + [this, self, &deadline](const boost::system::error_code& /*error*/) + { + // Check if the session was stopped while the operation was pending. + if (stopped()) + return; + + // Check whether the deadline has passed. We compare the deadline + // against the current time since a new asynchronous operation may + // have moved the deadline before this actor had a chance to run. + if (deadline.expiry() <= steady_timer::clock_type::now()) + { + // The deadline has passed. Stop the session. The other actors will + // terminate as soon as possible. + stop(); + } + else + { + // Put the actor back to sleep. + check_deadline(deadline); + } + }); + } + + channel& channel_; + tcp::socket socket_; + std::string input_buffer_; + steady_timer input_deadline_{socket_.get_executor()}; + std::deque<std::string> output_queue_; + steady_timer non_empty_output_queue_{socket_.get_executor()}; + steady_timer output_deadline_{socket_.get_executor()}; +}; + +typedef std::shared_ptr<tcp_session> tcp_session_ptr; + +//---------------------------------------------------------------------- + +class udp_broadcaster + : public subscriber +{ +public: + udp_broadcaster(boost::asio::io_context& io_context, + const udp::endpoint& broadcast_endpoint) + : socket_(io_context) + { + socket_.connect(broadcast_endpoint); + socket_.set_option(udp::socket::broadcast(true)); + } + +private: + void deliver(const std::string& msg) + { + boost::system::error_code ignored_error; + socket_.send(boost::asio::buffer(msg), 0, ignored_error); + } + + udp::socket socket_; +}; + +//---------------------------------------------------------------------- + +class server +{ +public: + server(boost::asio::io_context& io_context, + const tcp::endpoint& listen_endpoint, + const udp::endpoint& broadcast_endpoint) + : io_context_(io_context), + acceptor_(io_context, listen_endpoint) + { + channel_.join( + std::make_shared<udp_broadcaster>( + io_context_, broadcast_endpoint)); + + accept(); + } + +private: + void accept() + { + acceptor_.async_accept( + [this](const boost::system::error_code& error, tcp::socket socket) + { + if (!error) + { + std::make_shared<tcp_session>(std::move(socket), channel_)->start(); + } + + accept(); + }); + } + + boost::asio::io_context& io_context_; + tcp::acceptor acceptor_; + channel channel_; +}; + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + try + { + using namespace std; // For atoi. + + if (argc != 4) + { + std::cerr << "Usage: server <listen_port> <bcast_address> <bcast_port>\n"; + return 1; + } + + boost::asio::io_context io_context; + + tcp::endpoint listen_endpoint(tcp::v4(), atoi(argv[1])); + + udp::endpoint broadcast_endpoint( + boost::asio::ip::make_address(argv[2]), atoi(argv[3])); + + server s(io_context, listen_endpoint, broadcast_endpoint); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp11/timers/Jamfile.v2 b/src/boost/libs/asio/example/cpp11/timers/Jamfile.v2 new file mode 100644 index 000000000..7c57391ed --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/timers/Jamfile.v2 @@ -0,0 +1,30 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe time_t_timer + : time_t_timer.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; diff --git a/src/boost/libs/asio/example/cpp11/timers/time_t_timer.cpp b/src/boost/libs/asio/example/cpp11/timers/time_t_timer.cpp new file mode 100644 index 000000000..9ac53d343 --- /dev/null +++ b/src/boost/libs/asio/example/cpp11/timers/time_t_timer.cpp @@ -0,0 +1,106 @@ +// +// time_t_timer.cpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio.hpp> +#include <ctime> +#include <chrono> +#include <iostream> + +// A custom implementation of the Clock concept from the standard C++ library. +struct time_t_clock +{ + // The duration type. + typedef std::chrono::steady_clock::duration duration; + + // The duration's underlying arithmetic representation. + typedef duration::rep rep; + + // The ratio representing the duration's tick period. + typedef duration::period period; + + // An absolute time point represented using the clock. + typedef std::chrono::time_point<time_t_clock> time_point; + + // The clock is not monotonically increasing. + static constexpr bool is_steady = false; + + // Get the current time. + static time_point now() noexcept + { + return time_point() + std::chrono::seconds(std::time(0)); + } +}; + +// The boost::asio::basic_waitable_timer template accepts an optional WaitTraits +// template parameter. The underlying time_t clock has one-second granularity, +// so these traits may be customised to reduce the latency between the clock +// ticking over and a wait operation's completion. When the timeout is near +// (less than one second away) we poll the clock more frequently to detect the +// time change closer to when it occurs. The user can select the appropriate +// trade off between accuracy and the increased CPU cost of polling. In extreme +// cases, a zero duration may be returned to make the timers as accurate as +// possible, albeit with 100% CPU usage. +struct time_t_wait_traits +{ + // Determine how long until the clock should be next polled to determine + // whether the duration has elapsed. + static time_t_clock::duration to_wait_duration( + const time_t_clock::duration& d) + { + if (d > std::chrono::seconds(1)) + return d - std::chrono::seconds(1); + else if (d > std::chrono::seconds(0)) + return std::chrono::milliseconds(10); + else + return std::chrono::seconds(0); + } + + // Determine how long until the clock should be next polled to determine + // whether the absoluate time has been reached. + static time_t_clock::duration to_wait_duration( + const time_t_clock::time_point& t) + { + return to_wait_duration(t - time_t_clock::now()); + } +}; + +typedef boost::asio::basic_waitable_timer< + time_t_clock, time_t_wait_traits> time_t_timer; + +int main() +{ + try + { + boost::asio::io_context io_context; + + time_t_timer timer(io_context); + + timer.expires_after(std::chrono::seconds(5)); + std::cout << "Starting synchronous wait\n"; + timer.wait(); + std::cout << "Finished synchronous wait\n"; + + timer.expires_after(std::chrono::seconds(5)); + std::cout << "Starting asynchronous wait\n"; + timer.async_wait( + [](const boost::system::error_code& /*error*/) + { + std::cout << "timeout\n"; + }); + io_context.run(); + std::cout << "Finished asynchronous wait\n"; + } + catch (std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp14/executors/Jamfile.v2 b/src/boost/libs/asio/example/cpp14/executors/Jamfile.v2 new file mode 100644 index 000000000..cb7b200f6 --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/executors/Jamfile.v2 @@ -0,0 +1,38 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe actor : actor.cpp ; +exe async_1 : async_1.cpp ; +exe async_2 : async_2.cpp ; +exe bank_account_1 : bank_account_1.cpp ; +exe bank_account_2 : bank_account_2.cpp ; +exe fork_join : fork_join.cpp ; +exe pipeline : pipeline.cpp ; +exe priority_scheduler : priority_scheduler.cpp ; diff --git a/src/boost/libs/asio/example/cpp14/executors/actor.cpp b/src/boost/libs/asio/example/cpp14/executors/actor.cpp new file mode 100644 index 000000000..7046e7ba9 --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/executors/actor.cpp @@ -0,0 +1,281 @@ +#include <boost/asio/ts/executor.hpp> +#include <condition_variable> +#include <deque> +#include <memory> +#include <mutex> +#include <typeinfo> +#include <vector> + +using boost::asio::defer; +using boost::asio::executor; +using boost::asio::post; +using boost::asio::strand; +using boost::asio::system_executor; + +//------------------------------------------------------------------------------ +// A tiny actor framework +// ~~~~~~~~~~~~~~~~~~~~~~ + +class actor; + +// Used to identify the sender and recipient of messages. +typedef actor* actor_address; + +// Base class for all registered message handlers. +class message_handler_base +{ +public: + virtual ~message_handler_base() {} + + // Used to determine which message handlers receive an incoming message. + virtual const std::type_info& message_id() const = 0; +}; + +// Base class for a handler for a specific message type. +template <class Message> +class message_handler : public message_handler_base +{ +public: + // Handle an incoming message. + virtual void handle_message(Message msg, actor_address from) = 0; +}; + +// Concrete message handler for a specific message type. +template <class Actor, class Message> +class mf_message_handler : public message_handler<Message> +{ +public: + // Construct a message handler to invoke the specified member function. + mf_message_handler(void (Actor::* mf)(Message, actor_address), Actor* a) + : function_(mf), actor_(a) + { + } + + // Used to determine which message handlers receive an incoming message. + virtual const std::type_info& message_id() const + { + return typeid(Message); + } + + // Handle an incoming message. + virtual void handle_message(Message msg, actor_address from) + { + (actor_->*function_)(std::move(msg), from); + } + + // Determine whether the message handler represents the specified function. + bool is_function(void (Actor::* mf)(Message, actor_address)) const + { + return mf == function_; + } + +private: + void (Actor::* function_)(Message, actor_address); + Actor* actor_; +}; + +// Base class for all actors. +class actor +{ +public: + virtual ~actor() + { + } + + // Obtain the actor's address for use as a message sender or recipient. + actor_address address() + { + return this; + } + + // Send a message from one actor to another. + template <class Message> + friend void send(Message msg, actor_address from, actor_address to) + { + // Execute the message handler in the context of the target's executor. + post(to->executor_, + [=, msg=std::move(msg)] + { + to->call_handler(std::move(msg), from); + }); + } + +protected: + // Construct the actor to use the specified executor for all message handlers. + actor(executor e) + : executor_(std::move(e)) + { + } + + // Register a handler for a specific message type. Duplicates are permitted. + template <class Actor, class Message> + void register_handler(void (Actor::* mf)(Message, actor_address)) + { + handlers_.push_back( + std::make_shared<mf_message_handler<Actor, Message>>( + mf, static_cast<Actor*>(this))); + } + + // Deregister a handler. Removes only the first matching handler. + template <class Actor, class Message> + void deregister_handler(void (Actor::* mf)(Message, actor_address)) + { + const std::type_info& id = typeid(Message); + for (auto iter = handlers_.begin(); iter != handlers_.end(); ++iter) + { + if ((*iter)->message_id() == id) + { + auto mh = static_cast<mf_message_handler<Actor, Message>*>(iter->get()); + if (mh->is_function(mf)) + { + handlers_.erase(iter); + return; + } + } + } + } + + // Send a message from within a message handler. + template <class Message> + void tail_send(Message msg, actor_address to) + { + // Execute the message handler in the context of the target's executor. + defer(to->executor_, + [=, msg=std::move(msg), from=this] + { + to->call_handler(std::move(msg), from); + }); + } + +private: + // Find the matching message handlers, if any, and call them. + template <class Message> + void call_handler(Message msg, actor_address from) + { + const std::type_info& message_id = typeid(Message); + for (auto& h: handlers_) + { + if (h->message_id() == message_id) + { + auto mh = static_cast<message_handler<Message>*>(h.get()); + mh->handle_message(msg, from); + } + } + } + + // All messages associated with a single actor object should be processed + // non-concurrently. We use a strand to ensure non-concurrent execution even + // if the underlying executor may use multiple threads. + strand<executor> executor_; + + std::vector<std::shared_ptr<message_handler_base>> handlers_; +}; + +// A concrete actor that allows synchronous message retrieval. +template <class Message> +class receiver : public actor +{ +public: + receiver() + : actor(system_executor()) + { + register_handler(&receiver::message_handler); + } + + // Block until a message has been received. + Message wait() + { + std::unique_lock<std::mutex> lock(mutex_); + condition_.wait(lock, [this]{ return !message_queue_.empty(); }); + Message msg(std::move(message_queue_.front())); + message_queue_.pop_front(); + return msg; + } + +private: + // Handle a new message by adding it to the queue and waking a waiter. + void message_handler(Message msg, actor_address /* from */) + { + std::lock_guard<std::mutex> lock(mutex_); + message_queue_.push_back(std::move(msg)); + condition_.notify_one(); + } + + std::mutex mutex_; + std::condition_variable condition_; + std::deque<Message> message_queue_; +}; + +//------------------------------------------------------------------------------ + +#include <boost/asio/thread_pool.hpp> +#include <iostream> + +using boost::asio::thread_pool; + +class member : public actor +{ +public: + explicit member(executor e) + : actor(std::move(e)) + { + register_handler(&member::init_handler); + } + +private: + void init_handler(actor_address next, actor_address from) + { + next_ = next; + caller_ = from; + + register_handler(&member::token_handler); + deregister_handler(&member::init_handler); + } + + void token_handler(int token, actor_address /*from*/) + { + int msg(token); + actor_address to(caller_); + + if (token > 0) + { + msg = token - 1; + to = next_; + } + + tail_send(msg, to); + } + + actor_address next_; + actor_address caller_; +}; + +int main() +{ + const std::size_t num_threads = 16; + const int num_hops = 50000000; + const std::size_t num_actors = 503; + const int token_value = (num_hops + num_actors - 1) / num_actors; + const std::size_t actors_per_thread = num_actors / num_threads; + + struct single_thread_pool : thread_pool { single_thread_pool() : thread_pool(1) {} }; + single_thread_pool pools[num_threads]; + std::vector<std::shared_ptr<member>> members(num_actors); + receiver<int> rcvr; + + // Create the member actors. + for (std::size_t i = 0; i < num_actors; ++i) + members[i] = std::make_shared<member>(pools[(i / actors_per_thread) % num_threads].get_executor()); + + // Initialise the actors by passing each one the address of the next actor in the ring. + for (std::size_t i = num_actors, next_i = 0; i > 0; next_i = --i) + send(members[next_i]->address(), rcvr.address(), members[i - 1]->address()); + + // Send exactly one token to each actor, all with the same initial value, rounding up if required. + for (std::size_t i = 0; i < num_actors; ++i) + send(token_value, rcvr.address(), members[i]->address()); + + // Wait for all signal messages, indicating the tokens have all reached zero. + for (std::size_t i = 0; i < num_actors; ++i) + rcvr.wait(); +} diff --git a/src/boost/libs/asio/example/cpp14/executors/async_1.cpp b/src/boost/libs/asio/example/cpp14/executors/async_1.cpp new file mode 100644 index 000000000..db72563a8 --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/executors/async_1.cpp @@ -0,0 +1,47 @@ +#include <boost/asio/ts/executor.hpp> +#include <boost/asio/thread_pool.hpp> +#include <iostream> +#include <string> + +using boost::asio::bind_executor; +using boost::asio::dispatch; +using boost::asio::make_work_guard; +using boost::asio::post; +using boost::asio::thread_pool; + +// A function to asynchronously read a single line from an input stream. +template <class Handler> +void async_getline(std::istream& is, Handler handler) +{ + // Create executor_work for the handler's associated executor. + auto work = make_work_guard(handler); + + // Post a function object to do the work asynchronously. + post([&is, work, handler=std::move(handler)]() mutable + { + std::string line; + std::getline(is, line); + + // Pass the result to the handler, via the associated executor. + dispatch(work.get_executor(), + [line=std::move(line), handler=std::move(handler)]() mutable + { + handler(std::move(line)); + }); + }); +} + +int main() +{ + thread_pool pool; + + std::cout << "Enter a line: "; + + async_getline(std::cin, + bind_executor(pool, [](std::string line) + { + std::cout << "Line: " << line << "\n"; + })); + + pool.join(); +} diff --git a/src/boost/libs/asio/example/cpp14/executors/async_2.cpp b/src/boost/libs/asio/example/cpp14/executors/async_2.cpp new file mode 100644 index 000000000..6f40a902c --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/executors/async_2.cpp @@ -0,0 +1,68 @@ +#include <boost/asio/ts/executor.hpp> +#include <boost/asio/thread_pool.hpp> +#include <iostream> +#include <string> + +using boost::asio::bind_executor; +using boost::asio::dispatch; +using boost::asio::get_associated_executor; +using boost::asio::make_work_guard; +using boost::asio::post; +using boost::asio::thread_pool; + +// A function to asynchronously read a single line from an input stream. +template <class Handler> +void async_getline(std::istream& is, Handler handler) +{ + // Create executor_work for the handler's associated executor. + auto work = make_work_guard(handler); + + // Post a function object to do the work asynchronously. + post([&is, work, handler=std::move(handler)]() mutable + { + std::string line; + std::getline(is, line); + + // Pass the result to the handler, via the associated executor. + dispatch(work.get_executor(), + [line=std::move(line), handler=std::move(handler)]() mutable + { + handler(std::move(line)); + }); + }); +} + +// A function to asynchronously read multiple lines from an input stream. +template <class Handler> +void async_getlines(std::istream& is, std::string init, Handler handler) +{ + // Get the final handler's associated executor. + auto ex = get_associated_executor(handler); + + // Use the associated executor for each operation in the composition. + async_getline(is, + bind_executor(ex, + [&is, lines=std::move(init), handler=std::move(handler)] + (std::string line) mutable + { + if (line.empty()) + handler(lines); + else + async_getlines(is, lines + line + "\n", std::move(handler)); + })); +} + +int main() +{ + thread_pool pool; + + std::cout << "Enter text, terminating with a blank line:\n"; + + async_getlines(std::cin, "", + bind_executor(pool, [](std::string lines) + { + std::cout << "Lines:\n" << lines << "\n"; + })); + + pool.join(); +} diff --git a/src/boost/libs/asio/example/cpp14/executors/bank_account_1.cpp b/src/boost/libs/asio/example/cpp14/executors/bank_account_1.cpp new file mode 100644 index 000000000..6464b48d3 --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/executors/bank_account_1.cpp @@ -0,0 +1,54 @@ +#include <boost/asio/ts/executor.hpp> +#include <boost/asio/thread_pool.hpp> +#include <iostream> + +using boost::asio::post; +using boost::asio::thread_pool; + +// Traditional active object pattern. +// Member functions do not block. + +class bank_account +{ + int balance_ = 0; + mutable thread_pool pool_{1}; + +public: + void deposit(int amount) + { + post(pool_, [=] + { + balance_ += amount; + }); + } + + void withdraw(int amount) + { + post(pool_, [=] + { + if (balance_ >= amount) + balance_ -= amount; + }); + } + + void print_balance() const + { + post(pool_, [=] + { + std::cout << "balance = " << balance_ << "\n"; + }); + } + + ~bank_account() + { + pool_.join(); + } +}; + +int main() +{ + bank_account acct; + acct.deposit(20); + acct.withdraw(10); + acct.print_balance(); +} diff --git a/src/boost/libs/asio/example/cpp14/executors/bank_account_2.cpp b/src/boost/libs/asio/example/cpp14/executors/bank_account_2.cpp new file mode 100644 index 000000000..8de9a3cc2 --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/executors/bank_account_2.cpp @@ -0,0 +1,53 @@ +#include <boost/asio/ts/executor.hpp> +#include <boost/asio/thread_pool.hpp> +#include <iostream> + +using boost::asio::post; +using boost::asio::thread_pool; +using boost::asio::use_future; + +// Traditional active object pattern. +// Member functions block until operation is finished. + +class bank_account +{ + int balance_ = 0; + mutable thread_pool pool_{1}; + +public: + void deposit(int amount) + { + post(pool_, + use_future([=] + { + balance_ += amount; + })).get(); + } + + void withdraw(int amount) + { + post(pool_, + use_future([=] + { + if (balance_ >= amount) + balance_ -= amount; + })).get(); + } + + int balance() const + { + return post(pool_, + use_future([=] + { + return balance_; + })).get(); + } +}; + +int main() +{ + bank_account acct; + acct.deposit(20); + acct.withdraw(10); + std::cout << "balance = " << acct.balance() << "\n"; +} diff --git a/src/boost/libs/asio/example/cpp14/executors/fork_join.cpp b/src/boost/libs/asio/example/cpp14/executors/fork_join.cpp new file mode 100644 index 000000000..2d9b4f7bd --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/executors/fork_join.cpp @@ -0,0 +1,327 @@ +#include <boost/asio/ts/executor.hpp> +#include <boost/asio/thread_pool.hpp> +#include <condition_variable> +#include <memory> +#include <mutex> +#include <queue> +#include <thread> +#include <numeric> + +using boost::asio::dispatch; +using boost::asio::execution_context; +using boost::asio::thread_pool; + +// A fixed-size thread pool used to implement fork/join semantics. Functions +// are scheduled using a simple FIFO queue. Implementing work stealing, or +// using a queue based on atomic operations, are left as tasks for the reader. +class fork_join_pool : public execution_context +{ +public: + // The constructor starts a thread pool with the specified number of threads. + // Note that the thread_count is not a fixed limit on the pool's concurrency. + // Additional threads may temporarily be added to the pool if they join a + // fork_executor. + explicit fork_join_pool( + std::size_t thread_count = std::thread::hardware_concurrency() * 2) + : use_count_(1), + threads_(thread_count) + { + try + { + // Ask each thread in the pool to dequeue and execute functions until + // it is time to shut down, i.e. the use count is zero. + for (thread_count_ = 0; thread_count_ < thread_count; ++thread_count_) + { + dispatch(threads_, [&] + { + std::unique_lock<std::mutex> lock(mutex_); + while (use_count_ > 0) + if (!execute_next(lock)) + condition_.wait(lock); + }); + } + } + catch (...) + { + stop_threads(); + threads_.join(); + throw; + } + } + + // The destructor waits for the pool to finish executing functions. + ~fork_join_pool() + { + stop_threads(); + threads_.join(); + } + +private: + friend class fork_executor; + + // The base for all functions that are queued in the pool. + struct function_base + { + std::shared_ptr<std::size_t> work_count_; + void (*execute_)(std::shared_ptr<function_base>& p); + }; + + // Execute the next function from the queue, if any. Returns true if a + // function was executed, and false if the queue was empty. + bool execute_next(std::unique_lock<std::mutex>& lock) + { + if (queue_.empty()) + return false; + auto p(queue_.front()); + queue_.pop(); + lock.unlock(); + execute(lock, p); + return true; + } + + // Execute a function and decrement the outstanding work. + void execute(std::unique_lock<std::mutex>& lock, + std::shared_ptr<function_base>& p) + { + std::shared_ptr<std::size_t> work_count(std::move(p->work_count_)); + try + { + p->execute_(p); + lock.lock(); + do_work_finished(work_count); + } + catch (...) + { + lock.lock(); + do_work_finished(work_count); + throw; + } + } + + // Increment outstanding work. + void do_work_started(const std::shared_ptr<std::size_t>& work_count) noexcept + { + if (++(*work_count) == 1) + ++use_count_; + } + + // Decrement outstanding work. Notify waiting threads if we run out. + void do_work_finished(const std::shared_ptr<std::size_t>& work_count) noexcept + { + if (--(*work_count) == 0) + { + --use_count_; + condition_.notify_all(); + } + } + + // Dispatch a function, executing it immediately if the queue is already + // loaded. Otherwise adds the function to the queue and wakes a thread. + void do_dispatch(std::shared_ptr<function_base> p, + const std::shared_ptr<std::size_t>& work_count) + { + std::unique_lock<std::mutex> lock(mutex_); + if (queue_.size() > thread_count_ * 16) + { + do_work_started(work_count); + lock.unlock(); + execute(lock, p); + } + else + { + queue_.push(p); + do_work_started(work_count); + condition_.notify_one(); + } + } + + // Add a function to the queue and wake a thread. + void do_post(std::shared_ptr<function_base> p, + const std::shared_ptr<std::size_t>& work_count) + { + std::lock_guard<std::mutex> lock(mutex_); + queue_.push(p); + do_work_started(work_count); + condition_.notify_one(); + } + + // Ask all threads to shut down. + void stop_threads() + { + std::lock_guard<std::mutex> lock(mutex_); + --use_count_; + condition_.notify_all(); + } + + std::mutex mutex_; + std::condition_variable condition_; + std::queue<std::shared_ptr<function_base>> queue_; + std::size_t use_count_; + std::size_t thread_count_; + thread_pool threads_; +}; + +// A class that satisfies the Executor requirements. Every function or piece of +// work associated with a fork_executor is part of a single, joinable group. +class fork_executor +{ +public: + fork_executor(fork_join_pool& ctx) + : context_(ctx), + work_count_(std::make_shared<std::size_t>(0)) + { + } + + fork_join_pool& context() const noexcept + { + return context_; + } + + void on_work_started() const noexcept + { + std::lock_guard<std::mutex> lock(context_.mutex_); + context_.do_work_started(work_count_); + } + + void on_work_finished() const noexcept + { + std::lock_guard<std::mutex> lock(context_.mutex_); + context_.do_work_finished(work_count_); + } + + template <class Func, class Alloc> + void dispatch(Func&& f, const Alloc& a) const + { + auto p(std::allocate_shared<function<Func>>( + typename std::allocator_traits<Alloc>::template rebind_alloc<char>(a), + std::move(f), work_count_)); + context_.do_dispatch(p, work_count_); + } + + template <class Func, class Alloc> + void post(Func f, const Alloc& a) const + { + auto p(std::allocate_shared<function<Func>>( + typename std::allocator_traits<Alloc>::template rebind_alloc<char>(a), + std::move(f), work_count_)); + context_.do_post(p, work_count_); + } + + template <class Func, class Alloc> + void defer(Func&& f, const Alloc& a) const + { + post(std::forward<Func>(f), a); + } + + friend bool operator==(const fork_executor& a, + const fork_executor& b) noexcept + { + return a.work_count_ == b.work_count_; + } + + friend bool operator!=(const fork_executor& a, + const fork_executor& b) noexcept + { + return a.work_count_ != b.work_count_; + } + + // Block until all work associated with the executor is complete. While it is + // waiting, the thread may be borrowed to execute functions from the queue. + void join() const + { + std::unique_lock<std::mutex> lock(context_.mutex_); + while (*work_count_ > 0) + if (!context_.execute_next(lock)) + context_.condition_.wait(lock); + } + +private: + template <class Func> + struct function : fork_join_pool::function_base + { + explicit function(Func f, const std::shared_ptr<std::size_t>& w) + : function_(std::move(f)) + { + work_count_ = w; + execute_ = [](std::shared_ptr<fork_join_pool::function_base>& p) + { + Func tmp(std::move(static_cast<function*>(p.get())->function_)); + p.reset(); + tmp(); + }; + } + + Func function_; + }; + + fork_join_pool& context_; + std::shared_ptr<std::size_t> work_count_; +}; + +// Helper class to automatically join a fork_executor when exiting a scope. +class join_guard +{ +public: + explicit join_guard(const fork_executor& ex) : ex_(ex) {} + join_guard(const join_guard&) = delete; + join_guard(join_guard&&) = delete; + ~join_guard() { ex_.join(); } + +private: + fork_executor ex_; +}; + +//------------------------------------------------------------------------------ + +#include <algorithm> +#include <iostream> +#include <random> +#include <vector> + +fork_join_pool pool; + +template <class Iterator> +void fork_join_sort(Iterator begin, Iterator end) +{ + std::size_t n = end - begin; + if (n > 32768) + { + { + fork_executor fork(pool); + join_guard join(fork); + dispatch(fork, [=]{ fork_join_sort(begin, begin + n / 2); }); + dispatch(fork, [=]{ fork_join_sort(begin + n / 2, end); }); + } + std::inplace_merge(begin, begin + n / 2, end); + } + else + { + std::sort(begin, end); + } +} + +int main(int argc, char* argv[]) +{ + if (argc != 2) + { + std::cerr << "Usage: fork_join <size>\n"; + return 1; + } + + std::vector<double> vec(std::atoll(argv[1])); + std::iota(vec.begin(), vec.end(), 0); + + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(vec.begin(), vec.end(), g); + + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + + fork_join_sort(vec.begin(), vec.end()); + + std::chrono::steady_clock::duration elapsed = std::chrono::steady_clock::now() - start; + + std::cout << "sort took "; + std::cout << std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count(); + std::cout << " microseconds" << std::endl; +} diff --git a/src/boost/libs/asio/example/cpp14/executors/pipeline.cpp b/src/boost/libs/asio/example/cpp14/executors/pipeline.cpp new file mode 100644 index 000000000..bd837083b --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/executors/pipeline.cpp @@ -0,0 +1,294 @@ +#include <boost/asio/ts/executor.hpp> +#include <condition_variable> +#include <future> +#include <memory> +#include <mutex> +#include <queue> +#include <thread> +#include <vector> +#include <cctype> + +using boost::asio::execution_context; +using boost::asio::executor_binder; +using boost::asio::get_associated_executor; +using boost::asio::post; +using boost::asio::system_executor; +using boost::asio::use_future; +using boost::asio::use_service; + +// An executor that launches a new thread for each function submitted to it. +// This class satisfies the Executor requirements. +class thread_executor +{ +private: + // Service to track all threads started through a thread_executor. + class thread_bag : public execution_context::service + { + public: + typedef thread_bag key_type; + + explicit thread_bag(execution_context& ctx) + : execution_context::service(ctx) + { + } + + void add_thread(std::thread&& t) + { + std::unique_lock<std::mutex> lock(mutex_); + threads_.push_back(std::move(t)); + } + + private: + virtual void shutdown() + { + for (auto& t : threads_) + t.join(); + } + + std::mutex mutex_; + std::vector<std::thread> threads_; + }; + +public: + execution_context& context() const noexcept + { + return system_executor().context(); + } + + void on_work_started() const noexcept + { + // This executor doesn't count work. + } + + void on_work_finished() const noexcept + { + // This executor doesn't count work. + } + + template <class Func, class Alloc> + void dispatch(Func&& f, const Alloc& a) const + { + post(std::forward<Func>(f), a); + } + + template <class Func, class Alloc> + void post(Func f, const Alloc&) const + { + thread_bag& bag = use_service<thread_bag>(context()); + bag.add_thread(std::thread(std::move(f))); + } + + template <class Func, class Alloc> + void defer(Func&& f, const Alloc& a) const + { + post(std::forward<Func>(f), a); + } + + friend bool operator==(const thread_executor&, + const thread_executor&) noexcept + { + return true; + } + + friend bool operator!=(const thread_executor&, + const thread_executor&) noexcept + { + return false; + } +}; + +// Base class for all thread-safe queue implementations. +class queue_impl_base +{ + template <class> friend class queue_front; + template <class> friend class queue_back; + std::mutex mutex_; + std::condition_variable condition_; + bool stop_ = false; +}; + +// Underlying implementation of a thread-safe queue, shared between the +// queue_front and queue_back classes. +template <class T> +class queue_impl : public queue_impl_base +{ + template <class> friend class queue_front; + template <class> friend class queue_back; + std::queue<T> queue_; +}; + +// The front end of a queue between consecutive pipeline stages. +template <class T> +class queue_front +{ +public: + typedef T value_type; + + explicit queue_front(std::shared_ptr<queue_impl<T>> impl) + : impl_(impl) + { + } + + void push(T t) + { + std::unique_lock<std::mutex> lock(impl_->mutex_); + impl_->queue_.push(std::move(t)); + impl_->condition_.notify_one(); + } + + void stop() + { + std::unique_lock<std::mutex> lock(impl_->mutex_); + impl_->stop_ = true; + impl_->condition_.notify_one(); + } + +private: + std::shared_ptr<queue_impl<T>> impl_; +}; + +// The back end of a queue between consecutive pipeline stages. +template <class T> +class queue_back +{ +public: + typedef T value_type; + + explicit queue_back(std::shared_ptr<queue_impl<T>> impl) + : impl_(impl) + { + } + + bool pop(T& t) + { + std::unique_lock<std::mutex> lock(impl_->mutex_); + while (impl_->queue_.empty() && !impl_->stop_) + impl_->condition_.wait(lock); + if (!impl_->queue_.empty()) + { + t = impl_->queue_.front(); + impl_->queue_.pop(); + return true; + } + return false; + } + +private: + std::shared_ptr<queue_impl<T>> impl_; +}; + +// Launch the last stage in a pipeline. +template <class T, class F> +std::future<void> pipeline(queue_back<T> in, F f) +{ + // Get the function's associated executor, defaulting to thread_executor. + auto ex = get_associated_executor(f, thread_executor()); + + // Run the function, and as we're the last stage return a future so that the + // caller can wait for the pipeline to finish. + return post(ex, use_future([in, f = std::move(f)]() mutable { f(in); })); +} + +// Launch an intermediate stage in a pipeline. +template <class T, class F, class... Tail> +std::future<void> pipeline(queue_back<T> in, F f, Tail... t) +{ + // Determine the output queue type. + typedef typename executor_binder<F, thread_executor>::second_argument_type::value_type output_value_type; + + // Create the output queue and its implementation. + auto out_impl = std::make_shared<queue_impl<output_value_type>>(); + queue_front<output_value_type> out(out_impl); + queue_back<output_value_type> next_in(out_impl); + + // Get the function's associated executor, defaulting to thread_executor. + auto ex = get_associated_executor(f, thread_executor()); + + // Run the function. + post(ex, [in, out, f = std::move(f)]() mutable + { + f(in, out); + out.stop(); + }); + + // Launch the rest of the pipeline. + return pipeline(next_in, std::move(t)...); +} + +// Launch the first stage in a pipeline. +template <class F, class... Tail> +std::future<void> pipeline(F f, Tail... t) +{ + // Determine the output queue type. + typedef typename executor_binder<F, thread_executor>::argument_type::value_type output_value_type; + + // Create the output queue and its implementation. + auto out_impl = std::make_shared<queue_impl<output_value_type>>(); + queue_front<output_value_type> out(out_impl); + queue_back<output_value_type> next_in(out_impl); + + // Get the function's associated executor, defaulting to thread_executor. + auto ex = get_associated_executor(f, thread_executor()); + + // Run the function. + post(ex, [out, f = std::move(f)]() mutable + { + f(out); + out.stop(); + }); + + // Launch the rest of the pipeline. + return pipeline(next_in, std::move(t)...); +} + +//------------------------------------------------------------------------------ + +#include <boost/asio/thread_pool.hpp> +#include <iostream> +#include <string> + +using boost::asio::bind_executor; +using boost::asio::thread_pool; + +void reader(queue_front<std::string> out) +{ + std::string line; + while (std::getline(std::cin, line)) + out.push(line); +} + +void filter(queue_back<std::string> in, queue_front<std::string> out) +{ + std::string line; + while (in.pop(line)) + if (line.length() > 5) + out.push(line); +} + +void upper(queue_back<std::string> in, queue_front<std::string> out) +{ + std::string line; + while (in.pop(line)) + { + std::string new_line; + for (char c : line) + new_line.push_back(std::toupper(c)); + out.push(new_line); + } +} + +void writer(queue_back<std::string> in) +{ + std::size_t count = 0; + std::string line; + while (in.pop(line)) + std::cout << count++ << ": " << line << std::endl; +} + +int main() +{ + thread_pool pool; + + auto f = pipeline(reader, filter, bind_executor(pool, upper), writer); + f.wait(); +} diff --git a/src/boost/libs/asio/example/cpp14/executors/priority_scheduler.cpp b/src/boost/libs/asio/example/cpp14/executors/priority_scheduler.cpp new file mode 100644 index 000000000..f11853192 --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/executors/priority_scheduler.cpp @@ -0,0 +1,173 @@ +#include <boost/asio/ts/executor.hpp> +#include <condition_variable> +#include <iostream> +#include <memory> +#include <mutex> +#include <queue> + +using boost::asio::dispatch; +using boost::asio::execution_context; + +class priority_scheduler : public execution_context +{ +public: + // A class that satisfies the Executor requirements. + class executor_type + { + public: + executor_type(priority_scheduler& ctx, int pri) noexcept + : context_(ctx), priority_(pri) + { + } + + priority_scheduler& context() const noexcept + { + return context_; + } + + void on_work_started() const noexcept + { + // This executor doesn't count work. Instead, the scheduler simply runs + // until explicitly stopped. + } + + void on_work_finished() const noexcept + { + // This executor doesn't count work. Instead, the scheduler simply runs + // until explicitly stopped. + } + + template <class Func, class Alloc> + void dispatch(Func&& f, const Alloc& a) const + { + post(std::forward<Func>(f), a); + } + + template <class Func, class Alloc> + void post(Func f, const Alloc& a) const + { + auto p(std::allocate_shared<item<Func>>( + typename std::allocator_traits< + Alloc>::template rebind_alloc<char>(a), + priority_, std::move(f))); + std::lock_guard<std::mutex> lock(context_.mutex_); + context_.queue_.push(p); + context_.condition_.notify_one(); + } + + template <class Func, class Alloc> + void defer(Func&& f, const Alloc& a) const + { + post(std::forward<Func>(f), a); + } + + friend bool operator==(const executor_type& a, + const executor_type& b) noexcept + { + return &a.context_ == &b.context_; + } + + friend bool operator!=(const executor_type& a, + const executor_type& b) noexcept + { + return &a.context_ != &b.context_; + } + + private: + priority_scheduler& context_; + int priority_; + }; + + ~priority_scheduler() noexcept + { + shutdown(); + destroy(); + } + + executor_type get_executor(int pri = 0) noexcept + { + return executor_type(*const_cast<priority_scheduler*>(this), pri); + } + + void run() + { + std::unique_lock<std::mutex> lock(mutex_); + for (;;) + { + condition_.wait(lock, [&]{ return stopped_ || !queue_.empty(); }); + if (stopped_) + return; + auto p(queue_.top()); + queue_.pop(); + lock.unlock(); + p->execute_(p); + lock.lock(); + } + } + + void stop() + { + std::lock_guard<std::mutex> lock(mutex_); + stopped_ = true; + condition_.notify_all(); + } + +private: + struct item_base + { + int priority_; + void (*execute_)(std::shared_ptr<item_base>&); + }; + + template <class Func> + struct item : item_base + { + item(int pri, Func f) : function_(std::move(f)) + { + priority_ = pri; + execute_ = [](std::shared_ptr<item_base>& p) + { + Func tmp(std::move(static_cast<item*>(p.get())->function_)); + p.reset(); + tmp(); + }; + } + + Func function_; + }; + + struct item_comp + { + bool operator()( + const std::shared_ptr<item_base>& a, + const std::shared_ptr<item_base>& b) + { + return a->priority_ < b->priority_; + } + }; + + std::mutex mutex_; + std::condition_variable condition_; + std::priority_queue< + std::shared_ptr<item_base>, + std::vector<std::shared_ptr<item_base>>, + item_comp> queue_; + bool stopped_ = false; +}; + +int main() +{ + priority_scheduler sched; + auto low = sched.get_executor(0); + auto med = sched.get_executor(1); + auto high = sched.get_executor(2); + dispatch(low, []{ std::cout << "1\n"; }); + dispatch(low, []{ std::cout << "11\n"; }); + dispatch(med, []{ std::cout << "2\n"; }); + dispatch(med, []{ std::cout << "22\n"; }); + dispatch(high, []{ std::cout << "3\n"; }); + dispatch(high, []{ std::cout << "33\n"; }); + dispatch(high, []{ std::cout << "333\n"; }); + dispatch(sched.get_executor(-1), [&]{ sched.stop(); }); + sched.run(); +} diff --git a/src/boost/libs/asio/example/cpp14/operations/Jamfile.v2 b/src/boost/libs/asio/example/cpp14/operations/Jamfile.v2 new file mode 100644 index 000000000..a01b42a3a --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/operations/Jamfile.v2 @@ -0,0 +1,39 @@ +# +# Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +project + : requirements + <library>/boost/system//boost_system + <library>/boost/chrono//boost_chrono + <define>BOOST_ALL_NO_LIB=1 + <threading>multi + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + <target-os>windows:<define>_WIN32_WINNT=0x0501 + <target-os>windows,<toolset>gcc:<library>ws2_32 + <target-os>windows,<toolset>gcc:<library>mswsock + <target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS + <target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED + <target-os>hpux:<library>ipv6 + <target-os>haiku:<library>network + ; + +exe composed_1 : composed_1.cpp ; +exe composed_2 : composed_2.cpp ; +exe composed_3 : composed_3.cpp ; +exe composed_4 : composed_4.cpp ; +exe composed_5 : composed_5.cpp ; +exe composed_6 : composed_6.cpp ; +exe composed_7 : composed_7.cpp ; +exe composed_8 : composed_8.cpp ; diff --git a/src/boost/libs/asio/example/cpp14/operations/composed_1.cpp b/src/boost/libs/asio/example/cpp14/operations/composed_1.cpp new file mode 100644 index 000000000..4439fef6e --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/operations/composed_1.cpp @@ -0,0 +1,113 @@ +// +// composed_1.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <cstring> +#include <iostream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +//------------------------------------------------------------------------------ + +// This is the simplest example of a composed asynchronous operation, where we +// simply repackage an existing operation. The asynchronous operation +// requirements are met by delegating responsibility to the underlying +// operation. + +template <typename CompletionToken> +auto async_write_message(tcp::socket& socket, + const char* message, CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is void. However, + // when the completion token is boost::asio::yield_context (used for stackful + // coroutines) the return type would be std::size_t, and when the completion + // token is boost::asio::use_future it would be std::future<std::size_t>. + // + // In C++14 we can omit the return type as it is automatically deduced from + // the return type of our underlying asynchronous operation +{ + // When delegating to the underlying operation we must take care to perfectly + // forward the completion token. This ensures that our operation works + // correctly with move-only function objects as callbacks, as well as other + // completion token types. + return boost::asio::async_write(socket, + boost::asio::buffer(message, std::strlen(message)), + std::forward<CompletionToken>(token)); +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_message(socket, "Testing callback\r\n", + [](const boost::system::error_code& error, std::size_t n) + { + if (!error) + { + std::cout << n << " bytes transferred\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<std::size_t> f = async_write_message( + socket, "Testing future\r\n", boost::asio::use_future); + + io_context.run(); + + try + { + // Get the result of the operation. + std::size_t n = f.get(); + std::cout << n << " bytes transferred\n"; + } + catch (const std::exception& e) + { + std::cout << "Error: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp14/operations/composed_2.cpp b/src/boost/libs/asio/example/cpp14/operations/composed_2.cpp new file mode 100644 index 000000000..4c16776a5 --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/operations/composed_2.cpp @@ -0,0 +1,131 @@ +// +// composed_2.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <cstring> +#include <iostream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +//------------------------------------------------------------------------------ + +// This next simplest example of a composed asynchronous operation involves +// repackaging multiple operations but choosing to invoke just one of them. All +// of these underlying operations have the same completion signature. The +// asynchronous operation requirements are met by delegating responsibility to +// the underlying operations. + +template <typename CompletionToken> +auto async_write_message(tcp::socket& socket, + const char* message, bool allow_partial_write, + CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is void. However, + // when the completion token is boost::asio::yield_context (used for stackful + // coroutines) the return type would be std::size_t, and when the completion + // token is boost::asio::use_future it would be std::future<std::size_t>. + // + // In C++14 we can omit the return type as it is automatically deduced from + // the return type of our underlying asynchronous operation +{ + // As the return type of the initiating function is deduced solely from the + // CompletionToken and completion signature, we know that two different + // asynchronous operations having the same completion signature will produce + // the same return type, when passed the same CompletionToken. This allows us + // to trivially delegate to alternate implementations. + if (allow_partial_write) + { + // When delegating to an underlying operation we must take care to + // perfectly forward the completion token. This ensures that our operation + // works correctly with move-only function objects as callbacks, as well as + // other completion token types. + return socket.async_write_some( + boost::asio::buffer(message, std::strlen(message)), + std::forward<CompletionToken>(token)); + } + else + { + // As above, we must perfectly forward the completion token when calling + // the alternate underlying operation. + return boost::asio::async_write(socket, + boost::asio::buffer(message, std::strlen(message)), + std::forward<CompletionToken>(token)); + } +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_message(socket, "Testing callback\r\n", false, + [](const boost::system::error_code& error, std::size_t n) + { + if (!error) + { + std::cout << n << " bytes transferred\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<std::size_t> f = async_write_message( + socket, "Testing future\r\n", false, boost::asio::use_future); + + io_context.run(); + + try + { + // Get the result of the operation. + std::size_t n = f.get(); + std::cout << n << " bytes transferred\n"; + } + catch (const std::exception& e) + { + std::cout << "Error: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp14/operations/composed_3.cpp b/src/boost/libs/asio/example/cpp14/operations/composed_3.cpp new file mode 100644 index 000000000..00ab11206 --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/operations/composed_3.cpp @@ -0,0 +1,186 @@ +// +// composed_3.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/bind_executor.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <cstring> +#include <functional> +#include <iostream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +// NOTE: This example requires the new boost::asio::async_initiate function. For +// an example that works with the Networking TS style of completion tokens, +// please see an older version of asio. + +//------------------------------------------------------------------------------ + +// In this composed operation we repackage an existing operation, but with a +// different completion handler signature. The asynchronous operation +// requirements are met by delegating responsibility to the underlying +// operation. + +template <typename CompletionToken> +auto async_write_message(tcp::socket& socket, + const char* message, CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is always void. + // In this example, when the completion token is boost::asio::yield_context + // (used for stackful coroutines) the return type would be also be void, as + // there is no non-error argument to the completion handler. When the + // completion token is boost::asio::use_future it would be std::future<void>. + // + // In C++14 we can omit the return type as it is automatically deduced from + // the return type of boost::asio::async_initiate. +{ + // In addition to determining the mechanism by which an asynchronous + // operation delivers its result, a completion token also determines the time + // when the operation commences. For example, when the completion token is a + // simple callback the operation commences before the initiating function + // returns. However, if the completion token's delivery mechanism uses a + // future, we might instead want to defer initiation of the operation until + // the returned future object is waited upon. + // + // To enable this, when implementing an asynchronous operation we must + // package the initiation step as a function object. The initiation function + // object's call operator is passed the concrete completion handler produced + // by the completion token. This completion handler matches the asynchronous + // operation's completion handler signature, which in this example is: + // + // void(boost::system::error_code error) + // + // The initiation function object also receives any additional arguments + // required to start the operation. (Note: We could have instead passed these + // arguments in the lambda capture set. However, we should prefer to + // propagate them as function call arguments as this allows the completion + // token to optimise how they are passed. For example, a lazy future which + // defers initiation would need to make a decay-copy of the arguments, but + // when using a simple callback the arguments can be trivially forwarded + // straight through.) + auto initiation = [](auto&& completion_handler, + tcp::socket& socket, const char* message) + { + // The async_write operation has a completion handler signature of: + // + // void(boost::system::error_code error, std::size n) + // + // This differs from our operation's signature in that it is also passed + // the number of bytes transferred as an argument of type std::size_t. We + // will adapt our completion handler to async_write's completion handler + // signature by using std::bind, which drops the additional argument. + // + // However, it is essential to the correctness of our composed operation + // that we preserve the executor of the user-supplied completion handler. + // The std::bind function will not do this for us, so we must do this by + // first obtaining the completion handler's associated executor (defaulting + // to the I/O executor - in this case the executor of the socket - if the + // completion handler does not have its own) ... + auto executor = boost::asio::get_associated_executor( + completion_handler, socket.get_executor()); + + // ... and then binding this executor to our adapted completion handler + // using the boost::asio::bind_executor function. + boost::asio::async_write(socket, + boost::asio::buffer(message, std::strlen(message)), + boost::asio::bind_executor(executor, + std::bind(std::forward<decltype(completion_handler)>( + completion_handler), std::placeholders::_1))); + }; + + // The boost::asio::async_initiate function takes: + // + // - our initiation function object, + // - the completion token, + // - the completion handler signature, and + // - any additional arguments we need to initiate the operation. + // + // It then asks the completion token to create a completion handler (i.e. a + // callback) with the specified signature, and invoke the initiation function + // object with this completion handler as well as the additional arguments. + // The return value of async_initiate is the result of our operation's + // initiating function. + // + // Note that we wrap non-const reference arguments in std::reference_wrapper + // to prevent incorrect decay-copies of these objects. + return boost::asio::async_initiate< + CompletionToken, void(boost::system::error_code)>( + initiation, token, std::ref(socket), message); +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_message(socket, "Testing callback\r\n", + [](const boost::system::error_code& error) + { + if (!error) + { + std::cout << "Message sent\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<void> f = async_write_message( + socket, "Testing future\r\n", boost::asio::use_future); + + io_context.run(); + + // Get the result of the operation. + try + { + // Get the result of the operation. + f.get(); + std::cout << "Message sent\n"; + } + catch (const std::exception& e) + { + std::cout << "Error: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp14/operations/composed_4.cpp b/src/boost/libs/asio/example/cpp14/operations/composed_4.cpp new file mode 100644 index 000000000..65e0bf21b --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/operations/composed_4.cpp @@ -0,0 +1,201 @@ +// +// composed_4.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/bind_executor.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <cstring> +#include <functional> +#include <iostream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +// NOTE: This example requires the new boost::asio::async_initiate function. For +// an example that works with the Networking TS style of completion tokens, +// please see an older version of asio. + +//------------------------------------------------------------------------------ + +// In this composed operation we repackage an existing operation, but with a +// different completion handler signature. We will also intercept an empty +// message as an invalid argument, and propagate the corresponding error to the +// user. The asynchronous operation requirements are met by delegating +// responsibility to the underlying operation. + +template <typename CompletionToken> +auto async_write_message(tcp::socket& socket, + const char* message, CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is always void. + // In this example, when the completion token is boost::asio::yield_context + // (used for stackful coroutines) the return type would be also be void, as + // there is no non-error argument to the completion handler. When the + // completion token is boost::asio::use_future it would be std::future<void>. + // + // In C++14 we can omit the return type as it is automatically deduced from + // the return type of boost::asio::async_initiate. +{ + // In addition to determining the mechanism by which an asynchronous + // operation delivers its result, a completion token also determines the time + // when the operation commences. For example, when the completion token is a + // simple callback the operation commences before the initiating function + // returns. However, if the completion token's delivery mechanism uses a + // future, we might instead want to defer initiation of the operation until + // the returned future object is waited upon. + // + // To enable this, when implementing an asynchronous operation we must + // package the initiation step as a function object. The initiation function + // object's call operator is passed the concrete completion handler produced + // by the completion token. This completion handler matches the asynchronous + // operation's completion handler signature, which in this example is: + // + // void(boost::system::error_code error) + // + // The initiation function object also receives any additional arguments + // required to start the operation. (Note: We could have instead passed these + // arguments in the lambda capture set. However, we should prefer to + // propagate them as function call arguments as this allows the completion + // token to optimise how they are passed. For example, a lazy future which + // defers initiation would need to make a decay-copy of the arguments, but + // when using a simple callback the arguments can be trivially forwarded + // straight through.) + auto initiation = [](auto&& completion_handler, + tcp::socket& socket, const char* message) + { + // The post operation has a completion handler signature of: + // + // void() + // + // and the async_write operation has a completion handler signature of: + // + // void(boost::system::error_code error, std::size n) + // + // Both of these operations' completion handler signatures differ from our + // operation's completion handler signature. We will adapt our completion + // handler to these signatures by using std::bind, which drops the + // additional arguments. + // + // However, it is essential to the correctness of our composed operation + // that we preserve the executor of the user-supplied completion handler. + // The std::bind function will not do this for us, so we must do this by + // first obtaining the completion handler's associated executor (defaulting + // to the I/O executor - in this case the executor of the socket - if the + // completion handler does not have its own) ... + auto executor = boost::asio::get_associated_executor( + completion_handler, socket.get_executor()); + + // ... and then binding this executor to our adapted completion handler + // using the boost::asio::bind_executor function. + std::size_t length = std::strlen(message); + if (length == 0) + { + boost::asio::post( + boost::asio::bind_executor(executor, + std::bind(std::forward<decltype(completion_handler)>( + completion_handler), boost::asio::error::invalid_argument))); + } + else + { + boost::asio::async_write(socket, + boost::asio::buffer(message, length), + boost::asio::bind_executor(executor, + std::bind(std::forward<decltype(completion_handler)>( + completion_handler), std::placeholders::_1))); + } + }; + + // The boost::asio::async_initiate function takes: + // + // - our initiation function object, + // - the completion token, + // - the completion handler signature, and + // - any additional arguments we need to initiate the operation. + // + // It then asks the completion token to create a completion handler (i.e. a + // callback) with the specified signature, and invoke the initiation function + // object with this completion handler as well as the additional arguments. + // The return value of async_initiate is the result of our operation's + // initiating function. + // + // Note that we wrap non-const reference arguments in std::reference_wrapper + // to prevent incorrect decay-copies of these objects. + return boost::asio::async_initiate< + CompletionToken, void(boost::system::error_code)>( + initiation, token, std::ref(socket), message); +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_message(socket, "", + [](const boost::system::error_code& error) + { + if (!error) + { + std::cout << "Message sent\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<void> f = async_write_message( + socket, "", boost::asio::use_future); + + io_context.run(); + + try + { + // Get the result of the operation. + f.get(); + std::cout << "Message sent\n"; + } + catch (const std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp14/operations/composed_5.cpp b/src/boost/libs/asio/example/cpp14/operations/composed_5.cpp new file mode 100644 index 000000000..f7bcb169a --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/operations/composed_5.cpp @@ -0,0 +1,238 @@ +// +// composed_5.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <functional> +#include <iostream> +#include <memory> +#include <sstream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +// NOTE: This example requires the new boost::asio::async_initiate function. For +// an example that works with the Networking TS style of completion tokens, +// please see an older version of asio. + +//------------------------------------------------------------------------------ + +// This composed operation automatically serialises a message, using its I/O +// streams insertion operator, before sending it on the socket. To do this, it +// must allocate a buffer for the encoded message and ensure this buffer's +// validity until the underlying async_write operation completes. + +template <typename T, typename CompletionToken> +auto async_write_message(tcp::socket& socket, + const T& message, CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is always void. + // In this example, when the completion token is boost::asio::yield_context + // (used for stackful coroutines) the return type would be also be void, as + // there is no non-error argument to the completion handler. When the + // completion token is boost::asio::use_future it would be std::future<void>. + // + // In C++14 we can omit the return type as it is automatically deduced from + // the return type of boost::asio::async_initiate. +{ + // In addition to determining the mechanism by which an asynchronous + // operation delivers its result, a completion token also determines the time + // when the operation commences. For example, when the completion token is a + // simple callback the operation commences before the initiating function + // returns. However, if the completion token's delivery mechanism uses a + // future, we might instead want to defer initiation of the operation until + // the returned future object is waited upon. + // + // To enable this, when implementing an asynchronous operation we must + // package the initiation step as a function object. The initiation function + // object's call operator is passed the concrete completion handler produced + // by the completion token. This completion handler matches the asynchronous + // operation's completion handler signature, which in this example is: + // + // void(boost::system::error_code error) + // + // The initiation function object also receives any additional arguments + // required to start the operation. (Note: We could have instead passed these + // arguments in the lambda capture set. However, we should prefer to + // propagate them as function call arguments as this allows the completion + // token to optimise how they are passed. For example, a lazy future which + // defers initiation would need to make a decay-copy of the arguments, but + // when using a simple callback the arguments can be trivially forwarded + // straight through.) + auto initiation = [](auto&& completion_handler, + tcp::socket& socket, std::unique_ptr<std::string> encoded_message) + { + // In this example, the composed operation's intermediate completion + // handler is implemented as a hand-crafted function object, rather than + // using a lambda or std::bind. + struct intermediate_completion_handler + { + // The intermediate completion handler holds a reference to the socket so + // that it can obtain the I/O executor (see get_executor below). + tcp::socket& socket_; + + // The allocated buffer for the encoded message. The std::unique_ptr + // smart pointer is move-only, and as a consequence our intermediate + // completion handler is also move-only. + std::unique_ptr<std::string> encoded_message_; + + // The user-supplied completion handler. + typename std::decay<decltype(completion_handler)>::type handler_; + + // The function call operator matches the completion signature of the + // async_write operation. + void operator()(const boost::system::error_code& error, std::size_t /*n*/) + { + // Deallocate the encoded message before calling the user-supplied + // completion handler. + encoded_message_.reset(); + + // Call the user-supplied handler with the result of the operation. + // The arguments must match the completion signature of our composed + // operation. + handler_(error); + } + + // It is essential to the correctness of our composed operation that we + // preserve the executor of the user-supplied completion handler. With a + // hand-crafted function object we can do this by defining a nested type + // executor_type and member function get_executor. These obtain the + // completion handler's associated executor, and default to the I/O + // executor - in this case the executor of the socket - if the completion + // handler does not have its own. + using executor_type = boost::asio::associated_executor_t< + typename std::decay<decltype(completion_handler)>::type, + tcp::socket::executor_type>; + + executor_type get_executor() const noexcept + { + return boost::asio::get_associated_executor( + handler_, socket_.get_executor()); + } + + // Although not necessary for correctness, we may also preserve the + // allocator of the user-supplied completion handler. This is achieved by + // defining a nested type allocator_type and member function + // get_allocator. These obtain the completion handler's associated + // allocator, and default to std::allocator<void> if the completion + // handler does not have its own. + using allocator_type = boost::asio::associated_allocator_t< + typename std::decay<decltype(completion_handler)>::type, + std::allocator<void>>; + + allocator_type get_allocator() const noexcept + { + return boost::asio::get_associated_allocator( + handler_, std::allocator<void>{}); + } + }; + + // Initiate the underlying async_write operation using our intermediate + // completion handler. + auto encoded_message_buffer = boost::asio::buffer(*encoded_message); + boost::asio::async_write(socket, encoded_message_buffer, + intermediate_completion_handler{socket, std::move(encoded_message), + std::forward<decltype(completion_handler)>(completion_handler)}); + }; + + // Encode the message and copy it into an allocated buffer. The buffer will + // be maintained for the lifetime of the asynchronous operation. + std::ostringstream os; + os << message; + std::unique_ptr<std::string> encoded_message(new std::string(os.str())); + + // The boost::asio::async_initiate function takes: + // + // - our initiation function object, + // - the completion token, + // - the completion handler signature, and + // - any additional arguments we need to initiate the operation. + // + // It then asks the completion token to create a completion handler (i.e. a + // callback) with the specified signature, and invoke the initiation function + // object with this completion handler as well as the additional arguments. + // The return value of async_initiate is the result of our operation's + // initiating function. + // + // Note that we wrap non-const reference arguments in std::reference_wrapper + // to prevent incorrect decay-copies of these objects. + return boost::asio::async_initiate< + CompletionToken, void(boost::system::error_code)>( + initiation, token, std::ref(socket), + std::move(encoded_message)); +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_message(socket, 123456, + [](const boost::system::error_code& error) + { + if (!error) + { + std::cout << "Message sent\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<void> f = async_write_message( + socket, 654.321, boost::asio::use_future); + + io_context.run(); + + try + { + // Get the result of the operation. + f.get(); + std::cout << "Message sent\n"; + } + catch (const std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp14/operations/composed_6.cpp b/src/boost/libs/asio/example/cpp14/operations/composed_6.cpp new file mode 100644 index 000000000..8b6d01190 --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/operations/composed_6.cpp @@ -0,0 +1,298 @@ +// +// composed_6.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/executor_work_guard.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <functional> +#include <iostream> +#include <memory> +#include <sstream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +// NOTE: This example requires the new boost::asio::async_initiate function. For +// an example that works with the Networking TS style of completion tokens, +// please see an older version of asio. + +//------------------------------------------------------------------------------ + +// This composed operation shows composition of multiple underlying operations. +// It automatically serialises a message, using its I/O streams insertion +// operator, before sending it N times on the socket. To do this, it must +// allocate a buffer for the encoded message and ensure this buffer's validity +// until all underlying async_write operation complete. A one second delay is +// inserted prior to each write operation, using a steady_timer. + +template <typename T, typename CompletionToken> +auto async_write_messages(tcp::socket& socket, + const T& message, std::size_t repeat_count, + CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is always void. + // In this example, when the completion token is boost::asio::yield_context + // (used for stackful coroutines) the return type would be also be void, as + // there is no non-error argument to the completion handler. When the + // completion token is boost::asio::use_future it would be std::future<void>. + // + // In C++14 we can omit the return type as it is automatically deduced from + // the return type of boost::asio::async_initiate. +{ + // In addition to determining the mechanism by which an asynchronous + // operation delivers its result, a completion token also determines the time + // when the operation commences. For example, when the completion token is a + // simple callback the operation commences before the initiating function + // returns. However, if the completion token's delivery mechanism uses a + // future, we might instead want to defer initiation of the operation until + // the returned future object is waited upon. + // + // To enable this, when implementing an asynchronous operation we must + // package the initiation step as a function object. The initiation function + // object's call operator is passed the concrete completion handler produced + // by the completion token. This completion handler matches the asynchronous + // operation's completion handler signature, which in this example is: + // + // void(boost::system::error_code error) + // + // The initiation function object also receives any additional arguments + // required to start the operation. (Note: We could have instead passed these + // arguments in the lambda capture set. However, we should prefer to + // propagate them as function call arguments as this allows the completion + // token to optimise how they are passed. For example, a lazy future which + // defers initiation would need to make a decay-copy of the arguments, but + // when using a simple callback the arguments can be trivially forwarded + // straight through.) + auto initiation = [](auto&& completion_handler, tcp::socket& socket, + std::unique_ptr<std::string> encoded_message, std::size_t repeat_count, + std::unique_ptr<boost::asio::steady_timer> delay_timer) + { + // In this example, the composed operation's intermediate completion + // handler is implemented as a hand-crafted function object. + struct intermediate_completion_handler + { + // The intermediate completion handler holds a reference to the socket as + // it is used for multiple async_write operations, as well as for + // obtaining the I/O executor (see get_executor below). + tcp::socket& socket_; + + // The allocated buffer for the encoded message. The std::unique_ptr + // smart pointer is move-only, and as a consequence our intermediate + // completion handler is also move-only. + std::unique_ptr<std::string> encoded_message_; + + // The repeat count remaining. + std::size_t repeat_count_; + + // A steady timer used for introducing a delay. + std::unique_ptr<boost::asio::steady_timer> delay_timer_; + + // To manage the cycle between the multiple underlying asychronous + // operations, our intermediate completion handler is implemented as a + // state machine. + enum { starting, waiting, writing } state_; + + // As our composed operation performs multiple underlying I/O operations, + // we should maintain a work object against the I/O executor. This tells + // the I/O executor that there is still more work to come in the future. + boost::asio::executor_work_guard<tcp::socket::executor_type> io_work_; + + // The user-supplied completion handler, called once only on completion + // of the entire composed operation. + typename std::decay<decltype(completion_handler)>::type handler_; + + // By having a default value for the second argument, this function call + // operator matches the completion signature of both the async_write and + // steady_timer::async_wait operations. + void operator()(const boost::system::error_code& error, std::size_t = 0) + { + if (!error) + { + switch (state_) + { + case starting: + case writing: + if (repeat_count_ > 0) + { + --repeat_count_; + state_ = waiting; + delay_timer_->expires_after(std::chrono::seconds(1)); + delay_timer_->async_wait(std::move(*this)); + return; // Composed operation not yet complete. + } + break; // Composed operation complete, continue below. + case waiting: + state_ = writing; + boost::asio::async_write(socket_, + boost::asio::buffer(*encoded_message_), std::move(*this)); + return; // Composed operation not yet complete. + } + } + + // This point is reached only on completion of the entire composed + // operation. + + // We no longer have any future work coming for the I/O executor. + io_work_.reset(); + + // Deallocate the encoded message before calling the user-supplied + // completion handler. + encoded_message_.reset(); + + // Call the user-supplied handler with the result of the operation. + handler_(error); + } + + // It is essential to the correctness of our composed operation that we + // preserve the executor of the user-supplied completion handler. With a + // hand-crafted function object we can do this by defining a nested type + // executor_type and member function get_executor. These obtain the + // completion handler's associated executor, and default to the I/O + // executor - in this case the executor of the socket - if the completion + // handler does not have its own. + using executor_type = boost::asio::associated_executor_t< + typename std::decay<decltype(completion_handler)>::type, + tcp::socket::executor_type>; + + executor_type get_executor() const noexcept + { + return boost::asio::get_associated_executor( + handler_, socket_.get_executor()); + } + + // Although not necessary for correctness, we may also preserve the + // allocator of the user-supplied completion handler. This is achieved by + // defining a nested type allocator_type and member function + // get_allocator. These obtain the completion handler's associated + // allocator, and default to std::allocator<void> if the completion + // handler does not have its own. + using allocator_type = boost::asio::associated_allocator_t< + typename std::decay<decltype(completion_handler)>::type, + std::allocator<void>>; + + allocator_type get_allocator() const noexcept + { + return boost::asio::get_associated_allocator( + handler_, std::allocator<void>{}); + } + }; + + // Initiate the underlying async_write operation using our intermediate + // completion handler. + auto encoded_message_buffer = boost::asio::buffer(*encoded_message); + boost::asio::async_write(socket, encoded_message_buffer, + intermediate_completion_handler{ + socket, std::move(encoded_message), + repeat_count, std::move(delay_timer), + intermediate_completion_handler::starting, + boost::asio::make_work_guard(socket.get_executor()), + std::forward<decltype(completion_handler)>(completion_handler)}); + }; + + // Encode the message and copy it into an allocated buffer. The buffer will + // be maintained for the lifetime of the composed asynchronous operation. + std::ostringstream os; + os << message; + std::unique_ptr<std::string> encoded_message(new std::string(os.str())); + + // Create a steady_timer to be used for the delay between messages. + std::unique_ptr<boost::asio::steady_timer> delay_timer( + new boost::asio::steady_timer(socket.get_executor())); + + // The boost::asio::async_initiate function takes: + // + // - our initiation function object, + // - the completion token, + // - the completion handler signature, and + // - any additional arguments we need to initiate the operation. + // + // It then asks the completion token to create a completion handler (i.e. a + // callback) with the specified signature, and invoke the initiation function + // object with this completion handler as well as the additional arguments. + // The return value of async_initiate is the result of our operation's + // initiating function. + // + // Note that we wrap non-const reference arguments in std::reference_wrapper + // to prevent incorrect decay-copies of these objects. + return boost::asio::async_initiate< + CompletionToken, void(boost::system::error_code)>( + initiation, token, std::ref(socket), + std::move(encoded_message), repeat_count, + std::move(delay_timer)); +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_messages(socket, "Testing callback\r\n", 5, + [](const boost::system::error_code& error) + { + if (!error) + { + std::cout << "Messages sent\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<void> f = async_write_messages( + socket, "Testing future\r\n", 5, boost::asio::use_future); + + io_context.run(); + + try + { + // Get the result of the operation. + f.get(); + std::cout << "Messages sent\n"; + } + catch (const std::exception& e) + { + std::cout << "Error: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp14/operations/composed_7.cpp b/src/boost/libs/asio/example/cpp14/operations/composed_7.cpp new file mode 100644 index 000000000..352693085 --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/operations/composed_7.cpp @@ -0,0 +1,219 @@ +// +// composed_7.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/compose.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <functional> +#include <iostream> +#include <memory> +#include <sstream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +// NOTE: This example requires the new boost::asio::async_compose function. For +// an example that works with the Networking TS style of completion tokens, +// please see an older version of asio. + +//------------------------------------------------------------------------------ + +// This composed operation shows composition of multiple underlying operations. +// It automatically serialises a message, using its I/O streams insertion +// operator, before sending it N times on the socket. To do this, it must +// allocate a buffer for the encoded message and ensure this buffer's validity +// until all underlying async_write operation complete. A one second delay is +// inserted prior to each write operation, using a steady_timer. + +template <typename T, typename CompletionToken> +auto async_write_messages(tcp::socket& socket, + const T& message, std::size_t repeat_count, + CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is always void. + // In this example, when the completion token is boost::asio::yield_context + // (used for stackful coroutines) the return type would be also be void, as + // there is no non-error argument to the completion handler. When the + // completion token is boost::asio::use_future it would be std::future<void>. + // + // In C++14 we can omit the return type as it is automatically deduced from + // the return type of boost::asio::async_initiate. +{ + // Encode the message and copy it into an allocated buffer. The buffer will + // be maintained for the lifetime of the composed asynchronous operation. + std::ostringstream os; + os << message; + std::unique_ptr<std::string> encoded_message(new std::string(os.str())); + + // Create a steady_timer to be used for the delay between messages. + std::unique_ptr<boost::asio::steady_timer> delay_timer( + new boost::asio::steady_timer(socket.get_executor())); + + // To manage the cycle between the multiple underlying asychronous + // operations, our implementation is a state machine. + enum { starting, waiting, writing }; + + // The boost::asio::async_compose function takes: + // + // - our asynchronous operation implementation, + // - the completion token, + // - the completion handler signature, and + // - any I/O objects (or executors) used by the operation + // + // It then wraps our implementation, which is implemented here as a state + // machine in a lambda, in an intermediate completion handler that meets the + // requirements of a conforming asynchronous operation. This includes + // tracking outstanding work against the I/O executors associated with the + // operation (in this example, this is the socket's executor). + // + // The first argument to our lambda is a reference to the enclosing + // intermediate completion handler. This intermediate completion handler is + // provided for us by the boost::asio::async_compose function, and takes care + // of all the details required to implement a conforming asynchronous + // operation. When calling an underlying asynchronous operation, we pass it + // this enclosing intermediate completion handler as the completion token. + // + // All arguments to our lambda after the first must be defaulted to allow the + // state machine to be started, as well as to allow the completion handler to + // match the completion signature of both the async_write and + // steady_timer::async_wait operations. + return boost::asio::async_compose< + CompletionToken, void(boost::system::error_code)>( + [ + // The implementation holds a reference to the socket as it is used for + // multiple async_write operations. + &socket, + + // The allocated buffer for the encoded message. The std::unique_ptr + // smart pointer is move-only, and as a consequence our lambda + // implementation is also move-only. + encoded_message = std::move(encoded_message), + + // The repeat count remaining. + repeat_count, + + // A steady timer used for introducing a delay. + delay_timer = std::move(delay_timer), + + // To manage the cycle between the multiple underlying asychronous + // operations, our implementation is a state machine. + state = starting + ] + ( + auto& self, + const boost::system::error_code& error = {}, + std::size_t = 0 + ) mutable + { + if (!error) + { + switch (state) + { + case starting: + case writing: + if (repeat_count > 0) + { + --repeat_count; + state = waiting; + delay_timer->expires_after(std::chrono::seconds(1)); + delay_timer->async_wait(std::move(self)); + return; // Composed operation not yet complete. + } + break; // Composed operation complete, continue below. + case waiting: + state = writing; + boost::asio::async_write(socket, + boost::asio::buffer(*encoded_message), std::move(self)); + return; // Composed operation not yet complete. + } + } + + // This point is reached only on completion of the entire composed + // operation. + + // Deallocate the encoded message and delay timer before calling the + // user-supplied completion handler. + encoded_message.reset(); + delay_timer.reset(); + + // Call the user-supplied handler with the result of the operation. + self.complete(error); + }, + token, socket); +} + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_messages(socket, "Testing callback\r\n", 5, + [](const boost::system::error_code& error) + { + if (!error) + { + std::cout << "Messages sent\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<void> f = async_write_messages( + socket, "Testing future\r\n", 5, boost::asio::use_future); + + io_context.run(); + + try + { + // Get the result of the operation. + f.get(); + std::cout << "Messages sent\n"; + } + catch (const std::exception& e) + { + std::cout << "Error: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp14/operations/composed_8.cpp b/src/boost/libs/asio/example/cpp14/operations/composed_8.cpp new file mode 100644 index 000000000..9fe948cd6 --- /dev/null +++ b/src/boost/libs/asio/example/cpp14/operations/composed_8.cpp @@ -0,0 +1,212 @@ +// +// composed_8.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/compose.hpp> +#include <boost/asio/coroutine.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/asio/use_future.hpp> +#include <boost/asio/write.hpp> +#include <functional> +#include <iostream> +#include <memory> +#include <sstream> +#include <string> +#include <type_traits> +#include <utility> + +using boost::asio::ip::tcp; + +// NOTE: This example requires the new boost::asio::async_compose function. For +// an example that works with the Networking TS style of completion tokens, +// please see an older version of asio. + +//------------------------------------------------------------------------------ + +// This composed operation shows composition of multiple underlying operations, +// using asio's stackless coroutines support to express the flow of control. It +// automatically serialises a message, using its I/O streams insertion +// operator, before sending it N times on the socket. To do this, it must +// allocate a buffer for the encoded message and ensure this buffer's validity +// until all underlying async_write operation complete. A one second delay is +// inserted prior to each write operation, using a steady_timer. + +#include <boost/asio/yield.hpp> + +template <typename T, typename CompletionToken> +auto async_write_messages(tcp::socket& socket, + const T& message, std::size_t repeat_count, + CompletionToken&& token) + // The return type of the initiating function is deduced from the combination + // of CompletionToken type and the completion handler's signature. When the + // completion token is a simple callback, the return type is always void. + // In this example, when the completion token is boost::asio::yield_context + // (used for stackful coroutines) the return type would be also be void, as + // there is no non-error argument to the completion handler. When the + // completion token is boost::asio::use_future it would be std::future<void>. + // + // In C++14 we can omit the return type as it is automatically deduced from + // the return type of boost::asio::async_initiate. +{ + // Encode the message and copy it into an allocated buffer. The buffer will + // be maintained for the lifetime of the composed asynchronous operation. + std::ostringstream os; + os << message; + std::unique_ptr<std::string> encoded_message(new std::string(os.str())); + + // Create a steady_timer to be used for the delay between messages. + std::unique_ptr<boost::asio::steady_timer> delay_timer( + new boost::asio::steady_timer(socket.get_executor())); + + // The boost::asio::async_compose function takes: + // + // - our asynchronous operation implementation, + // - the completion token, + // - the completion handler signature, and + // - any I/O objects (or executors) used by the operation + // + // It then wraps our implementation, which is implemented here as a stackless + // coroutine in a lambda, in an intermediate completion handler that meets the + // requirements of a conforming asynchronous operation. This includes + // tracking outstanding work against the I/O executors associated with the + // operation (in this example, this is the socket's executor). + // + // The first argument to our lambda is a reference to the enclosing + // intermediate completion handler. This intermediate completion handler is + // provided for us by the boost::asio::async_compose function, and takes care + // of all the details required to implement a conforming asynchronous + // operation. When calling an underlying asynchronous operation, we pass it + // this enclosing intermediate completion handler as the completion token. + // + // All arguments to our lambda after the first must be defaulted to allow the + // state machine to be started, as well as to allow the completion handler to + // match the completion signature of both the async_write and + // steady_timer::async_wait operations. + return boost::asio::async_compose< + CompletionToken, void(boost::system::error_code)>( + [ + // The implementation holds a reference to the socket as it is used for + // multiple async_write operations. + &socket, + + // The allocated buffer for the encoded message. The std::unique_ptr + // smart pointer is move-only, and as a consequence our lambda + // implementation is also move-only. + encoded_message = std::move(encoded_message), + + // The repeat count remaining. + repeat_count, + + // A steady timer used for introducing a delay. + delay_timer = std::move(delay_timer), + + // The coroutine state. + coro = boost::asio::coroutine() + ] + ( + auto& self, + const boost::system::error_code& error = {}, + std::size_t = 0 + ) mutable + { + reenter (coro) + { + while (repeat_count > 0) + { + --repeat_count; + + delay_timer->expires_after(std::chrono::seconds(1)); + yield delay_timer->async_wait(std::move(self)); + if (error) + break; + + yield boost::asio::async_write(socket, + boost::asio::buffer(*encoded_message), std::move(self)); + if (error) + break; + } + + // Deallocate the encoded message and delay timer before calling the + // user-supplied completion handler. + encoded_message.reset(); + delay_timer.reset(); + + // Call the user-supplied handler with the result of the operation. + self.complete(error); + } + }, + token, socket); +} + +#include <boost/asio/unyield.hpp> + +//------------------------------------------------------------------------------ + +void test_callback() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using a lambda as a callback. + async_write_messages(socket, "Testing callback\r\n", 5, + [](const boost::system::error_code& error) + { + if (!error) + { + std::cout << "Messages sent\n"; + } + else + { + std::cout << "Error: " << error.message() << "\n"; + } + }); + + io_context.run(); +} + +//------------------------------------------------------------------------------ + +void test_future() +{ + boost::asio::io_context io_context; + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + tcp::socket socket = acceptor.accept(); + + // Test our asynchronous operation using the use_future completion token. + // This token causes the operation's initiating function to return a future, + // which may be used to synchronously wait for the result of the operation. + std::future<void> f = async_write_messages( + socket, "Testing future\r\n", 5, boost::asio::use_future); + + io_context.run(); + + try + { + // Get the result of the operation. + f.get(); + std::cout << "Messages sent\n"; + } + catch (const std::exception& e) + { + std::cout << "Error: " << e.what() << "\n"; + } +} + +//------------------------------------------------------------------------------ + +int main() +{ + test_callback(); + test_future(); +} diff --git a/src/boost/libs/asio/example/cpp17/coroutines_ts/chat_server.cpp b/src/boost/libs/asio/example/cpp17/coroutines_ts/chat_server.cpp new file mode 100644 index 000000000..e233e9d6d --- /dev/null +++ b/src/boost/libs/asio/example/cpp17/coroutines_ts/chat_server.cpp @@ -0,0 +1,225 @@ +// +// chat_server.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <cstdlib> +#include <deque> +#include <iostream> +#include <list> +#include <memory> +#include <set> +#include <string> +#include <utility> +#include <boost/asio/awaitable.hpp> +#include <boost/asio/detached.hpp> +#include <boost/asio/co_spawn.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/read_until.hpp> +#include <boost/asio/redirect_error.hpp> +#include <boost/asio/signal_set.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/asio/use_awaitable.hpp> +#include <boost/asio/write.hpp> + +using boost::asio::ip::tcp; +using boost::asio::awaitable; +using boost::asio::co_spawn; +using boost::asio::detached; +using boost::asio::redirect_error; +using boost::asio::use_awaitable; + +//---------------------------------------------------------------------- + +class chat_participant +{ +public: + virtual ~chat_participant() {} + virtual void deliver(const std::string& msg) = 0; +}; + +typedef std::shared_ptr<chat_participant> chat_participant_ptr; + +//---------------------------------------------------------------------- + +class chat_room +{ +public: + void join(chat_participant_ptr participant) + { + participants_.insert(participant); + for (auto msg: recent_msgs_) + participant->deliver(msg); + } + + void leave(chat_participant_ptr participant) + { + participants_.erase(participant); + } + + void deliver(const std::string& msg) + { + recent_msgs_.push_back(msg); + while (recent_msgs_.size() > max_recent_msgs) + recent_msgs_.pop_front(); + + for (auto participant: participants_) + participant->deliver(msg); + } + +private: + std::set<chat_participant_ptr> participants_; + enum { max_recent_msgs = 100 }; + std::deque<std::string> recent_msgs_; +}; + +//---------------------------------------------------------------------- + +class chat_session + : public chat_participant, + public std::enable_shared_from_this<chat_session> +{ +public: + chat_session(tcp::socket socket, chat_room& room) + : socket_(std::move(socket)), + timer_(socket_.get_executor()), + room_(room) + { + timer_.expires_at(std::chrono::steady_clock::time_point::max()); + } + + void start() + { + room_.join(shared_from_this()); + + co_spawn(socket_.get_executor(), + [self = shared_from_this()]{ return self->reader(); }, + detached); + + co_spawn(socket_.get_executor(), + [self = shared_from_this()]{ return self->writer(); }, + detached); + } + + void deliver(const std::string& msg) + { + write_msgs_.push_back(msg); + timer_.cancel_one(); + } + +private: + awaitable<void> reader() + { + try + { + for (std::string read_msg;;) + { + std::size_t n = co_await boost::asio::async_read_until(socket_, + boost::asio::dynamic_buffer(read_msg, 1024), "\n", use_awaitable); + + room_.deliver(read_msg.substr(0, n)); + read_msg.erase(0, n); + } + } + catch (std::exception&) + { + stop(); + } + } + + awaitable<void> writer() + { + try + { + while (socket_.is_open()) + { + if (write_msgs_.empty()) + { + boost::system::error_code ec; + co_await timer_.async_wait(redirect_error(use_awaitable, ec)); + } + else + { + co_await boost::asio::async_write(socket_, + boost::asio::buffer(write_msgs_.front()), use_awaitable); + write_msgs_.pop_front(); + } + } + } + catch (std::exception&) + { + stop(); + } + } + + void stop() + { + room_.leave(shared_from_this()); + socket_.close(); + timer_.cancel(); + } + + tcp::socket socket_; + boost::asio::steady_timer timer_; + chat_room& room_; + std::deque<std::string> write_msgs_; +}; + +//---------------------------------------------------------------------- + +awaitable<void> listener(tcp::acceptor acceptor) +{ + chat_room room; + + for (;;) + { + std::make_shared<chat_session>( + co_await acceptor.async_accept(use_awaitable), + room + )->start(); + } +} + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + try + { + if (argc < 2) + { + std::cerr << "Usage: chat_server <port> [<port> ...]\n"; + return 1; + } + + boost::asio::io_context io_context(1); + + for (int i = 1; i < argc; ++i) + { + unsigned short port = std::atoi(argv[i]); + co_spawn(io_context, + [&io_context, port] + { + return listener(tcp::acceptor(io_context, {tcp::v4(), port})); + }, + detached); + } + + boost::asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto){ io_context.stop(); }); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/boost/libs/asio/example/cpp17/coroutines_ts/echo_server.cpp b/src/boost/libs/asio/example/cpp17/coroutines_ts/echo_server.cpp new file mode 100644 index 000000000..7ca12c69a --- /dev/null +++ b/src/boost/libs/asio/example/cpp17/coroutines_ts/echo_server.cpp @@ -0,0 +1,76 @@ +// +// echo_server.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/co_spawn.hpp> +#include <boost/asio/detached.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/signal_set.hpp> +#include <boost/asio/write.hpp> +#include <cstdio> + +using boost::asio::ip::tcp; +using boost::asio::awaitable; +using boost::asio::co_spawn; +using boost::asio::detached; +using boost::asio::use_awaitable; +namespace this_coro = boost::asio::this_coro; + +awaitable<void> echo(tcp::socket socket) +{ + try + { + char data[1024]; + for (;;) + { + std::size_t n = co_await socket.async_read_some(boost::asio::buffer(data), use_awaitable); + co_await async_write(socket, boost::asio::buffer(data, n), use_awaitable); + } + } + catch (std::exception& e) + { + std::printf("echo Exception: %s\n", e.what()); + } +} + +awaitable<void> listener() +{ + auto executor = co_await this_coro::executor; + tcp::acceptor acceptor(executor, {tcp::v4(), 55555}); + for (;;) + { + tcp::socket socket = co_await acceptor.async_accept(use_awaitable); + co_spawn(executor, + [socket = std::move(socket)]() mutable + { + return echo(std::move(socket)); + }, + detached); + } +} + +int main() +{ + try + { + boost::asio::io_context io_context(1); + + boost::asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto){ io_context.stop(); }); + + co_spawn(io_context, listener, detached); + + io_context.run(); + } + catch (std::exception& e) + { + std::printf("Exception: %s\n", e.what()); + } +} diff --git a/src/boost/libs/asio/example/cpp17/coroutines_ts/echo_server_with_default.cpp b/src/boost/libs/asio/example/cpp17/coroutines_ts/echo_server_with_default.cpp new file mode 100644 index 000000000..a34f8bf57 --- /dev/null +++ b/src/boost/libs/asio/example/cpp17/coroutines_ts/echo_server_with_default.cpp @@ -0,0 +1,78 @@ +// +// echo_server_with_default.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/co_spawn.hpp> +#include <boost/asio/detached.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/signal_set.hpp> +#include <boost/asio/write.hpp> +#include <cstdio> + +using boost::asio::ip::tcp; +using boost::asio::awaitable; +using boost::asio::co_spawn; +using boost::asio::detached; +using boost::asio::use_awaitable_t; +using tcp_acceptor = use_awaitable_t<>::as_default_on_t<tcp::acceptor>; +using tcp_socket = use_awaitable_t<>::as_default_on_t<tcp::socket>; +namespace this_coro = boost::asio::this_coro; + +awaitable<void> echo(tcp_socket socket) +{ + try + { + char data[1024]; + for (;;) + { + std::size_t n = co_await socket.async_read_some(boost::asio::buffer(data)); + co_await async_write(socket, boost::asio::buffer(data, n)); + } + } + catch (std::exception& e) + { + std::printf("echo Exception: %s\n", e.what()); + } +} + +awaitable<void> listener() +{ + auto executor = co_await this_coro::executor; + tcp_acceptor acceptor(executor, {tcp::v4(), 55555}); + for (;;) + { + auto socket = co_await acceptor.async_accept(); + co_spawn(executor, + [socket = std::move(socket)]() mutable + { + return echo(std::move(socket)); + }, + detached); + } +} + +int main() +{ + try + { + boost::asio::io_context io_context(1); + + boost::asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto){ io_context.stop(); }); + + co_spawn(io_context, listener, detached); + + io_context.run(); + } + catch (std::exception& e) + { + std::printf("Exception: %s\n", e.what()); + } +} diff --git a/src/boost/libs/asio/example/cpp17/coroutines_ts/range_based_for.cpp b/src/boost/libs/asio/example/cpp17/coroutines_ts/range_based_for.cpp new file mode 100644 index 000000000..477349aee --- /dev/null +++ b/src/boost/libs/asio/example/cpp17/coroutines_ts/range_based_for.cpp @@ -0,0 +1,107 @@ +// +// range_based_for.cpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/co_spawn.hpp> +#include <boost/asio/detached.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/signal_set.hpp> +#include <boost/asio/write.hpp> +#include <cstdio> + +using boost::asio::ip::tcp; +using boost::asio::awaitable; +using boost::asio::co_spawn; +using boost::asio::detached; +using boost::asio::use_awaitable; + +class connection_iter +{ + friend class connections; + tcp::acceptor* acceptor_ = nullptr; + tcp::socket socket_; + + connection_iter(tcp::acceptor& a, tcp::socket s) + : acceptor_(&a), socket_(std::move(s)) {} + +public: + tcp::socket operator*() + { + return std::move(socket_); + } + + awaitable<void> operator++() + { + socket_ = co_await acceptor_->async_accept(use_awaitable); + } + + bool operator==(const connection_iter&) const noexcept + { + return false; + } + + bool operator!=(const connection_iter&) const noexcept + { + return true; + } +}; + +class connections +{ + tcp::acceptor& acceptor_; + +public: + explicit connections(tcp::acceptor& a) : acceptor_(a) {} + + awaitable<connection_iter> begin() + { + tcp::socket s = co_await acceptor_.async_accept(use_awaitable); + co_return connection_iter(acceptor_, std::move(s)); + } + + connection_iter end() + { + return connection_iter(acceptor_, + tcp::socket(acceptor_.get_executor())); + } +}; + +awaitable<void> listener(tcp::acceptor acceptor) +{ + for co_await (tcp::socket s : connections(acceptor)) + { + co_await boost::asio::async_write(s, boost::asio::buffer("hello\r\n", 7), use_awaitable); + } +} + +int main() +{ + try + { + boost::asio::io_context io_context(1); + + boost::asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto){ io_context.stop(); }); + + tcp::acceptor acceptor(io_context, {tcp::v4(), 55555}); + co_spawn(io_context, + [acceptor = std::move(acceptor)]() mutable + { + return listener(std::move(acceptor)); + }, + detached); + + io_context.run(); + } + catch (std::exception& e) + { + std::printf("Exception: %s\n", e.what()); + } +} diff --git a/src/boost/libs/asio/example/cpp17/coroutines_ts/refactored_echo_server.cpp b/src/boost/libs/asio/example/cpp17/coroutines_ts/refactored_echo_server.cpp new file mode 100644 index 000000000..062efc16c --- /dev/null +++ b/src/boost/libs/asio/example/cpp17/coroutines_ts/refactored_echo_server.cpp @@ -0,0 +1,85 @@ +// +// refactored_echo_server.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff 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) +// + +#include <boost/asio/co_spawn.hpp> +#include <boost/asio/detached.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/signal_set.hpp> +#include <boost/asio/write.hpp> +#include <cstdio> + +using boost::asio::ip::tcp; +using boost::asio::awaitable; +using boost::asio::co_spawn; +using boost::asio::detached; +using boost::asio::use_awaitable; +namespace this_coro = boost::asio::this_coro; + +awaitable<void> echo_once(tcp::socket& socket) +{ + char data[128]; + std::size_t n = co_await socket.async_read_some(boost::asio::buffer(data), use_awaitable); + co_await async_write(socket, boost::asio::buffer(data, n), use_awaitable); +} + +awaitable<void> echo(tcp::socket socket) +{ + try + { + for (;;) + { + // The asynchronous operations to echo a single chunk of data have been + // refactored into a separate function. When this function is called, the + // operations are still performed in the context of the current + // coroutine, and the behaviour is functionally equivalent. + co_await echo_once(socket); + } + } + catch (std::exception& e) + { + std::printf("echo Exception: %s\n", e.what()); + } +} + +awaitable<void> listener() +{ + auto executor = co_await this_coro::executor; + tcp::acceptor acceptor(executor, {tcp::v4(), 55555}); + for (;;) + { + tcp::socket socket = co_await acceptor.async_accept(use_awaitable); + co_spawn(executor, + [socket = std::move(socket)]() mutable + { + return echo(std::move(socket)); + }, + detached); + } +} + +int main() +{ + try + { + boost::asio::io_context io_context(1); + + boost::asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto){ io_context.stop(); }); + + co_spawn(io_context, listener, detached); + + io_context.run(); + } + catch (std::exception& e) + { + std::printf("Exception: %s\n", e.what()); + } +} |