summaryrefslogtreecommitdiff
path: root/doc/html/boost_asio/example/cpp03
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2017-09-13 11:08:07 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2017-09-13 11:09:00 +0900
commitb5c87084afaef42b2d058f68091be31988a6a874 (patch)
treeadef9a65870a41181687e11d57fdf98e7629de3c /doc/html/boost_asio/example/cpp03
parent34bd32e225e2a8a94104489b31c42e5801cc1f4a (diff)
downloadboost-b5c87084afaef42b2d058f68091be31988a6a874.tar.gz
boost-b5c87084afaef42b2d058f68091be31988a6a874.tar.bz2
boost-b5c87084afaef42b2d058f68091be31988a6a874.zip
Imported Upstream version 1.64.0upstream/1.64.0
Change-Id: Id9212edd016dd55f21172c427aa7894d1d24148b Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
Diffstat (limited to 'doc/html/boost_asio/example/cpp03')
-rw-r--r--doc/html/boost_asio/example/cpp03/allocation/server.cpp238
-rw-r--r--doc/html/boost_asio/example/cpp03/buffers/reference_counted.cpp131
-rw-r--r--doc/html/boost_asio/example/cpp03/chat/chat_client.cpp177
-rw-r--r--doc/html/boost_asio/example/cpp03/chat/chat_message.hpp93
-rw-r--r--doc/html/boost_asio/example/cpp03/chat/chat_server.cpp247
-rw-r--r--doc/html/boost_asio/example/cpp03/chat/posix_chat_client.cpp205
-rw-r--r--doc/html/boost_asio/example/cpp03/echo/async_tcp_echo_server.cpp137
-rw-r--r--doc/html/boost_asio/example/cpp03/echo/async_udp_echo_server.cpp92
-rw-r--r--doc/html/boost_asio/example/cpp03/echo/blocking_tcp_echo_client.cpp59
-rw-r--r--doc/html/boost_asio/example/cpp03/echo/blocking_tcp_echo_server.cpp80
-rw-r--r--doc/html/boost_asio/example/cpp03/echo/blocking_udp_echo_client.cpp59
-rw-r--r--doc/html/boost_asio/example/cpp03/echo/blocking_udp_echo_server.cpp53
-rw-r--r--doc/html/boost_asio/example/cpp03/fork/daemon.cpp189
-rw-r--r--doc/html/boost_asio/example/cpp03/fork/process_per_connection.cpp160
-rw-r--r--doc/html/boost_asio/example/cpp03/http/client/async_client.cpp205
-rw-r--r--doc/html/boost_asio/example/cpp03/http/client/sync_client.cpp107
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/connection.cpp99
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/connection.hpp83
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/connection_manager.cpp38
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/connection_manager.hpp44
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/header.hpp28
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/main.cpp44
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/mime_types.cpp46
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/mime_types.hpp27
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/reply.cpp256
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/reply.hpp64
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/request.hpp34
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/request_handler.cpp122
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/request_handler.hpp46
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/request_parser.cpp315
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/request_parser.hpp95
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/server.cpp94
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server/server.hpp69
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/connection.cpp93
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/connection.hpp75
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/header.hpp28
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/io_service_pool.cpp71
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/io_service_pool.hpp56
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/main.cpp46
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/mime_types.cpp46
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/mime_types.hpp27
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/reply.cpp256
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/reply.hpp64
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/request.hpp34
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/request_handler.cpp122
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/request_handler.hpp46
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/request_parser.cpp315
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/request_parser.hpp95
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/server.cpp77
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server2/server.hpp68
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/connection.cpp98
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/connection.hpp78
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/header.hpp28
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/main.cpp46
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/mime_types.cpp46
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/mime_types.hpp27
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/reply.cpp256
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/reply.hpp64
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/request.hpp34
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/request_handler.cpp122
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/request_handler.hpp46
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/request_parser.cpp315
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/request_parser.hpp95
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/server.cpp90
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server3/server.hpp70
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server4/file_handler.cpp122
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server4/file_handler.hpp44
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server4/header.hpp28
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server4/main.cpp58
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server4/mime_types.cpp46
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server4/mime_types.hpp27
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server4/reply.cpp256
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server4/reply.hpp64
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server4/request.hpp46
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server4/request_parser.cpp226
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server4/request_parser.hpp78
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server4/server.cpp121
-rw-r--r--doc/html/boost_asio/example/cpp03/http/server4/server.hpp73
-rw-r--r--doc/html/boost_asio/example/cpp03/icmp/icmp_header.hpp94
-rw-r--r--doc/html/boost_asio/example/cpp03/icmp/ipv4_header.hpp102
-rw-r--r--doc/html/boost_asio/example/cpp03/icmp/ping.cpp162
-rw-r--r--doc/html/boost_asio/example/cpp03/invocation/prioritised_handlers.cpp162
-rw-r--r--doc/html/boost_asio/example/cpp03/iostreams/daytime_client.cpp44
-rw-r--r--doc/html/boost_asio/example/cpp03/iostreams/daytime_server.cpp51
-rw-r--r--doc/html/boost_asio/example/cpp03/iostreams/http_client.cpp91
-rw-r--r--doc/html/boost_asio/example/cpp03/local/connect_pair.cpp142
-rw-r--r--doc/html/boost_asio/example/cpp03/local/iostream_client.cpp62
-rw-r--r--doc/html/boost_asio/example/cpp03/local/stream_client.cpp61
-rw-r--r--doc/html/boost_asio/example/cpp03/local/stream_server.cpp141
-rw-r--r--doc/html/boost_asio/example/cpp03/multicast/receiver.cpp93
-rw-r--r--doc/html/boost_asio/example/cpp03/multicast/sender.cpp99
-rw-r--r--doc/html/boost_asio/example/cpp03/nonblocking/third_party_lib.cpp242
-rw-r--r--doc/html/boost_asio/example/cpp03/porthopper/client.cpp193
-rw-r--r--doc/html/boost_asio/example/cpp03/porthopper/protocol.hpp156
-rw-r--r--doc/html/boost_asio/example/cpp03/porthopper/server.cpp187
-rw-r--r--doc/html/boost_asio/example/cpp03/serialization/client.cpp125
-rw-r--r--doc/html/boost_asio/example/cpp03/serialization/connection.hpp188
-rw-r--r--doc/html/boost_asio/example/cpp03/serialization/server.cpp123
-rw-r--r--doc/html/boost_asio/example/cpp03/serialization/stock.hpp50
-rw-r--r--doc/html/boost_asio/example/cpp03/services/basic_logger.hpp83
-rw-r--r--doc/html/boost_asio/example/cpp03/services/daytime_client.cpp85
-rw-r--r--doc/html/boost_asio/example/cpp03/services/logger.hpp24
-rw-r--r--doc/html/boost_asio/example/cpp03/services/logger_service.cpp17
-rw-r--r--doc/html/boost_asio/example/cpp03/services/logger_service.hpp145
-rw-r--r--doc/html/boost_asio/example/cpp03/services/stream_socket_service.hpp351
-rw-r--r--doc/html/boost_asio/example/cpp03/socks4/socks4.hpp144
-rw-r--r--doc/html/boost_asio/example/cpp03/socks4/sync_client.cpp95
-rw-r--r--doc/html/boost_asio/example/cpp03/spawn/echo_server.cpp122
-rw-r--r--doc/html/boost_asio/example/cpp03/ssl/client.cpp156
-rw-r--r--doc/html/boost_asio/example/cpp03/ssl/server.cpp170
-rw-r--r--doc/html/boost_asio/example/cpp03/timeouts/async_tcp_client.cpp306
-rw-r--r--doc/html/boost_asio/example/cpp03/timeouts/blocking_tcp_client.cpp241
-rw-r--r--doc/html/boost_asio/example/cpp03/timeouts/blocking_udp_client.cpp182
-rw-r--r--doc/html/boost_asio/example/cpp03/timeouts/server.cpp427
-rw-r--r--doc/html/boost_asio/example/cpp03/timers/tick_count_timer.cpp123
-rw-r--r--doc/html/boost_asio/example/cpp03/timers/time_t_timer.cpp93
-rw-r--r--doc/html/boost_asio/example/cpp03/windows/transmit_file.cpp171
117 files changed, 13362 insertions, 0 deletions
diff --git a/doc/html/boost_asio/example/cpp03/allocation/server.cpp b/doc/html/boost_asio/example/cpp03/allocation/server.cpp
new file mode 100644
index 0000000000..40bd91736f
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/allocation/server.cpp
@@ -0,0 +1,238 @@
+//
+// server.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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_allocator
+ : private boost::noncopyable
+{
+public:
+ handler_allocator()
+ : 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_;
+};
+
+// Wrapper class template for handler objects to allow handler memory
+// allocation to be customised. Calls to operator() are forwarded to the
+// encapsulated handler.
+template <typename Handler>
+class custom_alloc_handler
+{
+public:
+ custom_alloc_handler(handler_allocator& a, Handler h)
+ : allocator_(a),
+ handler_(h)
+ {
+ }
+
+ template <typename Arg1>
+ void operator()(Arg1 arg1)
+ {
+ handler_(arg1);
+ }
+
+ template <typename Arg1, typename Arg2>
+ void operator()(Arg1 arg1, Arg2 arg2)
+ {
+ handler_(arg1, arg2);
+ }
+
+ friend void* asio_handler_allocate(std::size_t size,
+ custom_alloc_handler<Handler>* this_handler)
+ {
+ return this_handler->allocator_.allocate(size);
+ }
+
+ friend void asio_handler_deallocate(void* pointer, std::size_t /*size*/,
+ custom_alloc_handler<Handler>* this_handler)
+ {
+ this_handler->allocator_.deallocate(pointer);
+ }
+
+private:
+ handler_allocator& allocator_;
+ 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_allocator& a, Handler h)
+{
+ return custom_alloc_handler<Handler>(a, h);
+}
+
+class session
+ : public boost::enable_shared_from_this<session>
+{
+public:
+ session(boost::asio::io_service& io_service)
+ : socket_(io_service)
+ {
+ }
+
+ tcp::socket& socket()
+ {
+ return socket_;
+ }
+
+ void start()
+ {
+ socket_.async_read_some(boost::asio::buffer(data_),
+ make_custom_alloc_handler(allocator_,
+ 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(allocator_,
+ 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(allocator_,
+ 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 allocator to use for handler-based custom memory allocation.
+ handler_allocator allocator_;
+};
+
+typedef boost::shared_ptr<session> session_ptr;
+
+class server
+{
+public:
+ server(boost::asio::io_service& io_service, short port)
+ : io_service_(io_service),
+ acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
+ {
+ session_ptr new_session(new session(io_service_));
+ 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_service_));
+ acceptor_.async_accept(new_session->socket(),
+ boost::bind(&server::handle_accept, this, new_session,
+ boost::asio::placeholders::error));
+ }
+
+private:
+ boost::asio::io_service& io_service_;
+ tcp::acceptor acceptor_;
+};
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ if (argc != 2)
+ {
+ std::cerr << "Usage: server <port>\n";
+ return 1;
+ }
+
+ boost::asio::io_service io_service;
+
+ using namespace std; // For atoi.
+ server s(io_service, atoi(argv[1]));
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/buffers/reference_counted.cpp b/doc/html/boost_asio/example/cpp03/buffers/reference_counted.cpp
new file mode 100644
index 0000000000..504662cd2b
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/buffers/reference_counted.cpp
@@ -0,0 +1,131 @@
+//
+// reference_counted.cpp
+// ~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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_service& io_service)
+ : socket_(io_service)
+ {
+ }
+
+ 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_service& io_service, short port)
+ : io_service_(io_service),
+ acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
+ {
+ session_ptr new_session(new session(io_service_));
+ 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_service_));
+ acceptor_.async_accept(new_session->socket(),
+ boost::bind(&server::handle_accept, this, new_session,
+ boost::asio::placeholders::error));
+ }
+
+private:
+ boost::asio::io_service& io_service_;
+ 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_service io_service;
+
+ using namespace std; // For atoi.
+ server s(io_service, atoi(argv[1]));
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/chat/chat_client.cpp b/doc/html/boost_asio/example/cpp03/chat/chat_client.cpp
new file mode 100644
index 0000000000..71bd50d6d2
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/chat/chat_client.cpp
@@ -0,0 +1,177 @@
+//
+// chat_client.cpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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_service& io_service,
+ tcp::resolver::iterator endpoint_iterator)
+ : io_service_(io_service),
+ socket_(io_service)
+ {
+ boost::asio::async_connect(socket_, endpoint_iterator,
+ boost::bind(&chat_client::handle_connect, this,
+ boost::asio::placeholders::error));
+ }
+
+ void write(const chat_message& msg)
+ {
+ io_service_.post(boost::bind(&chat_client::do_write, this, msg));
+ }
+
+ void close()
+ {
+ io_service_.post(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_service& io_service_;
+ 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_service io_service;
+
+ tcp::resolver resolver(io_service);
+ tcp::resolver::query query(argv[1], argv[2]);
+ tcp::resolver::iterator iterator = resolver.resolve(query);
+
+ chat_client c(io_service, iterator);
+
+ boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
+
+ 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/doc/html/boost_asio/example/cpp03/chat/chat_message.hpp b/doc/html/boost_asio/example/cpp03/chat/chat_message.hpp
new file mode 100644
index 0000000000..75d31ab993
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/chat/chat_message.hpp
@@ -0,0 +1,93 @@
+//
+// chat_message.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/chat/chat_server.cpp b/doc/html/boost_asio/example/cpp03/chat/chat_server.cpp
new file mode 100644
index 0000000000..611db89243
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/chat/chat_server.cpp
@@ -0,0 +1,247 @@
+//
+// chat_server.cpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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, _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, _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_service& io_service, chat_room& room)
+ : socket_(io_service),
+ 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_service& io_service,
+ const tcp::endpoint& endpoint)
+ : io_service_(io_service),
+ acceptor_(io_service, endpoint)
+ {
+ start_accept();
+ }
+
+ void start_accept()
+ {
+ chat_session_ptr new_session(new chat_session(io_service_, 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_service& io_service_;
+ 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_service io_service;
+
+ 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_service, endpoint));
+ servers.push_back(server);
+ }
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/chat/posix_chat_client.cpp b/doc/html/boost_asio/example/cpp03/chat/posix_chat_client.cpp
new file mode 100644
index 0000000000..434f9f5916
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/chat/posix_chat_client.cpp
@@ -0,0 +1,205 @@
+//
+// posix_chat_client.cpp
+// ~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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_service& io_service,
+ tcp::resolver::iterator endpoint_iterator)
+ : socket_(io_service),
+ input_(io_service, ::dup(STDIN_FILENO)),
+ output_(io_service, ::dup(STDOUT_FILENO)),
+ input_buffer_(chat_message::max_body_length)
+ {
+ boost::asio::async_connect(socket_, endpoint_iterator,
+ 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_service io_service;
+
+ tcp::resolver resolver(io_service);
+ tcp::resolver::query query(argv[1], argv[2]);
+ tcp::resolver::iterator iterator = resolver.resolve(query);
+
+ posix_chat_client c(io_service, iterator);
+
+ io_service.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/doc/html/boost_asio/example/cpp03/echo/async_tcp_echo_server.cpp b/doc/html/boost_asio/example/cpp03/echo/async_tcp_echo_server.cpp
new file mode 100644
index 0000000000..2aa2e6c65e
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/echo/async_tcp_echo_server.cpp
@@ -0,0 +1,137 @@
+//
+// async_tcp_echo_server.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp>
+#include <boost/asio.hpp>
+
+using boost::asio::ip::tcp;
+
+class session
+{
+public:
+ session(boost::asio::io_service& io_service)
+ : socket_(io_service)
+ {
+ }
+
+ 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_service& io_service, short port)
+ : io_service_(io_service),
+ acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
+ {
+ start_accept();
+ }
+
+private:
+ void start_accept()
+ {
+ session* new_session = new session(io_service_);
+ 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_service& io_service_;
+ 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_service io_service;
+
+ using namespace std; // For atoi.
+ server s(io_service, atoi(argv[1]));
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/echo/async_udp_echo_server.cpp b/doc/html/boost_asio/example/cpp03/echo/async_udp_echo_server.cpp
new file mode 100644
index 0000000000..97049c9dd8
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/echo/async_udp_echo_server.cpp
@@ -0,0 +1,92 @@
+//
+// async_udp_echo_server.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp>
+#include <boost/asio.hpp>
+
+using boost::asio::ip::udp;
+
+class server
+{
+public:
+ server(boost::asio::io_service& io_service, short port)
+ : socket_(io_service, 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_service io_service;
+
+ using namespace std; // For atoi.
+ server s(io_service, atoi(argv[1]));
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/echo/blocking_tcp_echo_client.cpp b/doc/html/boost_asio/example/cpp03/echo/blocking_tcp_echo_client.cpp
new file mode 100644
index 0000000000..77792af005
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/echo/blocking_tcp_echo_client.cpp
@@ -0,0 +1,59 @@
+//
+// blocking_tcp_echo_client.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service io_service;
+
+ tcp::resolver resolver(io_service);
+ tcp::resolver::query query(tcp::v4(), argv[1], argv[2]);
+ tcp::resolver::iterator iterator = resolver.resolve(query);
+
+ tcp::socket s(io_service);
+ boost::asio::connect(s, iterator);
+
+ 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/doc/html/boost_asio/example/cpp03/echo/blocking_tcp_echo_server.cpp b/doc/html/boost_asio/example/cpp03/echo/blocking_tcp_echo_server.cpp
new file mode 100644
index 0000000000..0c4529b4cc
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/echo/blocking_tcp_echo_server.cpp
@@ -0,0 +1,80 @@
+//
+// blocking_tcp_echo_server.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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_service& io_service, unsigned short port)
+{
+ tcp::acceptor a(io_service, tcp::endpoint(tcp::v4(), port));
+ for (;;)
+ {
+ socket_ptr sock(new tcp::socket(io_service));
+ 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_service io_service;
+
+ using namespace std; // For atoi.
+ server(io_service, atoi(argv[1]));
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/echo/blocking_udp_echo_client.cpp b/doc/html/boost_asio/example/cpp03/echo/blocking_udp_echo_client.cpp
new file mode 100644
index 0000000000..4f29e69b5e
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/echo/blocking_udp_echo_client.cpp
@@ -0,0 +1,59 @@
+//
+// blocking_udp_echo_client.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service io_service;
+
+ udp::socket s(io_service, udp::endpoint(udp::v4(), 0));
+
+ udp::resolver resolver(io_service);
+ udp::resolver::query query(udp::v4(), argv[1], argv[2]);
+ udp::resolver::iterator iterator = resolver.resolve(query);
+
+ 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), *iterator);
+
+ 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/doc/html/boost_asio/example/cpp03/echo/blocking_udp_echo_server.cpp b/doc/html/boost_asio/example/cpp03/echo/blocking_udp_echo_server.cpp
new file mode 100644
index 0000000000..31c9cd8b22
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/echo/blocking_udp_echo_server.cpp
@@ -0,0 +1,53 @@
+//
+// blocking_udp_echo_server.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service& io_service, unsigned short port)
+{
+ udp::socket sock(io_service, 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_service io_service;
+
+ using namespace std; // For atoi.
+ server(io_service, atoi(argv[1]));
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/fork/daemon.cpp b/doc/html/boost_asio/example/cpp03/fork/daemon.cpp
new file mode 100644
index 0000000000..f16f7cf8c9
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/fork/daemon.cpp
@@ -0,0 +1,189 @@
+//
+// daemon.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service.hpp>
+#include <boost/asio/ip/udp.hpp>
+#include <boost/asio/signal_set.hpp>
+#include <boost/array.hpp>
+#include <boost/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_service& io_service)
+ : socket_(io_service, 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, _1));
+ }
+
+ void handle_receive(const boost::system::error_code& ec)
+ {
+ if (!ec || ec == boost::asio::error::message_size)
+ {
+ 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_service io_service;
+
+ // 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_service);
+
+ // 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_service, SIGINT, SIGTERM);
+ signals.async_wait(
+ boost::bind(&boost::asio::io_service::stop, &io_service));
+
+ // Inform the io_service that we are about to become a daemon. The
+ // io_service cleans up any internal resources, such as threads, that may
+ // interfere with forking.
+ io_service.notify_fork(boost::asio::io_service::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_service object is a local variable, this means
+ // we do not have to call:
+ //
+ // io_service.notify_fork(boost::asio::io_service::fork_parent);
+ //
+ // However, this line should be added before each call to exit() if
+ // using a global io_service object. An additional call:
+ //
+ // io_service.notify_fork(boost::asio::io_service::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_service that we have finished becoming a daemon. The
+ // io_service uses this opportunity to create any internal file descriptors
+ // that need to be private to the new process.
+ io_service.notify_fork(boost::asio::io_service::fork_child);
+
+ // The io_service can now be used normally.
+ syslog(LOG_INFO | LOG_USER, "Daemon started");
+ io_service.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/doc/html/boost_asio/example/cpp03/fork/process_per_connection.cpp b/doc/html/boost_asio/example/cpp03/fork/process_per_connection.cpp
new file mode 100644
index 0000000000..70faa02d57
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/fork/process_per_connection.cpp
@@ -0,0 +1,160 @@
+//
+// process_per_connection.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service.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.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_service& io_service, unsigned short port)
+ : io_service_(io_service),
+ signal_(io_service, SIGCHLD),
+ acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
+ socket_(io_service)
+ {
+ 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, _1));
+ }
+
+ void handle_accept(const boost::system::error_code& ec)
+ {
+ if (!ec)
+ {
+ // Inform the io_service that we are about to fork. The io_service cleans
+ // up any internal resources, such as threads, that may interfere with
+ // forking.
+ io_service_.notify_fork(boost::asio::io_service::fork_prepare);
+
+ if (fork() == 0)
+ {
+ // Inform the io_service that the fork is finished and that this is the
+ // child process. The io_service uses this opportunity to create any
+ // internal file descriptors that must be private to the new process.
+ io_service_.notify_fork(boost::asio::io_service::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_service that the fork is finished (or failed) and that
+ // this is the parent process. The io_service uses this opportunity to
+ // recreate any internal resources that were cleaned up during
+ // preparation for the fork.
+ io_service_.notify_fork(boost::asio::io_service::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, _1, _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, _1));
+ }
+
+ void handle_write(const boost::system::error_code& ec)
+ {
+ if (!ec)
+ start_read();
+ }
+
+ boost::asio::io_service& io_service_;
+ 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_service io_service;
+
+ using namespace std; // For atoi.
+ server s(io_service, atoi(argv[1]));
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << std::endl;
+ }
+}
diff --git a/doc/html/boost_asio/example/cpp03/http/client/async_client.cpp b/doc/html/boost_asio/example/cpp03/http/client/async_client.cpp
new file mode 100644
index 0000000000..c240ad6642
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/client/async_client.cpp
@@ -0,0 +1,205 @@
+//
+// async_client.cpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp>
+
+using boost::asio::ip::tcp;
+
+class client
+{
+public:
+ client(boost::asio::io_service& io_service,
+ const std::string& server, const std::string& path)
+ : resolver_(io_service),
+ socket_(io_service)
+ {
+ // 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.
+ tcp::resolver::query query(server, "http");
+ resolver_.async_resolve(query,
+ boost::bind(&client::handle_resolve, this,
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::iterator));
+ }
+
+private:
+ void handle_resolve(const boost::system::error_code& err,
+ tcp::resolver::iterator endpoint_iterator)
+ {
+ if (!err)
+ {
+ // Attempt a connection to each endpoint in the list until we
+ // successfully establish a connection.
+ boost::asio::async_connect(socket_, endpoint_iterator,
+ 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_service io_service;
+ client c(io_service, argv[1], argv[2]);
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cout << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/http/client/sync_client.cpp b/doc/html/boost_asio/example/cpp03/http/client/sync_client.cpp
new file mode 100644
index 0000000000..2eea356ca5
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/client/sync_client.cpp
@@ -0,0 +1,107 @@
+//
+// sync_client.cpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service io_service;
+
+ // Get a list of endpoints corresponding to the server name.
+ tcp::resolver resolver(io_service);
+ tcp::resolver::query query(argv[1], "http");
+ tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
+
+ // Try each endpoint until we successfully establish a connection.
+ tcp::socket socket(io_service);
+ boost::asio::connect(socket, endpoint_iterator);
+
+ // 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/doc/html/boost_asio/example/cpp03/http/server/connection.cpp b/doc/html/boost_asio/example/cpp03/http/server/connection.cpp
new file mode 100644
index 0000000000..a9b04f401c
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/connection.cpp
@@ -0,0 +1,99 @@
+//
+// connection.cpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp>
+#include "connection_manager.hpp"
+#include "request_handler.hpp"
+
+namespace http {
+namespace server {
+
+connection::connection(boost::asio::io_service& io_service,
+ connection_manager& manager, request_handler& handler)
+ : socket_(io_service),
+ 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/doc/html/boost_asio/example/cpp03/http/server/connection.hpp b/doc/html/boost_asio/example/cpp03/http/server/connection.hpp
new file mode 100644
index 0000000000..e38b43a881
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/connection.hpp
@@ -0,0 +1,83 @@
+//
+// connection.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service.
+ explicit connection(boost::asio::io_service& io_service,
+ 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/doc/html/boost_asio/example/cpp03/http/server/connection_manager.cpp b/doc/html/boost_asio/example/cpp03/http/server/connection_manager.cpp
new file mode 100644
index 0000000000..ac2ba36817
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/connection_manager.cpp
@@ -0,0 +1,38 @@
+//
+// connection_manager.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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, _1));
+ connections_.clear();
+}
+
+} // namespace server
+} // namespace http
diff --git a/doc/html/boost_asio/example/cpp03/http/server/connection_manager.hpp b/doc/html/boost_asio/example/cpp03/http/server/connection_manager.hpp
new file mode 100644
index 0000000000..706c4e1e07
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/connection_manager.hpp
@@ -0,0 +1,44 @@
+//
+// connection_manager.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server/header.hpp b/doc/html/boost_asio/example/cpp03/http/server/header.hpp
new file mode 100644
index 0000000000..877211f190
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/header.hpp
@@ -0,0 +1,28 @@
+//
+// header.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server/main.cpp b/doc/html/boost_asio/example/cpp03/http/server/main.cpp
new file mode 100644
index 0000000000..0125aee76a
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/main.cpp
@@ -0,0 +1,44 @@
+//
+// main.cpp
+// ~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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/doc/html/boost_asio/example/cpp03/http/server/mime_types.cpp b/doc/html/boost_asio/example/cpp03/http/server/mime_types.cpp
new file mode 100644
index 0000000000..a03d5e7a16
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/mime_types.cpp
@@ -0,0 +1,46 @@
+//
+// mime_types.cpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server/mime_types.hpp b/doc/html/boost_asio/example/cpp03/http/server/mime_types.hpp
new file mode 100644
index 0000000000..891c443669
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/mime_types.hpp
@@ -0,0 +1,27 @@
+//
+// mime_types.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server/reply.cpp b/doc/html/boost_asio/example/cpp03/http/server/reply.cpp
new file mode 100644
index 0000000000..4f08fe34d3
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/reply.cpp
@@ -0,0 +1,256 @@
+//
+// reply.cpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server/reply.hpp b/doc/html/boost_asio/example/cpp03/http/server/reply.hpp
new file mode 100644
index 0000000000..51d495bc65
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/reply.hpp
@@ -0,0 +1,64 @@
+//
+// reply.hpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server/request.hpp b/doc/html/boost_asio/example/cpp03/http/server/request.hpp
new file mode 100644
index 0000000000..20efff4dd3
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/request.hpp
@@ -0,0 +1,34 @@
+//
+// request.hpp
+// ~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server/request_handler.cpp b/doc/html/boost_asio/example/cpp03/http/server/request_handler.cpp
new file mode 100644
index 0000000000..1b45ce577c
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/request_handler.cpp
@@ -0,0 +1,122 @@
+//
+// request_handler.cpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server/request_handler.hpp b/doc/html/boost_asio/example/cpp03/http/server/request_handler.hpp
new file mode 100644
index 0000000000..542df83b7a
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/request_handler.hpp
@@ -0,0 +1,46 @@
+//
+// request_handler.hpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server/request_parser.cpp b/doc/html/boost_asio/example/cpp03/http/server/request_parser.cpp
new file mode 100644
index 0000000000..bda4f1dbe3
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/request_parser.cpp
@@ -0,0 +1,315 @@
+//
+// request_parser.cpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server/request_parser.hpp b/doc/html/boost_asio/example/cpp03/http/server/request_parser.hpp
new file mode 100644
index 0000000000..f8c45bec9f
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/request_parser.hpp
@@ -0,0 +1,95 @@
+//
+// request_parser.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server/server.cpp b/doc/html/boost_asio/example/cpp03/http/server/server.cpp
new file mode 100644
index 0000000000..8ee0523a6d
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/server.cpp
@@ -0,0 +1,94 @@
+//
+// server.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp>
+#include <signal.h>
+
+namespace http {
+namespace server {
+
+server::server(const std::string& address, const std::string& port,
+ const std::string& doc_root)
+ : io_service_(),
+ signals_(io_service_),
+ acceptor_(io_service_),
+ 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_service_);
+ boost::asio::ip::tcp::resolver::query query(address, port);
+ boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
+ 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_service::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_service_.run();
+}
+
+void server::start_accept()
+{
+ new_connection_.reset(new connection(io_service_,
+ 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_service::run() call
+ // will exit.
+ acceptor_.close();
+ connection_manager_.stop_all();
+}
+
+} // namespace server
+} // namespace http
diff --git a/doc/html/boost_asio/example/cpp03/http/server/server.hpp b/doc/html/boost_asio/example/cpp03/http/server/server.hpp
new file mode 100644
index 0000000000..6662e3c2f4
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server/server.hpp
@@ -0,0 +1,69 @@
+//
+// server.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service 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_service used to perform asynchronous operations.
+ boost::asio::io_service io_service_;
+
+ /// 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/doc/html/boost_asio/example/cpp03/http/server2/connection.cpp b/doc/html/boost_asio/example/cpp03/http/server2/connection.cpp
new file mode 100644
index 0000000000..e2700c6001
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/connection.cpp
@@ -0,0 +1,93 @@
+//
+// connection.cpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp>
+#include "request_handler.hpp"
+
+namespace http {
+namespace server2 {
+
+connection::connection(boost::asio::io_service& io_service,
+ request_handler& handler)
+ : socket_(io_service),
+ 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/doc/html/boost_asio/example/cpp03/http/server2/connection.hpp b/doc/html/boost_asio/example/cpp03/http/server2/connection.hpp
new file mode 100644
index 0000000000..7dff608277
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/connection.hpp
@@ -0,0 +1,75 @@
+//
+// connection.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service.
+ explicit connection(boost::asio::io_service& io_service,
+ 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/doc/html/boost_asio/example/cpp03/http/server2/header.hpp b/doc/html/boost_asio/example/cpp03/http/server2/header.hpp
new file mode 100644
index 0000000000..45613d3cf4
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/header.hpp
@@ -0,0 +1,28 @@
+//
+// header.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server2/io_service_pool.cpp b/doc/html/boost_asio/example/cpp03/http/server2/io_service_pool.cpp
new file mode 100644
index 0000000000..9b948b8ddb
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/io_service_pool.cpp
@@ -0,0 +1,71 @@
+//
+// io_service_pool.cpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace http {
+namespace server2 {
+
+io_service_pool::io_service_pool(std::size_t pool_size)
+ : next_io_service_(0)
+{
+ if (pool_size == 0)
+ throw std::runtime_error("io_service_pool size is 0");
+
+ // Give all the io_services 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_service_ptr io_service(new boost::asio::io_service);
+ work_ptr work(new boost::asio::io_service::work(*io_service));
+ io_services_.push_back(io_service);
+ work_.push_back(work);
+ }
+}
+
+void io_service_pool::run()
+{
+ // Create a pool of threads to run all of the io_services.
+ std::vector<boost::shared_ptr<boost::thread> > threads;
+ for (std::size_t i = 0; i < io_services_.size(); ++i)
+ {
+ boost::shared_ptr<boost::thread> thread(new boost::thread(
+ boost::bind(&boost::asio::io_service::run, io_services_[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_service_pool::stop()
+{
+ // Explicitly stop all io_services.
+ for (std::size_t i = 0; i < io_services_.size(); ++i)
+ io_services_[i]->stop();
+}
+
+boost::asio::io_service& io_service_pool::get_io_service()
+{
+ // Use a round-robin scheme to choose the next io_service to use.
+ boost::asio::io_service& io_service = *io_services_[next_io_service_];
+ ++next_io_service_;
+ if (next_io_service_ == io_services_.size())
+ next_io_service_ = 0;
+ return io_service;
+}
+
+} // namespace server2
+} // namespace http
diff --git a/doc/html/boost_asio/example/cpp03/http/server2/io_service_pool.hpp b/doc/html/boost_asio/example/cpp03/http/server2/io_service_pool.hpp
new file mode 100644
index 0000000000..a0031388dc
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/io_service_pool.hpp
@@ -0,0 +1,56 @@
+//
+// io_service_pool.hpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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 <vector>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace http {
+namespace server2 {
+
+/// A pool of io_service objects.
+class io_service_pool
+ : private boost::noncopyable
+{
+public:
+ /// Construct the io_service pool.
+ explicit io_service_pool(std::size_t pool_size);
+
+ /// Run all io_service objects in the pool.
+ void run();
+
+ /// Stop all io_service objects in the pool.
+ void stop();
+
+ /// Get an io_service to use.
+ boost::asio::io_service& get_io_service();
+
+private:
+ typedef boost::shared_ptr<boost::asio::io_service> io_service_ptr;
+ typedef boost::shared_ptr<boost::asio::io_service::work> work_ptr;
+
+ /// The pool of io_services.
+ std::vector<io_service_ptr> io_services_;
+
+ /// The work that keeps the io_services running.
+ std::vector<work_ptr> work_;
+
+ /// The next io_service to use for a connection.
+ std::size_t next_io_service_;
+};
+
+} // namespace server2
+} // namespace http
+
+#endif // HTTP_SERVER2_IO_SERVICE_POOL_HPP
diff --git a/doc/html/boost_asio/example/cpp03/http/server2/main.cpp b/doc/html/boost_asio/example/cpp03/http/server2/main.cpp
new file mode 100644
index 0000000000..c6a00836c2
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/main.cpp
@@ -0,0 +1,46 @@
+//
+// main.cpp
+// ~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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/doc/html/boost_asio/example/cpp03/http/server2/mime_types.cpp b/doc/html/boost_asio/example/cpp03/http/server2/mime_types.cpp
new file mode 100644
index 0000000000..5dee5eb3b6
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/mime_types.cpp
@@ -0,0 +1,46 @@
+//
+// mime_types.cpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server2/mime_types.hpp b/doc/html/boost_asio/example/cpp03/http/server2/mime_types.hpp
new file mode 100644
index 0000000000..bf1f6d39e5
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/mime_types.hpp
@@ -0,0 +1,27 @@
+//
+// mime_types.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server2/reply.cpp b/doc/html/boost_asio/example/cpp03/http/server2/reply.cpp
new file mode 100644
index 0000000000..d7f7c987cb
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/reply.cpp
@@ -0,0 +1,256 @@
+//
+// reply.cpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server2/reply.hpp b/doc/html/boost_asio/example/cpp03/http/server2/reply.hpp
new file mode 100644
index 0000000000..fd386217ef
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/reply.hpp
@@ -0,0 +1,64 @@
+//
+// reply.hpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server2/request.hpp b/doc/html/boost_asio/example/cpp03/http/server2/request.hpp
new file mode 100644
index 0000000000..0d70416209
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/request.hpp
@@ -0,0 +1,34 @@
+//
+// request.hpp
+// ~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server2/request_handler.cpp b/doc/html/boost_asio/example/cpp03/http/server2/request_handler.cpp
new file mode 100644
index 0000000000..84f50b8732
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/request_handler.cpp
@@ -0,0 +1,122 @@
+//
+// request_handler.cpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server2/request_handler.hpp b/doc/html/boost_asio/example/cpp03/http/server2/request_handler.hpp
new file mode 100644
index 0000000000..75d8d0d0ba
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/request_handler.hpp
@@ -0,0 +1,46 @@
+//
+// request_handler.hpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server2/request_parser.cpp b/doc/html/boost_asio/example/cpp03/http/server2/request_parser.cpp
new file mode 100644
index 0000000000..8e2e4637e6
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/request_parser.cpp
@@ -0,0 +1,315 @@
+//
+// request_parser.cpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server2/request_parser.hpp b/doc/html/boost_asio/example/cpp03/http/server2/request_parser.hpp
new file mode 100644
index 0000000000..a8c6945492
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/request_parser.hpp
@@ -0,0 +1,95 @@
+//
+// request_parser.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server2/server.cpp b/doc/html/boost_asio/example/cpp03/http/server2/server.cpp
new file mode 100644
index 0000000000..c3504d2764
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/server.cpp
@@ -0,0 +1,77 @@
+//
+// server.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp>
+
+namespace http {
+namespace server2 {
+
+server::server(const std::string& address, const std::string& port,
+ const std::string& doc_root, std::size_t io_service_pool_size)
+ : io_service_pool_(io_service_pool_size),
+ signals_(io_service_pool_.get_io_service()),
+ acceptor_(io_service_pool_.get_io_service()),
+ 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_io_service());
+ boost::asio::ip::tcp::resolver::query query(address, port);
+ boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
+ 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_service_pool_.run();
+}
+
+void server::start_accept()
+{
+ new_connection_.reset(new connection(
+ io_service_pool_.get_io_service(), 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_service_pool_.stop();
+}
+
+} // namespace server2
+} // namespace http
diff --git a/doc/html/boost_asio/example/cpp03/http/server2/server.hpp b/doc/html/boost_asio/example/cpp03/http/server2/server.hpp
new file mode 100644
index 0000000000..412a00cee1
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server2/server.hpp
@@ -0,0 +1,68 @@
+//
+// server.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service_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_service_pool_size);
+
+ /// Run the server's io_service 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_service objects used to perform asynchronous operations.
+ io_service_pool io_service_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/doc/html/boost_asio/example/cpp03/http/server3/connection.cpp b/doc/html/boost_asio/example/cpp03/http/server3/connection.cpp
new file mode 100644
index 0000000000..5f77f03752
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/connection.cpp
@@ -0,0 +1,98 @@
+//
+// connection.cpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp>
+#include "request_handler.hpp"
+
+namespace http {
+namespace server3 {
+
+connection::connection(boost::asio::io_service& io_service,
+ request_handler& handler)
+ : strand_(io_service),
+ socket_(io_service),
+ request_handler_(handler)
+{
+}
+
+boost::asio::ip::tcp::socket& connection::socket()
+{
+ return socket_;
+}
+
+void connection::start()
+{
+ socket_.async_read_some(boost::asio::buffer(buffer_),
+ strand_.wrap(
+ 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(),
+ strand_.wrap(
+ 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(),
+ strand_.wrap(
+ boost::bind(&connection::handle_write, shared_from_this(),
+ boost::asio::placeholders::error)));
+ }
+ else
+ {
+ socket_.async_read_some(boost::asio::buffer(buffer_),
+ strand_.wrap(
+ 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/doc/html/boost_asio/example/cpp03/http/server3/connection.hpp b/doc/html/boost_asio/example/cpp03/http/server3/connection.hpp
new file mode 100644
index 0000000000..e552c4a38c
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/connection.hpp
@@ -0,0 +1,78 @@
+//
+// connection.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service.
+ explicit connection(boost::asio::io_service& io_service,
+ 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::io_service::strand 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/doc/html/boost_asio/example/cpp03/http/server3/header.hpp b/doc/html/boost_asio/example/cpp03/http/server3/header.hpp
new file mode 100644
index 0000000000..54e034ba29
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/header.hpp
@@ -0,0 +1,28 @@
+//
+// header.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server3/main.cpp b/doc/html/boost_asio/example/cpp03/http/server3/main.cpp
new file mode 100644
index 0000000000..ee1812e425
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/main.cpp
@@ -0,0 +1,46 @@
+//
+// main.cpp
+// ~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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/doc/html/boost_asio/example/cpp03/http/server3/mime_types.cpp b/doc/html/boost_asio/example/cpp03/http/server3/mime_types.cpp
new file mode 100644
index 0000000000..97aba9db57
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/mime_types.cpp
@@ -0,0 +1,46 @@
+//
+// mime_types.cpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server3/mime_types.hpp b/doc/html/boost_asio/example/cpp03/http/server3/mime_types.hpp
new file mode 100644
index 0000000000..e4793e0613
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/mime_types.hpp
@@ -0,0 +1,27 @@
+//
+// mime_types.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server3/reply.cpp b/doc/html/boost_asio/example/cpp03/http/server3/reply.cpp
new file mode 100644
index 0000000000..a8a2f3e7b9
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/reply.cpp
@@ -0,0 +1,256 @@
+//
+// reply.cpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server3/reply.hpp b/doc/html/boost_asio/example/cpp03/http/server3/reply.hpp
new file mode 100644
index 0000000000..fa3c7e8f7f
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/reply.hpp
@@ -0,0 +1,64 @@
+//
+// reply.hpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server3/request.hpp b/doc/html/boost_asio/example/cpp03/http/server3/request.hpp
new file mode 100644
index 0000000000..388f80b6f9
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/request.hpp
@@ -0,0 +1,34 @@
+//
+// request.hpp
+// ~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server3/request_handler.cpp b/doc/html/boost_asio/example/cpp03/http/server3/request_handler.cpp
new file mode 100644
index 0000000000..611d06c311
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/request_handler.cpp
@@ -0,0 +1,122 @@
+//
+// request_handler.cpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server3/request_handler.hpp b/doc/html/boost_asio/example/cpp03/http/server3/request_handler.hpp
new file mode 100644
index 0000000000..167b3771d5
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/request_handler.hpp
@@ -0,0 +1,46 @@
+//
+// request_handler.hpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server3/request_parser.cpp b/doc/html/boost_asio/example/cpp03/http/server3/request_parser.cpp
new file mode 100644
index 0000000000..9ef5e7c773
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/request_parser.cpp
@@ -0,0 +1,315 @@
+//
+// request_parser.cpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server3/request_parser.hpp b/doc/html/boost_asio/example/cpp03/http/server3/request_parser.hpp
new file mode 100644
index 0000000000..20da2d55e7
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/request_parser.hpp
@@ -0,0 +1,95 @@
+//
+// request_parser.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server3/server.cpp b/doc/html/boost_asio/example/cpp03/http/server3/server.cpp
new file mode 100644
index 0000000000..3ce54d736c
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/server.cpp
@@ -0,0 +1,90 @@
+//
+// server.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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_service_),
+ acceptor_(io_service_),
+ 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_service_);
+ boost::asio::ip::tcp::resolver::query query(address, port);
+ boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
+ 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_services.
+ 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_service::run, &io_service_)));
+ 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_service_, 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_service_.stop();
+}
+
+} // namespace server3
+} // namespace http
diff --git a/doc/html/boost_asio/example/cpp03/http/server3/server.hpp b/doc/html/boost_asio/example/cpp03/http/server3/server.hpp
new file mode 100644
index 0000000000..5ff6bf3c4f
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server3/server.hpp
@@ -0,0 +1,70 @@
+//
+// server.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service 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_service::run().
+ std::size_t thread_pool_size_;
+
+ /// The io_service used to perform asynchronous operations.
+ boost::asio::io_service io_service_;
+
+ /// 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/doc/html/boost_asio/example/cpp03/http/server4/file_handler.cpp b/doc/html/boost_asio/example/cpp03/http/server4/file_handler.cpp
new file mode 100644
index 0000000000..d1cdb0e2f0
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server4/file_handler.cpp
@@ -0,0 +1,122 @@
+//
+// file_handler.cpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server4/file_handler.hpp b/doc/html/boost_asio/example/cpp03/http/server4/file_handler.hpp
new file mode 100644
index 0000000000..678da526e8
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server4/file_handler.hpp
@@ -0,0 +1,44 @@
+//
+// file_handler.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server4/header.hpp b/doc/html/boost_asio/example/cpp03/http/server4/header.hpp
new file mode 100644
index 0000000000..4478e1de39
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server4/header.hpp
@@ -0,0 +1,28 @@
+//
+// header.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server4/main.cpp b/doc/html/boost_asio/example/cpp03/http/server4/main.cpp
new file mode 100644
index 0000000000..48c3ec516c
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server4/main.cpp
@@ -0,0 +1,58 @@
+//
+// main.cpp
+// ~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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_service io_service;
+
+ // Launch the initial server coroutine.
+ http::server4::server(io_service, argv[1], argv[2],
+ http::server4::file_handler(argv[3]))();
+
+ // Wait for signals indicating time to shut down.
+ boost::asio::signal_set signals(io_service);
+ signals.add(SIGINT);
+ signals.add(SIGTERM);
+#if defined(SIGQUIT)
+ signals.add(SIGQUIT);
+#endif // defined(SIGQUIT)
+ signals.async_wait(boost::bind(
+ &boost::asio::io_service::stop, &io_service));
+
+ // Run the server.
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/http/server4/mime_types.cpp b/doc/html/boost_asio/example/cpp03/http/server4/mime_types.cpp
new file mode 100644
index 0000000000..255f628e89
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server4/mime_types.cpp
@@ -0,0 +1,46 @@
+//
+// mime_types.cpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server4/mime_types.hpp b/doc/html/boost_asio/example/cpp03/http/server4/mime_types.hpp
new file mode 100644
index 0000000000..7f3f04f9ce
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server4/mime_types.hpp
@@ -0,0 +1,27 @@
+//
+// mime_types.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server4/reply.cpp b/doc/html/boost_asio/example/cpp03/http/server4/reply.cpp
new file mode 100644
index 0000000000..b69247c2e6
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server4/reply.cpp
@@ -0,0 +1,256 @@
+//
+// reply.cpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server4/reply.hpp b/doc/html/boost_asio/example/cpp03/http/server4/reply.hpp
new file mode 100644
index 0000000000..1f744a7a17
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server4/reply.hpp
@@ -0,0 +1,64 @@
+//
+// reply.hpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server4/request.hpp b/doc/html/boost_asio/example/cpp03/http/server4/request.hpp
new file mode 100644
index 0000000000..ade91fa965
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server4/request.hpp
@@ -0,0 +1,46 @@
+//
+// request.hpp
+// ~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server4/request_parser.cpp b/doc/html/boost_asio/example/cpp03/http/server4/request_parser.cpp
new file mode 100644
index 0000000000..1031e7046c
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server4/request_parser.cpp
@@ -0,0 +1,226 @@
+//
+// request_parser.cpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server4/request_parser.hpp b/doc/html/boost_asio/example/cpp03/http/server4/request_parser.hpp
new file mode 100644
index 0000000000..34d4c47f58
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server4/request_parser.hpp
@@ -0,0 +1,78 @@
+//
+// request_parser.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/http/server4/server.cpp b/doc/html/boost_asio/example/cpp03/http/server4/server.cpp
new file mode 100644
index 0000000000..66aa329339
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server4/server.cpp
@@ -0,0 +1,121 @@
+//
+// server.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service& io_service,
+ const std::string& address, const std::string& port,
+ boost::function<void(const request&, reply&)> request_handler)
+ : request_handler_(request_handler)
+{
+ tcp::resolver resolver(io_service);
+ tcp::resolver::query query(address, port);
+ acceptor_.reset(new tcp::acceptor(io_service, *resolver.resolve(query)));
+}
+
+// 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_io_service()));
+
+ // 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_service
+ // 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/doc/html/boost_asio/example/cpp03/http/server4/server.hpp b/doc/html/boost_asio/example/cpp03/http/server4/server.hpp
new file mode 100644
index 0000000000..7d01701af9
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/http/server4/server.hpp
@@ -0,0 +1,73 @@
+//
+// server.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service& io_service,
+ 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/doc/html/boost_asio/example/cpp03/icmp/icmp_header.hpp b/doc/html/boost_asio/example/cpp03/icmp/icmp_header.hpp
new file mode 100644
index 0000000000..12272e21a8
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/icmp/icmp_header.hpp
@@ -0,0 +1,94 @@
+//
+// icmp_header.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/icmp/ipv4_header.hpp b/doc/html/boost_asio/example/cpp03/icmp/ipv4_header.hpp
new file mode 100644
index 0000000000..b724707d50
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/icmp/ipv4_header.hpp
@@ -0,0 +1,102 @@
+//
+// ipv4_header.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/icmp/ping.cpp b/doc/html/boost_asio/example/cpp03/icmp/ping.cpp
new file mode 100644
index 0000000000..a9aa57f808
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/icmp/ping.cpp
@@ -0,0 +1,162 @@
+//
+// ping.cpp
+// ~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp>
+#include <istream>
+#include <iostream>
+#include <ostream>
+
+#include "icmp_header.hpp"
+#include "ipv4_header.hpp"
+
+using boost::asio::ip::icmp;
+using boost::asio::deadline_timer;
+namespace posix_time = boost::posix_time;
+
+class pinger
+{
+public:
+ pinger(boost::asio::io_service& io_service, const char* destination)
+ : resolver_(io_service), socket_(io_service, icmp::v4()),
+ timer_(io_service), sequence_number_(0), num_replies_(0)
+ {
+ icmp::resolver::query query(icmp::v4(), destination, "");
+ destination_ = *resolver_.resolve(query);
+
+ 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_ = posix_time::microsec_clock::universal_time();
+ socket_.send_to(request_buffer.data(), destination_);
+
+ // Wait up to five seconds for a reply.
+ num_replies_ = 0;
+ timer_.expires_at(time_sent_ + posix_time::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_ + posix_time::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, _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.
+ posix_time::ptime now = posix_time::microsec_clock::universal_time();
+ 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=" << (now - time_sent_).total_milliseconds() << " ms"
+ << 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_;
+ deadline_timer timer_;
+ unsigned short sequence_number_;
+ posix_time::ptime 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_service io_service;
+ pinger p(io_service, argv[1]);
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << std::endl;
+ }
+}
diff --git a/doc/html/boost_asio/example/cpp03/invocation/prioritised_handlers.cpp b/doc/html/boost_asio/example/cpp03/invocation/prioritised_handlers.cpp
new file mode 100644
index 0000000000..67ffee545a
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/invocation/prioritised_handlers.cpp
@@ -0,0 +1,162 @@
+//
+// prioritised_handlers.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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:
+ 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();
+ }
+ }
+
+ // A generic wrapper class for handlers to allow the invocation to be hooked.
+ template <typename Handler>
+ class wrapped_handler
+ {
+ public:
+ wrapped_handler(handler_priority_queue& q, int p, Handler h)
+ : queue_(q), priority_(p), handler_(h)
+ {
+ }
+
+ void operator()()
+ {
+ handler_();
+ }
+
+ 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_priority_queue& queue_;
+ int priority_;
+ Handler handler_;
+ };
+
+ template <typename Handler>
+ wrapped_handler<Handler> wrap(int priority, Handler handler)
+ {
+ return wrapped_handler<Handler>(*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_;
+};
+
+// Custom invocation hook for wrapped handlers.
+template <typename Function, typename Handler>
+void asio_handler_invoke(Function f,
+ handler_priority_queue::wrapped_handler<Handler>* h)
+{
+ h->queue_.add(h->priority_, f);
+}
+
+//----------------------------------------------------------------------
+
+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_service io_service;
+
+ handler_priority_queue pri_queue;
+
+ // Post a completion handler to be run immediately.
+ io_service.post(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_service, endpoint);
+ tcp::socket server_socket(io_service);
+ acceptor.async_accept(server_socket,
+ pri_queue.wrap(100, high_priority_handler));
+ tcp::socket client_socket(io_service);
+ client_socket.connect(acceptor.local_endpoint());
+
+ // Set a deadline timer to expire immediately.
+ boost::asio::deadline_timer timer(io_service);
+ timer.expires_at(boost::posix_time::neg_infin);
+ timer.async_wait(pri_queue.wrap(42, middle_priority_handler));
+
+ while (io_service.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_service.poll_one())
+ ;
+
+ pri_queue.execute_all();
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/iostreams/daytime_client.cpp b/doc/html/boost_asio/example/cpp03/iostreams/daytime_client.cpp
new file mode 100644
index 0000000000..20ed37211f
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/iostreams/daytime_client.cpp
@@ -0,0 +1,44 @@
+//
+// daytime_client.cpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/iostreams/daytime_server.cpp b/doc/html/boost_asio/example/cpp03/iostreams/daytime_server.cpp
new file mode 100644
index 0000000000..7d7c3f0889
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/iostreams/daytime_server.cpp
@@ -0,0 +1,51 @@
+//
+// daytime_server.cpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service io_service;
+
+ tcp::endpoint endpoint(tcp::v4(), 13);
+ tcp::acceptor acceptor(io_service, endpoint);
+
+ for (;;)
+ {
+ tcp::iostream stream;
+ boost::system::error_code ec;
+ acceptor.accept(*stream.rdbuf(), ec);
+ if (!ec)
+ {
+ stream << make_daytime_string();
+ }
+ }
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << e.what() << std::endl;
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/iostreams/http_client.cpp b/doc/html/boost_asio/example/cpp03/iostreams/http_client.cpp
new file mode 100644
index 0000000000..5f2bfc8736
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/iostreams/http_client.cpp
@@ -0,0 +1,91 @@
+//
+// sync_client.cpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_from_now(boost::posix_time::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/doc/html/boost_asio/example/cpp03/local/connect_pair.cpp b/doc/html/boost_asio/example/cpp03/local/connect_pair.cpp
new file mode 100644
index 0000000000..1d8a7f3196
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/local/connect_pair.cpp
@@ -0,0 +1,142 @@
+//
+// connect_pair.cpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp>
+
+#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
+
+using boost::asio::local::stream_protocol;
+
+class uppercase_filter
+{
+public:
+ uppercase_filter(boost::asio::io_service& io_service)
+ : socket_(io_service)
+ {
+ }
+
+ 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_service* io_service)
+{
+ try
+ {
+ io_service->run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception in thread: " << e.what() << "\n";
+ std::exit(1);
+ }
+}
+
+int main()
+{
+ try
+ {
+ boost::asio::io_service io_service;
+
+ // Create filter and establish a connection to it.
+ uppercase_filter filter(io_service);
+ stream_protocol::socket socket(io_service);
+ boost::asio::local::connect_pair(socket, filter.socket());
+ filter.start();
+
+ // The io_service runs in a background thread to perform filtering.
+ boost::thread thread(boost::bind(run, &io_service));
+
+ 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/doc/html/boost_asio/example/cpp03/local/iostream_client.cpp b/doc/html/boost_asio/example/cpp03/local/iostream_client.cpp
new file mode 100644
index 0000000000..d2e0621c82
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/local/iostream_client.cpp
@@ -0,0 +1,62 @@
+//
+// stream_client.cpp
+// ~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/local/stream_client.cpp b/doc/html/boost_asio/example/cpp03/local/stream_client.cpp
new file mode 100644
index 0000000000..f0f7b978ca
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/local/stream_client.cpp
@@ -0,0 +1,61 @@
+//
+// stream_client.cpp
+// ~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service io_service;
+
+ stream_protocol::socket s(io_service);
+ 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/doc/html/boost_asio/example/cpp03/local/stream_server.cpp b/doc/html/boost_asio/example/cpp03/local/stream_server.cpp
new file mode 100644
index 0000000000..a376a82139
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/local/stream_server.cpp
@@ -0,0 +1,141 @@
+//
+// stream_server.cpp
+// ~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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_service& io_service)
+ : socket_(io_service)
+ {
+ }
+
+ 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_service& io_service, const std::string& file)
+ : io_service_(io_service),
+ acceptor_(io_service, stream_protocol::endpoint(file))
+ {
+ session_ptr new_session(new session(io_service_));
+ 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_service_));
+ acceptor_.async_accept(new_session->socket(),
+ boost::bind(&server::handle_accept, this, new_session,
+ boost::asio::placeholders::error));
+ }
+
+private:
+ boost::asio::io_service& io_service_;
+ 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_service io_service;
+
+ std::remove(argv[1]);
+ server s(io_service, argv[1]);
+
+ io_service.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/doc/html/boost_asio/example/cpp03/multicast/receiver.cpp b/doc/html/boost_asio/example/cpp03/multicast/receiver.cpp
new file mode 100644
index 0000000000..da98a5f21c
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/multicast/receiver.cpp
@@ -0,0 +1,93 @@
+//
+// receiver.cpp
+// ~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp"
+
+const short multicast_port = 30001;
+
+class receiver
+{
+public:
+ receiver(boost::asio::io_service& io_service,
+ const boost::asio::ip::address& listen_address,
+ const boost::asio::ip::address& multicast_address)
+ : socket_(io_service)
+ {
+ // 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_service io_service;
+ receiver r(io_service,
+ boost::asio::ip::address::from_string(argv[1]),
+ boost::asio::ip::address::from_string(argv[2]));
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/multicast/sender.cpp b/doc/html/boost_asio/example/cpp03/multicast/sender.cpp
new file mode 100644
index 0000000000..42e763429c
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/multicast/sender.cpp
@@ -0,0 +1,99 @@
+//
+// sender.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp"
+#include "boost/date_time/posix_time/posix_time_types.hpp"
+
+const short multicast_port = 30001;
+const int max_message_count = 10;
+
+class sender
+{
+public:
+ sender(boost::asio::io_service& io_service,
+ const boost::asio::ip::address& multicast_address)
+ : endpoint_(multicast_address, multicast_port),
+ socket_(io_service, endpoint_.protocol()),
+ timer_(io_service),
+ 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_from_now(boost::posix_time::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::deadline_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_service io_service;
+ sender s(io_service, boost::asio::ip::address::from_string(argv[1]));
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/nonblocking/third_party_lib.cpp b/doc/html/boost_asio/example/cpp03/nonblocking/third_party_lib.cpp
new file mode 100644
index 0000000000..cbaef56e2c
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/nonblocking/third_party_lib.cpp
@@ -0,0 +1,242 @@
+//
+// third_party_lib.cpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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(boost::asio::io_service& io_service)
+ {
+ return pointer(new connection(io_service));
+ }
+
+ tcp::socket& socket()
+ {
+ return socket_;
+ }
+
+ void start()
+ {
+ // Put the socket into non-blocking mode.
+ socket_.non_blocking(true);
+
+ start_operations();
+ }
+
+private:
+ connection(boost::asio::io_service& io_service)
+ : socket_(io_service),
+ 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_read_some(
+ boost::asio::null_buffers(),
+ 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_write_some(
+ boost::asio::null_buffers(),
+ 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_service& io_service, unsigned short port)
+ : acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
+ {
+ start_accept();
+ }
+
+private:
+ void start_accept()
+ {
+ connection::pointer new_connection =
+ connection::create(acceptor_.get_io_service());
+
+ 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_service io_service;
+
+ using namespace std; // For atoi.
+ server s(io_service, atoi(argv[1]));
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/porthopper/client.cpp b/doc/html/boost_asio/example/cpp03/porthopper/client.cpp
new file mode 100644
index 0000000000..ba607a7054
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/porthopper/client.cpp
@@ -0,0 +1,193 @@
+//
+// client.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service io_service;
+
+ // Determine the location of the server.
+ tcp::resolver resolver(io_service);
+ tcp::resolver::query query(host_name, port);
+ tcp::endpoint remote_endpoint = *resolver.resolve(query);
+
+ // Establish the control connection to the server.
+ tcp::socket control_socket(io_service);
+ 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_service, 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_service, 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_service,
+ // 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_service so that we can print it.
+ lambda::bind(&boost::asio::io_service::stop, &io_service),
+ lambda::var(done) = false
+ ]
+ ));
+ }
+
+ // Run the operations in parallel. This will block until all operations
+ // have finished, or until the io_service is interrupted. (No threads!)
+ io_service.reset();
+ io_service.run();
+
+ // If the io_service.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/doc/html/boost_asio/example/cpp03/porthopper/protocol.hpp b/doc/html/boost_asio/example/cpp03/porthopper/protocol.hpp
new file mode 100644
index 0000000000..272aa876eb
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/porthopper/protocol.hpp
@@ -0,0 +1,156 @@
+//
+// protocol.hpp
+// ~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/porthopper/server.cpp b/doc/html/boost_asio/example/cpp03/porthopper/server.cpp
new file mode 100644
index 0000000000..0fdc7e011b
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/porthopper/server.cpp
@@ -0,0 +1,187 @@
+//
+// server.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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::deadline_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_service& io_service, unsigned short port)
+ : acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
+ timer_(io_service),
+ udp_socket_(io_service, 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_io_service()));
+ 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_from_now(boost::posix_time::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_io_service()));
+ 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::deadline_timer(acceptor_.get_io_service()));
+ delay_timer->expires_from_now(boost::posix_time::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_from_now(boost::posix_time::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::deadline_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_service io_service;
+
+ using namespace std; // For atoi.
+ server s(io_service, atoi(argv[1]));
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << std::endl;
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/serialization/client.cpp b/doc/html/boost_asio/example/cpp03/serialization/client.cpp
new file mode 100644
index 0000000000..2d1f609b45
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/serialization/client.cpp
@@ -0,0 +1,125 @@
+//
+// client.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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_service& io_service,
+ const std::string& host, const std::string& service)
+ : connection_(io_service)
+ {
+ // Resolve the host name into an IP address.
+ boost::asio::ip::tcp::resolver resolver(io_service);
+ 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_service 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_service 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_service io_service;
+ s11n_example::client client(io_service, argv[1], argv[2]);
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << e.what() << std::endl;
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/serialization/connection.hpp b/doc/html/boost_asio/example/cpp03/serialization/connection.hpp
new file mode 100644
index 0000000000..41a24ab224
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/serialization/connection.hpp
@@ -0,0 +1,188 @@
+//
+// connection.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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(boost::asio::io_service& io_service)
+ : socket_(io_service)
+ {
+ }
+
+ /// 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);
+ socket_.get_io_service().post(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/doc/html/boost_asio/example/cpp03/serialization/server.cpp b/doc/html/boost_asio/example/cpp03/serialization/server.cpp
new file mode 100644
index 0000000000..8fd045da80
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/serialization/server.cpp
@@ -0,0 +1,123 @@
+//
+// server.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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_service& io_service, unsigned short port)
+ : acceptor_(io_service,
+ 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_io_service()));
+ 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_io_service()));
+ 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_service io_service;
+ s11n_example::server server(io_service, port);
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << e.what() << std::endl;
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/serialization/stock.hpp b/doc/html/boost_asio/example/cpp03/serialization/stock.hpp
new file mode 100644
index 0000000000..67a5d3f49b
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/serialization/stock.hpp
@@ -0,0 +1,50 @@
+//
+// stock.hpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/services/basic_logger.hpp b/doc/html/boost_asio/example/cpp03/services/basic_logger.hpp
new file mode 100644
index 0000000000..380fd21500
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/services/basic_logger.hpp
@@ -0,0 +1,83 @@
+//
+// basic_logger.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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 io_service The io_service object used to locate the logger service.
+ *
+ * @param identifier An identifier for this logger.
+ */
+ explicit basic_logger(boost::asio::io_service& io_service,
+ const std::string& identifier)
+ : service_(boost::asio::use_service<Service>(io_service)),
+ impl_(service_.null())
+ {
+ service_.create(impl_, identifier);
+ }
+
+ /// Destructor.
+ ~basic_logger()
+ {
+ service_.destroy(impl_);
+ }
+
+ /// Get the io_service associated with the object.
+ boost::asio::io_service& get_io_service()
+ {
+ return service_.get_io_service();
+ }
+
+ /// 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/doc/html/boost_asio/example/cpp03/services/daytime_client.cpp b/doc/html/boost_asio/example/cpp03/services/daytime_client.cpp
new file mode 100644
index 0000000000..c812063acb
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/services/daytime_client.cpp
@@ -0,0 +1,85 @@
+//
+// daytime_client.cpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp>
+#include <iostream>
+#include "logger.hpp"
+#include "stream_socket_service.hpp"
+
+typedef boost::asio::basic_stream_socket<boost::asio::ip::tcp,
+ services::stream_socket_service<boost::asio::ip::tcp> > debug_stream_socket;
+
+char read_buffer[1024];
+
+void read_handler(const boost::system::error_code& e,
+ std::size_t bytes_transferred, debug_stream_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));
+ }
+}
+
+void connect_handler(const boost::system::error_code& e, debug_stream_socket* s)
+{
+ if (!e)
+ {
+ 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::cerr << e.message() << std::endl;
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ if (argc != 2)
+ {
+ std::cerr << "Usage: daytime_client <host>" << std::endl;
+ return 1;
+ }
+
+ boost::asio::io_service io_service;
+
+ // Set the name of the file that all logger instances will use.
+ services::logger logger(io_service, "");
+ logger.use_file("log.txt");
+
+ // Resolve the address corresponding to the given host.
+ boost::asio::ip::tcp::resolver resolver(io_service);
+ boost::asio::ip::tcp::resolver::query query(argv[1], "daytime");
+ boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+
+ // Start an asynchronous connect.
+ debug_stream_socket socket(io_service);
+ boost::asio::async_connect(socket, iterator,
+ boost::bind(connect_handler,
+ boost::asio::placeholders::error, &socket));
+
+ // Run the io_service until all operations have finished.
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << e.what() << std::endl;
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/services/logger.hpp b/doc/html/boost_asio/example/cpp03/services/logger.hpp
new file mode 100644
index 0000000000..749620d1ab
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/services/logger.hpp
@@ -0,0 +1,24 @@
+//
+// logger.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/services/logger_service.cpp b/doc/html/boost_asio/example/cpp03/services/logger_service.cpp
new file mode 100644
index 0000000000..23fc764f6b
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/services/logger_service.cpp
@@ -0,0 +1,17 @@
+//
+// logger_service.cpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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"
+
+namespace services {
+
+boost::asio::io_service::id logger_service::id;
+
+} // namespace services
diff --git a/doc/html/boost_asio/example/cpp03/services/logger_service.hpp b/doc/html/boost_asio/example/cpp03/services/logger_service.hpp
new file mode 100644
index 0000000000..d5e76437da
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/services/logger_service.hpp
@@ -0,0 +1,145 @@
+//
+// logger_service.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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::io_service::service
+{
+public:
+ /// The unique service identifier.
+ static boost::asio::io_service::id id;
+
+ /// 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_service.
+ logger_service(boost::asio::io_service& io_service)
+ : boost::asio::io_service::service(io_service),
+ work_io_service_(),
+ work_(new boost::asio::io_service::work(work_io_service_)),
+ work_thread_(new boost::thread(
+ boost::bind(&boost::asio::io_service::run, &work_io_service_)))
+ {
+ }
+
+ /// Destructor shuts down the private io_service.
+ ~logger_service()
+ {
+ /// Indicate that we have finished with the private io_service. Its
+ /// io_service::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_service()
+ {
+ }
+
+ /// 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.
+ work_io_service_.post(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 opening the file to the background thread.
+ work_io_service_.post(boost::bind(
+ &logger_service::log_impl, this, os.str()));
+ }
+
+private:
+ /// Helper function used to open the output file from within the private
+ /// io_service'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_service's
+ /// thread.
+ void log_impl(const std::string& text)
+ {
+ ofstream_ << text << std::endl;
+ }
+
+ /// Private io_service used for performing logging operations.
+ boost::asio::io_service work_io_service_;
+
+ /// Work for the private io_service to perform. If we do not give the
+ /// io_service some work to do then the io_service::run() function will exit
+ /// immediately.
+ boost::scoped_ptr<boost::asio::io_service::work> work_;
+
+ /// Thread used for running the work io_service'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/doc/html/boost_asio/example/cpp03/services/stream_socket_service.hpp b/doc/html/boost_asio/example/cpp03/services/stream_socket_service.hpp
new file mode 100644
index 0000000000..4c976604e1
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/services/stream_socket_service.hpp
@@ -0,0 +1,351 @@
+//
+// stream_socket_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_STREAM_SOCKET_SERVICE_HPP
+#define SERVICES_STREAM_SOCKET_SERVICE_HPP
+
+#include <boost/asio.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/lexical_cast.hpp>
+#include "logger.hpp"
+
+namespace services {
+
+/// Debugging stream socket service that wraps the normal stream socket service.
+template <typename Protocol>
+class stream_socket_service
+ : public boost::asio::io_service::service
+{
+private:
+ /// The type of the wrapped stream socket service.
+ typedef boost::asio::stream_socket_service<Protocol> service_impl_type;
+
+public:
+ /// The unique service identifier.
+ static boost::asio::io_service::id id;
+
+ /// The protocol type.
+ typedef Protocol protocol_type;
+
+ /// The endpoint type.
+ typedef typename Protocol::endpoint endpoint_type;
+
+ /// The implementation type of a stream socket.
+ typedef typename service_impl_type::implementation_type implementation_type;
+
+ /// The native type of a stream socket.
+ typedef typename service_impl_type::native_handle_type native_handle_type;
+
+ /// Construct a new stream socket service for the specified io_service.
+ explicit stream_socket_service(boost::asio::io_service& io_service)
+ : boost::asio::io_service::service(io_service),
+ service_impl_(boost::asio::use_service<service_impl_type>(io_service)),
+ logger_(io_service, "stream_socket")
+ {
+ }
+
+ /// Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ }
+
+ /// Construct a new stream socket implementation.
+ void construct(implementation_type& impl)
+ {
+ service_impl_.construct(impl);
+ }
+
+ /// Destroy a stream socket implementation.
+ void destroy(implementation_type& impl)
+ {
+ service_impl_.destroy(impl);
+ }
+
+ /// Open a new stream socket implementation.
+ boost::system::error_code open(implementation_type& impl,
+ const protocol_type& protocol, boost::system::error_code& ec)
+ {
+ logger_.log("Opening new socket");
+ return service_impl_.open(impl, protocol, ec);
+ }
+
+ /// Open a stream socket from an existing native socket.
+ boost::system::error_code assign(implementation_type& impl,
+ const protocol_type& protocol, const native_handle_type& native_socket,
+ boost::system::error_code& ec)
+ {
+ logger_.log("Assigning from a native socket");
+ return service_impl_.assign(impl, protocol, native_socket, ec);
+ }
+
+ /// Determine whether the socket is open.
+ bool is_open(const implementation_type& impl) const
+ {
+ logger_.log("Checking if socket is open");
+ return service_impl_.is_open(impl);
+ }
+
+ /// Close a stream socket implementation.
+ boost::system::error_code close(implementation_type& impl,
+ boost::system::error_code& ec)
+ {
+ logger_.log("Closing socket");
+ return service_impl_.close(impl, ec);
+ }
+
+ /// Determine whether the socket is at the out-of-band data mark.
+ bool at_mark(const implementation_type& impl,
+ boost::system::error_code& ec) const
+ {
+ logger_.log("Checking if socket is at out-of-band data mark");
+ return service_impl_.at_mark(impl, ec);
+ }
+
+ /// Determine the number of bytes available for reading.
+ std::size_t available(const implementation_type& impl,
+ boost::system::error_code& ec) const
+ {
+ logger_.log("Determining number of bytes available for reading");
+ return service_impl_.available(impl, ec);
+ }
+
+ /// Bind the stream socket to the specified local endpoint.
+ boost::system::error_code bind(implementation_type& impl,
+ const endpoint_type& endpoint, boost::system::error_code& ec)
+ {
+ logger_.log("Binding socket");
+ return service_impl_.bind(impl, endpoint, ec);
+ }
+
+ /// Connect the stream socket to the specified endpoint.
+ boost::system::error_code connect(implementation_type& impl,
+ const endpoint_type& peer_endpoint, boost::system::error_code& ec)
+ {
+ logger_.log("Connecting socket to " +
+ boost::lexical_cast<std::string>(peer_endpoint));
+ return service_impl_.connect(impl, peer_endpoint, ec);
+ }
+
+ /// Handler to wrap asynchronous connect completion.
+ template <typename Handler>
+ class connect_handler
+ {
+ public:
+ connect_handler(Handler h, logger& l)
+ : handler_(h),
+ logger_(l)
+ {
+ }
+
+ void operator()(const boost::system::error_code& e)
+ {
+ if (e)
+ {
+ std::string msg = "Asynchronous connect failed: ";
+ msg += e.message();
+ logger_.log(msg);
+ }
+ else
+ {
+ logger_.log("Asynchronous connect succeeded");
+ }
+
+ handler_(e);
+ }
+
+ private:
+ Handler handler_;
+ logger& logger_;
+ };
+
+ /// Start an asynchronous connect.
+ template <typename Handler>
+ void async_connect(implementation_type& impl,
+ const endpoint_type& peer_endpoint, Handler handler)
+ {
+ logger_.log("Starting asynchronous connect to " +
+ boost::lexical_cast<std::string>(peer_endpoint));
+ service_impl_.async_connect(impl, peer_endpoint,
+ connect_handler<Handler>(handler, logger_));
+ }
+
+ /// Set a socket option.
+ template <typename Option>
+ boost::system::error_code set_option(implementation_type& impl,
+ const Option& option, boost::system::error_code& ec)
+ {
+ logger_.log("Setting socket option");
+ return service_impl_.set_option(impl, option, ec);
+ }
+
+ /// Get a socket option.
+ template <typename Option>
+ boost::system::error_code get_option(const implementation_type& impl,
+ Option& option, boost::system::error_code& ec) const
+ {
+ logger_.log("Getting socket option");
+ return service_impl_.get_option(impl, option, ec);
+ }
+
+ /// Perform an IO control command on the socket.
+ template <typename IO_Control_Command>
+ boost::system::error_code io_control(implementation_type& impl,
+ IO_Control_Command& command, boost::system::error_code& ec)
+ {
+ logger_.log("Performing IO control command on socket");
+ return service_impl_.io_control(impl, command, ec);
+ }
+
+ /// Get the local endpoint.
+ endpoint_type local_endpoint(const implementation_type& impl,
+ boost::system::error_code& ec) const
+ {
+ logger_.log("Getting socket's local endpoint");
+ return service_impl_.local_endpoint(impl, ec);
+ }
+
+ /// Get the remote endpoint.
+ endpoint_type remote_endpoint(const implementation_type& impl,
+ boost::system::error_code& ec) const
+ {
+ logger_.log("Getting socket's remote endpoint");
+ return service_impl_.remote_endpoint(impl, ec);
+ }
+
+ /// Disable sends or receives on the socket.
+ boost::system::error_code shutdown(implementation_type& impl,
+ boost::asio::socket_base::shutdown_type what,
+ boost::system::error_code& ec)
+ {
+ logger_.log("Shutting down socket");
+ return service_impl_.shutdown(impl, what, ec);
+ }
+
+ /// Send the given data to the peer.
+ template <typename Const_Buffers>
+ std::size_t send(implementation_type& impl, const Const_Buffers& buffers,
+ boost::asio::socket_base::message_flags flags,
+ boost::system::error_code& ec)
+ {
+ logger_.log("Sending data on socket");
+ return service_impl_.send(impl, buffers, flags, ec);
+ }
+
+ /// Handler to wrap asynchronous send completion.
+ template <typename Handler>
+ class send_handler
+ {
+ public:
+ send_handler(Handler h, logger& l)
+ : handler_(h),
+ logger_(l)
+ {
+ }
+
+ void operator()(const boost::system::error_code& e,
+ std::size_t bytes_transferred)
+ {
+ if (e)
+ {
+ std::string msg = "Asynchronous send failed: ";
+ msg += e.message();
+ logger_.log(msg);
+ }
+ else
+ {
+ logger_.log("Asynchronous send succeeded");
+ }
+
+ handler_(e, bytes_transferred);
+ }
+
+ private:
+ Handler handler_;
+ logger& logger_;
+ };
+
+ /// Start an asynchronous send.
+ template <typename Const_Buffers, typename Handler>
+ void async_send(implementation_type& impl, const Const_Buffers& buffers,
+ boost::asio::socket_base::message_flags flags, Handler handler)
+ {
+ logger_.log("Starting asynchronous send");
+ service_impl_.async_send(impl, buffers, flags,
+ send_handler<Handler>(handler, logger_));
+ }
+
+ /// Receive some data from the peer.
+ template <typename Mutable_Buffers>
+ std::size_t receive(implementation_type& impl,
+ const Mutable_Buffers& buffers,
+ boost::asio::socket_base::message_flags flags,
+ boost::system::error_code& ec)
+ {
+ logger_.log("Receiving data on socket");
+ return service_impl_.receive(impl, buffers, flags, ec);
+ }
+
+ /// Handler to wrap asynchronous receive completion.
+ template <typename Handler>
+ class receive_handler
+ {
+ public:
+ receive_handler(Handler h, logger& l)
+ : handler_(h),
+ logger_(l)
+ {
+ }
+
+ void operator()(const boost::system::error_code& e,
+ std::size_t bytes_transferred)
+ {
+ if (e)
+ {
+ std::string msg = "Asynchronous receive failed: ";
+ msg += e.message();
+ logger_.log(msg);
+ }
+ else
+ {
+ logger_.log("Asynchronous receive succeeded");
+ }
+
+ handler_(e, bytes_transferred);
+ }
+
+ private:
+ Handler handler_;
+ logger& logger_;
+ };
+
+ /// Start an asynchronous receive.
+ template <typename Mutable_Buffers, typename Handler>
+ void async_receive(implementation_type& impl, const Mutable_Buffers& buffers,
+ boost::asio::socket_base::message_flags flags, Handler handler)
+ {
+ logger_.log("Starting asynchronous receive");
+ service_impl_.async_receive(impl, buffers, flags,
+ receive_handler<Handler>(handler, logger_));
+ }
+
+private:
+ /// The wrapped stream socket service.
+ service_impl_type& service_impl_;
+
+ /// The logger used for writing debug messages.
+ mutable logger logger_;
+};
+
+template <typename Protocol>
+boost::asio::io_service::id stream_socket_service<Protocol>::id;
+
+} // namespace services
+
+#endif // SERVICES_STREAM_SOCKET_SERVICE_HPP
diff --git a/doc/html/boost_asio/example/cpp03/socks4/socks4.hpp b/doc/html/boost_asio/example/cpp03/socks4/socks4.hpp
new file mode 100644
index 0000000000..07dc1a1ce2
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/socks4/socks4.hpp
@@ -0,0 +1,144 @@
+//
+// socks4.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/doc/html/boost_asio/example/cpp03/socks4/sync_client.cpp b/doc/html/boost_asio/example/cpp03/socks4/sync_client.cpp
new file mode 100644
index 0000000000..d331969f6c
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/socks4/sync_client.cpp
@@ -0,0 +1,95 @@
+//
+// sync_client.cpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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_service io_service;
+
+ // Get a list of endpoints corresponding to the SOCKS 4 server name.
+ tcp::resolver resolver(io_service);
+ tcp::resolver::query socks_query(argv[1], argv[2]);
+ tcp::resolver::iterator endpoint_iterator = resolver.resolve(socks_query);
+
+ // Try each endpoint until we successfully establish a connection to the
+ // SOCKS 4 server.
+ tcp::socket socket(io_service);
+ boost::asio::connect(socket, endpoint_iterator);
+
+ // 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::resolver::query http_query(tcp::v4(), "www.boost.org", "http");
+ tcp::endpoint http_endpoint = *resolver.resolve(http_query);
+
+ // 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/doc/html/boost_asio/example/cpp03/spawn/echo_server.cpp b/doc/html/boost_asio/example/cpp03/spawn/echo_server.cpp
new file mode 100644
index 0000000000..aa718c441b
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/spawn/echo_server.cpp
@@ -0,0 +1,122 @@
+//
+// echo_server.cpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/deadline_timer.hpp>
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/spawn.hpp>
+#include <boost/asio/write.hpp>
+#include <boost/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_service& io_service)
+ : strand_(io_service),
+ socket_(io_service),
+ timer_(io_service)
+ {
+ }
+
+ tcp::socket& socket()
+ {
+ return socket_;
+ }
+
+ void go()
+ {
+ boost::asio::spawn(strand_,
+ boost::bind(&session::echo,
+ shared_from_this(), _1));
+ boost::asio::spawn(strand_,
+ boost::bind(&session::timeout,
+ shared_from_this(), _1));
+ }
+
+private:
+ void echo(boost::asio::yield_context yield)
+ {
+ try
+ {
+ char data[128];
+ for (;;)
+ {
+ timer_.expires_from_now(boost::posix_time::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_.expires_from_now() <= boost::posix_time::seconds(0))
+ socket_.close();
+ }
+ }
+
+ boost::asio::io_service::strand strand_;
+ tcp::socket socket_;
+ boost::asio::deadline_timer timer_;
+};
+
+void do_accept(boost::asio::io_service& io_service,
+ unsigned short port, boost::asio::yield_context yield)
+{
+ tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), port));
+
+ for (;;)
+ {
+ boost::system::error_code ec;
+ boost::shared_ptr<session> new_session(new session(io_service));
+ 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_service io_service;
+
+ boost::asio::spawn(io_service,
+ boost::bind(do_accept,
+ boost::ref(io_service), atoi(argv[1]), _1));
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/ssl/client.cpp b/doc/html/boost_asio/example/cpp03/ssl/client.cpp
new file mode 100644
index 0000000000..8a86865550
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/ssl/client.cpp
@@ -0,0 +1,156 @@
+//
+// client.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.hpp>
+#include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
+
+enum { max_length = 1024 };
+
+class client
+{
+public:
+ client(boost::asio::io_service& io_service,
+ boost::asio::ssl::context& context,
+ boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
+ : socket_(io_service, context)
+ {
+ socket_.set_verify_mode(boost::asio::ssl::verify_peer);
+ socket_.set_verify_callback(
+ boost::bind(&client::verify_certificate, this, _1, _2));
+
+ boost::asio::async_connect(socket_.lowest_layer(), endpoint_iterator,
+ 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_service io_service;
+
+ boost::asio::ip::tcp::resolver resolver(io_service);
+ boost::asio::ip::tcp::resolver::query query(argv[1], argv[2]);
+ boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+
+ boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
+ ctx.load_verify_file("ca.pem");
+
+ client c(io_service, ctx, iterator);
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/ssl/server.cpp b/doc/html/boost_asio/example/cpp03/ssl/server.cpp
new file mode 100644
index 0000000000..772b178435
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/ssl/server.cpp
@@ -0,0 +1,170 @@
+//
+// server.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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_service& io_service,
+ boost::asio::ssl::context& context)
+ : socket_(io_service, 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_service& io_service, unsigned short port)
+ : io_service_(io_service),
+ acceptor_(io_service,
+ 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_service_, 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_service& io_service_;
+ 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_service io_service;
+
+ using namespace std; // For atoi.
+ server s(io_service, atoi(argv[1]));
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/timeouts/async_tcp_client.cpp b/doc/html/boost_asio/example/cpp03/timeouts/async_tcp_client.cpp
new file mode 100644
index 0000000000..9d52300ab3
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/timeouts/async_tcp_client.cpp
@@ -0,0 +1,306 @@
+//
+// async_tcp_client.cpp
+// ~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/deadline_timer.hpp>
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/read_until.hpp>
+#include <boost/asio/streambuf.hpp>
+#include <boost/asio/write.hpp>
+#include <boost/bind.hpp>
+#include <iostream>
+
+using boost::asio::deadline_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
+// message sending.
+//
+class client
+{
+public:
+ client(boost::asio::io_service& io_service)
+ : stopped_(false),
+ socket_(io_service),
+ deadline_(io_service),
+ heartbeat_timer_(io_service)
+ {
+ }
+
+ // Called by the user of the client class to initiate the connection process.
+ // The endpoint iterator will have been obtained using a tcp::resolver.
+ void start(tcp::resolver::iterator endpoint_iter)
+ {
+ // Start the connect actor.
+ start_connect(endpoint_iter);
+
+ // 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::iterator endpoint_iter)
+ {
+ if (endpoint_iter != tcp::resolver::iterator())
+ {
+ std::cout << "Trying " << endpoint_iter->endpoint() << "...\n";
+
+ // Set a deadline for the connect operation.
+ deadline_.expires_from_now(boost::posix_time::seconds(60));
+
+ // Start the asynchronous connect operation.
+ socket_.async_connect(endpoint_iter->endpoint(),
+ boost::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& ec,
+ tcp::resolver::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_from_now(boost::posix_time::seconds(30));
+
+ // Start an asynchronous operation to read a newline-delimited message.
+ boost::asio::async_read_until(socket_, input_buffer_, '\n',
+ boost::bind(&client::handle_read, this, _1));
+ }
+
+ void handle_read(const boost::system::error_code& ec)
+ {
+ if (stopped_)
+ return;
+
+ if (!ec)
+ {
+ // Extract the newline-delimited message from the buffer.
+ std::string line;
+ std::istream is(&input_buffer_);
+ std::getline(is, line);
+
+ // 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, _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_from_now(boost::posix_time::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_.expires_at() <= deadline_timer::traits_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 positive
+ // infinity so that the actor takes no action until a new deadline is set.
+ deadline_.expires_at(boost::posix_time::pos_infin);
+ }
+
+ // Put the actor back to sleep.
+ deadline_.async_wait(boost::bind(&client::check_deadline, this));
+ }
+
+private:
+ bool stopped_;
+ tcp::socket socket_;
+ boost::asio::streambuf input_buffer_;
+ deadline_timer deadline_;
+ deadline_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_service io_service;
+ tcp::resolver r(io_service);
+ client c(io_service);
+
+ c.start(r.resolve(tcp::resolver::query(argv[1], argv[2])));
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/timeouts/blocking_tcp_client.cpp b/doc/html/boost_asio/example/cpp03/timeouts/blocking_tcp_client.cpp
new file mode 100644
index 0000000000..765a2a7a71
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/timeouts/blocking_tcp_client.cpp
@@ -0,0 +1,241 @@
+//
+// blocking_tcp_client.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/deadline_timer.hpp>
+#include <boost/asio/io_service.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 <string>
+#include <boost/lambda/bind.hpp>
+#include <boost/lambda/lambda.hpp>
+
+using boost::asio::deadline_timer;
+using boost::asio::ip::tcp;
+using boost::lambda::bind;
+using boost::lambda::var;
+using boost::lambda::_1;
+
+//----------------------------------------------------------------------
+
+//
+// This class manages socket timeouts by applying the concept of a deadline.
+// Each asynchronous operation is given a deadline by which it must complete.
+// Deadlines are enforced by an "actor" that persists for the lifetime of the
+// client object:
+//
+// +----------------+
+// | |
+// | check_deadline |<---+
+// | | |
+// +----------------+ | async_wait()
+// | |
+// +---------+
+//
+// If the actor determines that the deadline has expired, the socket is closed
+// and any outstanding operations are consequently cancelled. The socket
+// operations themselves use boost::lambda function objects as completion
+// handlers. For a given socket operation, the client object runs the
+// io_service to block thread execution until the actor completes.
+//
+class client
+{
+public:
+ client()
+ : socket_(io_service_),
+ deadline_(io_service_)
+ {
+ // No deadline is required until the first socket operation is started. We
+ // set the deadline to positive infinity so that the actor takes no action
+ // until a specific deadline is set.
+ deadline_.expires_at(boost::posix_time::pos_infin);
+
+ // Start the persistent actor that checks for deadline expiry.
+ check_deadline();
+ }
+
+ void connect(const std::string& host, const std::string& service,
+ boost::posix_time::time_duration timeout)
+ {
+ // Resolve the host name and service to a list of endpoints.
+ tcp::resolver::query query(host, service);
+ tcp::resolver::iterator iter = tcp::resolver(io_service_).resolve(query);
+
+ // Set a deadline for the asynchronous operation. As a host name may
+ // resolve to multiple endpoints, this function uses the composed operation
+ // async_connect. The deadline applies to the entire operation, rather than
+ // individual connection attempts.
+ deadline_.expires_from_now(timeout);
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ // 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::asio::async_connect(socket_, iter, var(ec) = _1);
+
+ // Block until the asynchronous operation has completed.
+ do io_service_.run_one(); while (ec == boost::asio::error::would_block);
+
+ // Determine whether a connection was successfully established. The
+ // deadline actor may have had a chance to run and close our socket, even
+ // though the connect operation notionally succeeded. Therefore we must
+ // check whether the socket is still open before deciding if we succeeded
+ // or failed.
+ if (ec || !socket_.is_open())
+ throw boost::system::system_error(
+ ec ? ec : boost::asio::error::operation_aborted);
+ }
+
+ std::string read_line(boost::posix_time::time_duration timeout)
+ {
+ // Set a deadline for the asynchronous operation. Since this function uses
+ // a composed operation (async_read_until), the deadline applies to the
+ // entire operation, rather than individual reads from the socket.
+ deadline_.expires_from_now(timeout);
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ // 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::asio::async_read_until(socket_, input_buffer_, '\n', var(ec) = _1);
+
+ // Block until the asynchronous operation has completed.
+ do io_service_.run_one(); while (ec == boost::asio::error::would_block);
+
+ if (ec)
+ throw boost::system::system_error(ec);
+
+ std::string line;
+ std::istream is(&input_buffer_);
+ std::getline(is, line);
+ return line;
+ }
+
+ void write_line(const std::string& line,
+ boost::posix_time::time_duration timeout)
+ {
+ std::string data = line + "\n";
+
+ // Set a deadline for the asynchronous operation. Since this function uses
+ // a composed operation (async_write), the deadline applies to the entire
+ // operation, rather than individual writes to the socket.
+ deadline_.expires_from_now(timeout);
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ // 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::asio::async_write(socket_, boost::asio::buffer(data), var(ec) = _1);
+
+ // Block until the asynchronous operation has completed.
+ do io_service_.run_one(); while (ec == boost::asio::error::would_block);
+
+ if (ec)
+ throw boost::system::system_error(ec);
+ }
+
+private:
+ void check_deadline()
+ {
+ // 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_.expires_at() <= deadline_timer::traits_type::now())
+ {
+ // The deadline has passed. The socket is closed so that any outstanding
+ // asynchronous operations are cancelled. This allows the blocked
+ // connect(), read_line() or write_line() functions to return.
+ boost::system::error_code ignored_ec;
+ socket_.close(ignored_ec);
+
+ // There is no longer an active deadline. The expiry is set to positive
+ // infinity so that the actor takes no action until a new deadline is set.
+ deadline_.expires_at(boost::posix_time::pos_infin);
+ }
+
+ // Put the actor back to sleep.
+ deadline_.async_wait(bind(&client::check_deadline, this));
+ }
+
+ boost::asio::io_service io_service_;
+ tcp::socket socket_;
+ deadline_timer deadline_;
+ boost::asio::streambuf input_buffer_;
+};
+
+//----------------------------------------------------------------------
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ if (argc != 4)
+ {
+ std::cerr << "Usage: blocking_tcp <host> <port> <message>\n";
+ return 1;
+ }
+
+ client c;
+ c.connect(argv[1], argv[2], boost::posix_time::seconds(10));
+
+ boost::posix_time::ptime time_sent =
+ boost::posix_time::microsec_clock::universal_time();
+
+ c.write_line(argv[3], boost::posix_time::seconds(10));
+
+ for (;;)
+ {
+ std::string line = c.read_line(boost::posix_time::seconds(10));
+
+ // Keep going until we get back the line that was sent.
+ if (line == argv[3])
+ break;
+ }
+
+ boost::posix_time::ptime time_received =
+ boost::posix_time::microsec_clock::universal_time();
+
+ std::cout << "Round trip time: ";
+ std::cout << (time_received - time_sent).total_microseconds();
+ std::cout << " microseconds\n";
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/timeouts/blocking_udp_client.cpp b/doc/html/boost_asio/example/cpp03/timeouts/blocking_udp_client.cpp
new file mode 100644
index 0000000000..fd81522cc9
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/timeouts/blocking_udp_client.cpp
@@ -0,0 +1,182 @@
+//
+// blocking_udp_client.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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/deadline_timer.hpp>
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/ip/udp.hpp>
+#include <cstdlib>
+#include <boost/bind.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <iostream>
+
+using boost::asio::deadline_timer;
+using boost::asio::ip::udp;
+
+//----------------------------------------------------------------------
+
+//
+// This class manages socket timeouts by applying the concept of a deadline.
+// Each asynchronous operation is given a deadline by which it must complete.
+// Deadlines are enforced by an "actor" that persists for the lifetime of the
+// client object:
+//
+// +----------------+
+// | |
+// | check_deadline |<---+
+// | | |
+// +----------------+ | async_wait()
+// | |
+// +---------+
+//
+// If the actor determines that the deadline has expired, any outstanding
+// socket operations are cancelled. The socket operations themselves are
+// implemented as transient actors:
+//
+// +---------------+
+// | |
+// | receive |
+// | |
+// +---------------+
+// |
+// async_- | +----------------+
+// receive() | | |
+// +--->| handle_receive |
+// | |
+// +----------------+
+//
+// The client object runs the io_service to block thread execution until the
+// actor completes.
+//
+class client
+{
+public:
+ client(const udp::endpoint& listen_endpoint)
+ : socket_(io_service_, listen_endpoint),
+ deadline_(io_service_)
+ {
+ // No deadline is required until the first socket operation is started. We
+ // set the deadline to positive infinity so that the actor takes no action
+ // until a specific deadline is set.
+ deadline_.expires_at(boost::posix_time::pos_infin);
+
+ // Start the persistent actor that checks for deadline expiry.
+ check_deadline();
+ }
+
+ std::size_t receive(const boost::asio::mutable_buffer& buffer,
+ boost::posix_time::time_duration timeout, boost::system::error_code& ec)
+ {
+ // Set a deadline for the asynchronous operation.
+ deadline_.expires_from_now(timeout);
+
+ // Set up the variables that receive the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ ec = boost::asio::error::would_block;
+ std::size_t length = 0;
+
+ // Start the asynchronous operation itself. The handle_receive function
+ // used as a callback will update the ec and length variables.
+ socket_.async_receive(boost::asio::buffer(buffer),
+ boost::bind(&client::handle_receive, _1, _2, &ec, &length));
+
+ // Block until the asynchronous operation has completed.
+ do io_service_.run_one(); while (ec == boost::asio::error::would_block);
+
+ return length;
+ }
+
+private:
+ void check_deadline()
+ {
+ // 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_.expires_at() <= deadline_timer::traits_type::now())
+ {
+ // The deadline has passed. The outstanding asynchronous operation needs
+ // to be cancelled so that the blocked receive() function will return.
+ //
+ // Please note that cancel() has portability issues on some versions of
+ // Microsoft Windows, and it may be necessary to use close() instead.
+ // Consult the documentation for cancel() for further information.
+ socket_.cancel();
+
+ // There is no longer an active deadline. The expiry is set to positive
+ // infinity so that the actor takes no action until a new deadline is set.
+ deadline_.expires_at(boost::posix_time::pos_infin);
+ }
+
+ // Put the actor back to sleep.
+ deadline_.async_wait(boost::bind(&client::check_deadline, this));
+ }
+
+ 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_service io_service_;
+ udp::socket socket_;
+ deadline_timer deadline_;
+};
+
+//----------------------------------------------------------------------
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ using namespace std; // For atoi.
+
+ if (argc != 3)
+ {
+ std::cerr << "Usage: blocking_udp_timeout <listen_addr> <listen_port>\n";
+ return 1;
+ }
+
+ udp::endpoint listen_endpoint(
+ boost::asio::ip::address::from_string(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::posix_time::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/doc/html/boost_asio/example/cpp03/timeouts/server.cpp b/doc/html/boost_asio/example/cpp03/timeouts/server.cpp
new file mode 100644
index 0000000000..ce4dce60a8
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/timeouts/server.cpp
@@ -0,0 +1,427 @@
+//
+// server.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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 <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/asio/deadline_timer.hpp>
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/ip/udp.hpp>
+#include <boost/asio/read_until.hpp>
+#include <boost/asio/streambuf.hpp>
+#include <boost/asio/write.hpp>
+
+using boost::asio::deadline_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, _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 deadline_timer as an asynchronous condition variable. The
+// deadline_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_service& io_service, channel& ch)
+ : channel_(ch),
+ socket_(io_service),
+ input_deadline_(io_service),
+ non_empty_output_queue_(io_service),
+ output_deadline_(io_service)
+ {
+ input_deadline_.expires_at(boost::posix_time::pos_infin);
+ output_deadline_.expires_at(boost::posix_time::pos_infin);
+
+ // The non_empty_output_queue_ deadline_timer is set to pos_infin 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(boost::posix_time::pos_infin);
+ }
+
+ 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(boost::posix_time::neg_infin);
+ }
+
+ void start_read()
+ {
+ // Set a deadline for the read operation.
+ input_deadline_.expires_from_now(boost::posix_time::seconds(30));
+
+ // Start an asynchronous operation to read a newline-delimited message.
+ boost::asio::async_read_until(socket_, input_buffer_, '\n',
+ boost::bind(&tcp_session::handle_read, shared_from_this(), _1));
+ }
+
+ void handle_read(const boost::system::error_code& ec)
+ {
+ if (stopped())
+ return;
+
+ if (!ec)
+ {
+ // Extract the newline-delimited message from the buffer.
+ std::string msg;
+ std::istream is(&input_buffer_);
+ std::getline(is, msg);
+
+ 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(boost::posix_time::neg_infin);
+ }
+ }
+
+ 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(boost::posix_time::pos_infin);
+ 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_from_now(boost::posix_time::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(), _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(deadline_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->expires_at() <= deadline_timer::traits_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_;
+ boost::asio::streambuf input_buffer_;
+ deadline_timer input_deadline_;
+ std::deque<std::string> output_queue_;
+ deadline_timer non_empty_output_queue_;
+ deadline_timer output_deadline_;
+};
+
+typedef boost::shared_ptr<tcp_session> tcp_session_ptr;
+
+//----------------------------------------------------------------------
+
+class udp_broadcaster
+ : public subscriber
+{
+public:
+ udp_broadcaster(boost::asio::io_service& io_service,
+ const udp::endpoint& broadcast_endpoint)
+ : socket_(io_service)
+ {
+ socket_.connect(broadcast_endpoint);
+ }
+
+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_service& io_service,
+ const tcp::endpoint& listen_endpoint,
+ const udp::endpoint& broadcast_endpoint)
+ : io_service_(io_service),
+ acceptor_(io_service, listen_endpoint)
+ {
+ subscriber_ptr bc(new udp_broadcaster(io_service_, broadcast_endpoint));
+ channel_.join(bc);
+
+ start_accept();
+ }
+
+ void start_accept()
+ {
+ tcp_session_ptr new_session(new tcp_session(io_service_, channel_));
+
+ acceptor_.async_accept(new_session->socket(),
+ boost::bind(&server::handle_accept, this, new_session, _1));
+ }
+
+ void handle_accept(tcp_session_ptr session,
+ const boost::system::error_code& ec)
+ {
+ if (!ec)
+ {
+ session->start();
+ }
+
+ start_accept();
+ }
+
+private:
+ boost::asio::io_service& io_service_;
+ 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_service io_service;
+
+ tcp::endpoint listen_endpoint(tcp::v4(), atoi(argv[1]));
+
+ udp::endpoint broadcast_endpoint(
+ boost::asio::ip::address::from_string(argv[2]), atoi(argv[3]));
+
+ server s(io_service, listen_endpoint, broadcast_endpoint);
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/timers/tick_count_timer.cpp b/doc/html/boost_asio/example/cpp03/timers/tick_count_timer.cpp
new file mode 100644
index 0000000000..4f0c85d054
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/timers/tick_count_timer.cpp
@@ -0,0 +1,123 @@
+//
+// tick_count_timer.cpp
+// ~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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>
+
+#if defined(WIN32)
+# include <windows.h>
+#else
+# error This example is for Windows only!
+#endif
+
+struct tick_count_traits
+{
+ // The time type. This type has no constructor that takes a DWORD to ensure
+ // that the timer can only be used with relative times.
+ class time_type
+ {
+ public:
+ time_type() : ticks_(0) {}
+ private:
+ friend struct tick_count_traits;
+ DWORD ticks_;
+ };
+
+ // The duration type. According to the TimeTraits requirements, the duration
+ // must be a signed type. This means we can't handle durations larger than
+ // 2^31.
+ class duration_type
+ {
+ public:
+ duration_type() : ticks_(0) {}
+ duration_type(LONG ticks) : ticks_(ticks) {}
+ private:
+ friend struct tick_count_traits;
+ LONG ticks_;
+ };
+
+ // Get the current time.
+ static time_type now()
+ {
+ time_type result;
+ result.ticks_ = ::GetTickCount();
+ return result;
+ }
+
+ // Add a duration to a time.
+ static time_type add(const time_type& t, const duration_type& d)
+ {
+ time_type result;
+ result.ticks_ = t.ticks_ + d.ticks_;
+ return result;
+ }
+
+ // Subtract one time from another.
+ static duration_type subtract(const time_type& t1, const time_type& t2)
+ {
+ // DWORD tick count values can wrap (see less_than() below). We'll convert
+ // to a duration by taking the absolute difference and adding the sign
+ // based on which is the "lesser" absolute time.
+ return duration_type(less_than(t1, t2)
+ ? -static_cast<LONG>(t2.ticks_ - t1.ticks_)
+ : static_cast<LONG>(t1.ticks_ - t2.ticks_));
+ }
+
+ // Test whether one time is less than another.
+ static bool less_than(const time_type& t1, const time_type& t2)
+ {
+ // DWORD tick count values wrap periodically, so we'll use a heuristic that
+ // says that if subtracting t1 from t2 yields a value smaller than 2^31,
+ // then t1 is probably less than t2. This means that we can't handle
+ // durations larger than 2^31, which shouldn't be a problem in practice.
+ return (t2.ticks_ - t1.ticks_) < static_cast<DWORD>(1 << 31);
+ }
+
+ // Convert to POSIX duration type.
+ static boost::posix_time::time_duration to_posix_duration(
+ const duration_type& d)
+ {
+ return boost::posix_time::milliseconds(d.ticks_);
+ }
+};
+
+typedef boost::asio::basic_deadline_timer<
+ DWORD, tick_count_traits> tick_count_timer;
+
+void handle_timeout(const boost::system::error_code&)
+{
+ std::cout << "handle_timeout\n";
+}
+
+int main()
+{
+ try
+ {
+ boost::asio::io_service io_service;
+
+ tick_count_timer timer(io_service, 5000);
+ std::cout << "Starting synchronous wait\n";
+ timer.wait();
+ std::cout << "Finished synchronous wait\n";
+
+ timer.expires_from_now(5000);
+ std::cout << "Starting asynchronous wait\n";
+ timer.async_wait(&handle_timeout);
+ io_service.run();
+ std::cout << "Finished asynchronous wait\n";
+ }
+ catch (std::exception& e)
+ {
+ std::cout << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/timers/time_t_timer.cpp b/doc/html/boost_asio/example/cpp03/timers/time_t_timer.cpp
new file mode 100644
index 0000000000..19e84e35a4
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/timers/time_t_timer.cpp
@@ -0,0 +1,93 @@
+//
+// time_t_timer.cpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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>
+
+struct time_t_traits
+{
+ // The time type.
+ typedef std::time_t time_type;
+
+ // The duration type.
+ struct duration_type
+ {
+ duration_type() : value(0) {}
+ duration_type(std::time_t v) : value(v) {}
+ std::time_t value;
+ };
+
+ // Get the current time.
+ static time_type now()
+ {
+ return std::time(0);
+ }
+
+ // Add a duration to a time.
+ static time_type add(const time_type& t, const duration_type& d)
+ {
+ return t + d.value;
+ }
+
+ // Subtract one time from another.
+ static duration_type subtract(const time_type& t1, const time_type& t2)
+ {
+ return duration_type(t1 - t2);
+ }
+
+ // Test whether one time is less than another.
+ static bool less_than(const time_type& t1, const time_type& t2)
+ {
+ return t1 < t2;
+ }
+
+ // Convert to POSIX duration type.
+ static boost::posix_time::time_duration to_posix_duration(
+ const duration_type& d)
+ {
+ return boost::posix_time::seconds(d.value);
+ }
+};
+
+typedef boost::asio::basic_deadline_timer<
+ std::time_t, time_t_traits> time_t_timer;
+
+void handle_timeout(const boost::system::error_code&)
+{
+ std::cout << "handle_timeout\n";
+}
+
+int main()
+{
+ try
+ {
+ boost::asio::io_service io_service;
+
+ time_t_timer timer(io_service);
+
+ timer.expires_from_now(5);
+ std::cout << "Starting synchronous wait\n";
+ timer.wait();
+ std::cout << "Finished synchronous wait\n";
+
+ timer.expires_from_now(5);
+ std::cout << "Starting asynchronous wait\n";
+ timer.async_wait(&handle_timeout);
+ io_service.run();
+ std::cout << "Finished asynchronous wait\n";
+ }
+ catch (std::exception& e)
+ {
+ std::cout << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/doc/html/boost_asio/example/cpp03/windows/transmit_file.cpp b/doc/html/boost_asio/example/cpp03/windows/transmit_file.cpp
new file mode 100644
index 0000000000..2be12c6992
--- /dev/null
+++ b/doc/html/boost_asio/example/cpp03/windows/transmit_file.cpp
@@ -0,0 +1,171 @@
+//
+// transmit_file.cpp
+// ~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the 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.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;
+
+// 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_io_service(), 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_service.
+ 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_service.
+ overlapped.release();
+ }
+}
+
+class connection
+ : public boost::enable_shared_from_this<connection>
+{
+public:
+ typedef boost::shared_ptr<connection> pointer;
+
+ static pointer create(boost::asio::io_service& io_service,
+ const std::string& filename)
+ {
+ return pointer(new connection(io_service, 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_service& io_service, const std::string& filename)
+ : socket_(io_service),
+ filename_(filename),
+ file_(io_service)
+ {
+ }
+
+ 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_service& io_service,
+ unsigned short port, const std::string& filename)
+ : acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
+ filename_(filename)
+ {
+ start_accept();
+ }
+
+private:
+ void start_accept()
+ {
+ connection::pointer new_connection =
+ connection::create(acceptor_.get_io_service(), 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_service io_service;
+
+ using namespace std; // For atoi.
+ server s(io_service, atoi(argv[1]), argv[2]);
+
+ io_service.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)